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