feat: 支持多张支付凭证上传,更新了后端API、数据模型和前端上传组件。
This commit is contained in:
parent
5093cf8146
commit
20d8f155c6
@ -116,6 +116,14 @@ async def create_with_receipt(payload: AppCreateInvoiceWithReceipt, current_user
|
||||
wechat=getattr(current_user, "alias", None),
|
||||
)
|
||||
inv = await invoice_controller.create(inv_data)
|
||||
if payload.receipt_urls:
|
||||
receipts = []
|
||||
for url in payload.receipt_urls:
|
||||
receipt = await invoice_controller.create_receipt(inv.id, PaymentReceiptCreate(url=url, note=payload.note))
|
||||
detail = await invoice_controller.get_receipt_by_id(receipt.id)
|
||||
if detail:
|
||||
receipts.append(detail)
|
||||
return Success(data={"invoice_id": inv.id, "receipts": receipts}, msg="创建并上传成功")
|
||||
if payload.receipt_url:
|
||||
receipt = await invoice_controller.create_receipt(inv.id, PaymentReceiptCreate(url=payload.receipt_url, note=payload.note))
|
||||
detail = await invoice_controller.get_receipt_by_id(receipt.id)
|
||||
|
||||
@ -134,6 +134,7 @@ class AppCreateInvoiceWithReceipt(BaseModel):
|
||||
# 兼容前端索引字段:"0"→normal,"1"→special
|
||||
invoiceTypeIndex: Optional[str] = None
|
||||
receipt_url: Optional[str] = Field(None, max_length=512)
|
||||
receipt_urls: Optional[List[str]] = None
|
||||
note: Optional[str] = Field(None, max_length=256)
|
||||
|
||||
@field_validator('ticket_type', mode='before')
|
||||
@ -153,6 +154,21 @@ class AppCreateInvoiceWithReceipt(BaseModel):
|
||||
return s or None
|
||||
return v
|
||||
|
||||
@field_validator('receipt_urls', mode='before')
|
||||
@classmethod
|
||||
def _clean_receipt_urls(cls, v):
|
||||
if v is None:
|
||||
return v
|
||||
if isinstance(v, str):
|
||||
v = [v]
|
||||
if isinstance(v, list):
|
||||
cleaned = []
|
||||
for item in v:
|
||||
if isinstance(item, str) and item.strip():
|
||||
cleaned.append(item.strip())
|
||||
return cleaned or None
|
||||
return None
|
||||
|
||||
@model_validator(mode='after')
|
||||
def _coerce_invoice_type(self):
|
||||
if not self.invoice_type and self.invoiceTypeIndex is not None:
|
||||
|
||||
@ -144,13 +144,15 @@
|
||||
>
|
||||
<n-form-item label="上传支付凭证" required>
|
||||
<n-upload
|
||||
v-model:file-list="fileList"
|
||||
:action="actionUrl"
|
||||
list-type="image-card"
|
||||
:max="1"
|
||||
:max="10"
|
||||
multiple
|
||||
accept="image/png,image/jpeg,image/jpg"
|
||||
@before-upload="beforeUpload"
|
||||
@finish="handleUploadFinish"
|
||||
@remove="deleteUpload"
|
||||
@remove="handleRemove"
|
||||
>
|
||||
<div>
|
||||
<img style="width: 24px; height: 24px" src="@/assets/images/upload.png" alt="" />
|
||||
@ -158,7 +160,7 @@
|
||||
</div>
|
||||
</n-upload>
|
||||
<template #feedback>
|
||||
<div class="form-tip">支持jpg、png、jpeg格式,大小不超过5M</div>
|
||||
<div class="form-tip">支持JPG、PNG格式,大小不超过20M,最多上传10张</div>
|
||||
</template>
|
||||
</n-form-item>
|
||||
|
||||
@ -256,9 +258,8 @@ const actionUrl = 'https://value.cdcee.net/api/v1/upload/file'
|
||||
const currentStep = ref(1)
|
||||
|
||||
// 文件上传相关
|
||||
const fileInput = ref(null)
|
||||
const uploadedFile = ref(null)
|
||||
const uploadedFileUrl = ref('')
|
||||
const fileList = ref([])
|
||||
const uploadedFiles = ref([])
|
||||
|
||||
// 表单选择相关
|
||||
|
||||
@ -297,39 +298,43 @@ const message = useMessage()
|
||||
|
||||
// 计算表单是否有效
|
||||
const isFormValid = computed(() => {
|
||||
console.log('isFormValid check:', {
|
||||
uploadedFile: uploadedFile.value,
|
||||
invoiceHeader: formModel.invoiceHeader,
|
||||
invoiceType: formModel.invoiceType
|
||||
})
|
||||
return (
|
||||
!!uploadedFile.value &&
|
||||
uploadedFiles.value.length > 0 &&
|
||||
!!formModel.invoiceHeader &&
|
||||
!!formModel.invoiceType
|
||||
)
|
||||
})
|
||||
|
||||
const handleUploadFinish = (file) => {
|
||||
console.log('handleUploadFinish called:', file)
|
||||
console.log('response:', file.event?.target?.response)
|
||||
const handleUploadFinish = ({ file, event }) => {
|
||||
try {
|
||||
let response = JSON.parse(file.event.target.response)
|
||||
console.log('parsed response:', response)
|
||||
uploadedFile.value = response.data.url
|
||||
console.log('uploadedFile.value set to:', uploadedFile.value)
|
||||
const response = JSON.parse(event?.target?.response || '{}')
|
||||
const fileUrl = response.data?.url
|
||||
if (fileUrl) {
|
||||
file.url = fileUrl
|
||||
file.name = response.data.filename || file.name
|
||||
file.status = 'finished'
|
||||
if (!uploadedFiles.value.includes(fileUrl)) {
|
||||
uploadedFiles.value.push(fileUrl)
|
||||
}
|
||||
message.success('上传成功')
|
||||
return file
|
||||
}
|
||||
message.error(response.message || '上传失败')
|
||||
} catch (error) {
|
||||
console.error('Error parsing upload response:', error)
|
||||
message.error('上传失败,请重试')
|
||||
}
|
||||
}
|
||||
const deleteUpload = () => {
|
||||
uploadedFile.value = ''
|
||||
|
||||
const handleRemove = ({ file }) => {
|
||||
uploadedFiles.value = uploadedFiles.value.filter((url) => url !== file?.url)
|
||||
}
|
||||
|
||||
const beforeUpload = (data) => {
|
||||
const file = data.file.file
|
||||
const isLt10M = file.size / 1024 / 1024 < 10
|
||||
if (!isLt10M) {
|
||||
message.error('图片大小不能超过10MB,请重新上传')
|
||||
const isLt20M = file.size / 1024 / 1024 <= 20
|
||||
if (!isLt20M) {
|
||||
message.error('图片大小不能超过20MB,请重新上传')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@ -346,7 +351,7 @@ function handleUploadSubmit() {
|
||||
header_id: formModel.invoiceHeader,
|
||||
ticket_type: 'electronic',
|
||||
invoice_type: formModel.invoiceType,
|
||||
receipt_url: uploadedFile.value,
|
||||
receipt_urls: uploadedFiles.value,
|
||||
})
|
||||
currentStep.value = 3
|
||||
}
|
||||
@ -501,7 +506,8 @@ defineExpose({
|
||||
.form-tip {
|
||||
font-size: 12px;
|
||||
color: #c0c4cc;
|
||||
margin-top: 8px;
|
||||
line-height: 18px;
|
||||
margin: 12px 0 8px;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user