fix(pl): 调整证件建档弹窗布局并按所选类型解析单个附件。
This commit is contained in:
1 parent
c16681a16f
commit
4dbd48d827
4 files changed
+478
-32
No files matched your search
@@ -25,7 +25,7 @@
|
||||
class="att-upload"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:multiple="true"
|
||||
:multiple="limit > 1"
|
||||
:accept="accept"
|
||||
@change="handleAttachmentChange"
|
||||
>
|
||||
@@ -85,7 +85,7 @@
|
||||
ref="uploadRef"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:multiple="true"
|
||||
:multiple="limit > 1"
|
||||
:accept="accept"
|
||||
@change="handleAttachmentChange"
|
||||
>
|
||||
|
||||
@@ -104,10 +104,10 @@
|
||||
</section>
|
||||
<el-dialog
|
||||
v-model="formVisible"
|
||||
:title="`${editingId ? '编辑' : '新增'}${kind === 'purchase' ? '购买证' : '运输证'}`"
|
||||
width="960px"
|
||||
:title="editingId ? `编辑${formKind === 'purchase' ? '购买证' : '运输证'}` : '新增证件'"
|
||||
width="1200px"
|
||||
align-center
|
||||
class="dialog-form-layout"
|
||||
class="dialog-form-layout quota-cert-form-dialog"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="!saving"
|
||||
:show-close="!saving"
|
||||
@@ -117,47 +117,77 @@
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:validate-on-rule-change="false"
|
||||
label-position="top"
|
||||
scroll-to-error
|
||||
>
|
||||
<section class="view-section">
|
||||
<el-form-item label="建档方式"
|
||||
><el-radio-group v-model="form.entryMode" :disabled="!!editingId"
|
||||
<section class="view-section form-grid-2">
|
||||
<el-form-item>
|
||||
<template #label><span class="dialog-section-title">建档方式</span></template>
|
||||
<el-radio-group v-model="form.entryMode" :disabled="!!editingId || recognizing"
|
||||
><el-radio value="manual">手工录入(可后补图片)</el-radio
|
||||
><el-radio value="image_parse">图片解析后核对</el-radio></el-radio-group
|
||||
></el-form-item
|
||||
>
|
||||
<el-form-item label="证件附件"
|
||||
><AttachmentUploadField
|
||||
<el-form-item>
|
||||
<template #label><span class="dialog-section-title">证件类型</span></template>
|
||||
<el-radio-group
|
||||
v-model="selectedFormKind"
|
||||
:disabled="!!editingId"
|
||||
@change="changeFormKind"
|
||||
>
|
||||
<el-radio value="purchase">购买证</el-radio>
|
||||
<el-radio value="transport">运输证</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</section>
|
||||
<section class="view-section">
|
||||
<el-form-item>
|
||||
<template #label><span class="dialog-section-title">证件附件</span></template>
|
||||
<AttachmentUploadField
|
||||
ref="certAttachments"
|
||||
:key="editSession"
|
||||
:disabled="saving"
|
||||
:disabled="saving || recognizing"
|
||||
v-model="form.attachmentUrls"
|
||||
folder="pl/quota-cert"
|
||||
:limit="editingId ? 15 : 1"
|
||||
accept="image/*,.pdf"
|
||||
:hint="
|
||||
editingId
|
||||
? '支持图片、PDF;每次仅解析一个附件'
|
||||
: '支持图片、PDF;每次仅上传并解析一个附件,更换请先删除原附件'
|
||||
"
|
||||
@update:model-value="edited.add('attachmentUrls')"
|
||||
/></el-form-item>
|
||||
<template v-if="form.entryMode === 'image_parse' || !!form.attachmentUrls">
|
||||
<el-select v-model="recognitionImage" placeholder="选择一张已上传的证件图片"
|
||||
><el-option
|
||||
v-for="url in (form.attachmentUrls || '').split(',').filter(Boolean)"
|
||||
:key="url"
|
||||
:value="url"
|
||||
:label="url.split('/').pop()"
|
||||
/></el-select>
|
||||
<el-button :loading="recognizing" :disabled="!recognitionImage" @click="recognize"
|
||||
><el-icon><Document /></el-icon>解析为候选字段</el-button
|
||||
>
|
||||
<div class="recognition-actions">
|
||||
<el-select
|
||||
v-if="editingId && attachmentOptions.length > 1"
|
||||
v-model="recognitionImage"
|
||||
:disabled="recognizing"
|
||||
placeholder="选择一个附件进行解析"
|
||||
><el-option
|
||||
v-for="url in attachmentOptions"
|
||||
:key="url"
|
||||
:value="url"
|
||||
:label="url.split('/').pop()"
|
||||
/></el-select>
|
||||
<el-button :loading="recognizing" :disabled="!recognitionImage" @click="recognize"
|
||||
><el-icon><Document /></el-icon>解析{{
|
||||
formKind === 'purchase' ? '购买证' : '运输证'
|
||||
}}</el-button
|
||||
>
|
||||
</div>
|
||||
<p class="form-tip"
|
||||
>请核对证号、数量、单位、有效次数和引用关系。解析只补充未编辑的空字段。</p
|
||||
>
|
||||
</template>
|
||||
</section>
|
||||
<section class="view-section"
|
||||
><h3>证面信息</h3
|
||||
><div class="form-grid-2">
|
||||
><h3 class="dialog-section-title">证面信息</h3
|
||||
><div class="form-grid-3">
|
||||
<el-form-item
|
||||
v-for="field in fields"
|
||||
v-for="field in formFields"
|
||||
:key="field[0]"
|
||||
:label="field[1]"
|
||||
:prop="field[0]"
|
||||
@@ -179,7 +209,7 @@
|
||||
/>
|
||||
</el-form-item> </div
|
||||
></section>
|
||||
<section v-if="kind === 'transport'" class="view-section">
|
||||
<section v-if="formKind === 'transport'" class="view-section">
|
||||
<el-form-item label="车牌号(多车共用额度)"
|
||||
><el-select
|
||||
v-model="form.vehicleNumbersJson"
|
||||
@@ -189,7 +219,7 @@
|
||||
default-first-option
|
||||
@change="edited.add('vehicleNumbersJson')"
|
||||
/></el-form-item>
|
||||
<h3>证面引用</h3>
|
||||
<h3 class="dialog-section-title">证面引用</h3>
|
||||
<el-table :data="form.purchaseRefs" row-key="id" stripe>
|
||||
<el-table-column label="引用类型" min-width="160"
|
||||
><template #default="{ row }"
|
||||
@@ -397,6 +427,8 @@
|
||||
} from './model'
|
||||
const route = useRoute()
|
||||
const kind = ref<CertType>('purchase')
|
||||
const formKind = ref<CertType>('purchase')
|
||||
const selectedFormKind = ref<CertType>('purchase')
|
||||
const keyword = ref(''),
|
||||
page = ref(1)
|
||||
const rows = ref<QuotaCert[]>([]),
|
||||
@@ -430,6 +462,24 @@
|
||||
...commonFields,
|
||||
...(kind.value === 'purchase' ? purchaseFields : transportFields)
|
||||
])
|
||||
const formFields = computed(() => [
|
||||
...commonFields,
|
||||
...(formKind.value === 'purchase' ? purchaseFields : transportFields)
|
||||
])
|
||||
const attachmentOptions = computed<string[]>(() =>
|
||||
(form.value.attachmentUrls || '')
|
||||
.split(',')
|
||||
.map((url: string) => url.trim())
|
||||
.filter(Boolean)
|
||||
)
|
||||
watch(
|
||||
attachmentOptions,
|
||||
(urls) => {
|
||||
if (urls.length === 1) recognitionImage.value = urls[0]
|
||||
else if (!urls.includes(recognitionImage.value)) recognitionImage.value = ''
|
||||
},
|
||||
{ flush: 'sync' }
|
||||
)
|
||||
|
||||
const statusLabels = { draft: '待核对', active: '已启用', suspended: '已停用', void: '已作废' }
|
||||
const refLabels = {
|
||||
@@ -455,7 +505,7 @@
|
||||
'totalQty',
|
||||
'quantityUnit',
|
||||
'validUseText',
|
||||
...(kind.value === 'purchase' ? ['buyerName', 'sellerName'] : ['shipperName'])
|
||||
...(formKind.value === 'purchase' ? ['buyerName', 'sellerName'] : ['shipperName'])
|
||||
].map((k) => [k, [{ required: true, message: '请填写此项', trigger: 'blur' }]])
|
||||
)
|
||||
)
|
||||
@@ -507,6 +557,8 @@
|
||||
const options = targetKind === 'transport' ? await quotaCertApi.list('purchase') : []
|
||||
if (session !== editSession || kind.value !== targetKind) return
|
||||
editingId.value = row?.id || ''
|
||||
formKind.value = targetKind
|
||||
selectedFormKind.value = targetKind
|
||||
edited.clear()
|
||||
recognitionImage.value = ''
|
||||
form.value = value
|
||||
@@ -514,6 +566,54 @@
|
||||
recognizing.value = false
|
||||
formVisible.value = true
|
||||
}
|
||||
async function changeFormKind(value: string | number | boolean | undefined) {
|
||||
try {
|
||||
if (
|
||||
(value !== 'purchase' && value !== 'transport') ||
|
||||
value === formKind.value ||
|
||||
editingId.value ||
|
||||
saving.value
|
||||
)
|
||||
return
|
||||
if (certAttachments.value?.hasUploading()) {
|
||||
ElMessage.warning('请等待附件上传完成')
|
||||
return
|
||||
}
|
||||
const session = editSession
|
||||
const hasContent =
|
||||
formFields.value.some(([key]) => form.value[key] != null && form.value[key] !== '') ||
|
||||
form.value.purchaseRefs?.length ||
|
||||
form.value.vehicleNumbersJson?.length
|
||||
if (hasContent) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'切换证件类型将清空证面信息,保留建档方式、附件和系统备注。是否继续?',
|
||||
'切换证件类型',
|
||||
{ type: 'warning' }
|
||||
)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
}
|
||||
if (session !== editSession || !formVisible.value || saving.value) return
|
||||
const nextSession = ++editSession
|
||||
recognizing.value = false
|
||||
const { entryMode, attachmentUrls, remark, orderNum } = form.value
|
||||
formKind.value = value
|
||||
selectedFormKind.value = value
|
||||
form.value = { ...newCert(value), entryMode, attachmentUrls, remark, orderNum }
|
||||
purchases.value = []
|
||||
edited.clear()
|
||||
formRef.value?.clearValidate()
|
||||
if (value === 'transport') {
|
||||
const options = await quotaCertApi.list('purchase')
|
||||
if (nextSession === editSession && formVisible.value && formKind.value === value)
|
||||
purchases.value = options
|
||||
}
|
||||
} finally {
|
||||
selectedFormKind.value = formKind.value
|
||||
}
|
||||
}
|
||||
async function save() {
|
||||
if (saving.value) return
|
||||
if (certAttachments.value?.hasUploading()) {
|
||||
@@ -521,6 +621,7 @@
|
||||
return
|
||||
}
|
||||
const session = editSession
|
||||
const targetKind = formKind.value
|
||||
saving.value = true
|
||||
if (!(await formRef.value.validate().catch(() => false)) || session !== editSession) {
|
||||
saving.value = false
|
||||
@@ -528,21 +629,34 @@
|
||||
}
|
||||
try {
|
||||
await quotaCertApi.save(
|
||||
kind.value,
|
||||
certPayload(kind.value, form.value),
|
||||
targetKind,
|
||||
certPayload(targetKind, form.value),
|
||||
editingId.value || undefined
|
||||
)
|
||||
formVisible.value = false
|
||||
ElMessage.success('证件已保存')
|
||||
await load()
|
||||
if (kind.value !== targetKind) kind.value = targetKind
|
||||
else await load()
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
async function recognize() {
|
||||
if (recognizing.value || saving.value) return
|
||||
if (certAttachments.value?.hasUploading()) {
|
||||
ElMessage.warning('请等待附件上传完成')
|
||||
return
|
||||
}
|
||||
if (
|
||||
!recognitionImage.value ||
|
||||
!attachmentOptions.value.includes(recognitionImage.value) ||
|
||||
(!editingId.value && attachmentOptions.value.length !== 1)
|
||||
) {
|
||||
ElMessage.warning('请选择一个已上传的证件附件')
|
||||
return
|
||||
}
|
||||
const session = editSession,
|
||||
targetKind = kind.value,
|
||||
targetKind = formKind.value,
|
||||
targetImage = recognitionImage.value
|
||||
recognizing.value = true
|
||||
try {
|
||||
@@ -550,12 +664,15 @@
|
||||
if (
|
||||
session !== editSession ||
|
||||
!formVisible.value ||
|
||||
kind.value !== targetKind ||
|
||||
formKind.value !== targetKind ||
|
||||
recognitionImage.value !== targetImage ||
|
||||
!(form.value.attachmentUrls || '').split(',').includes(targetImage)
|
||||
)
|
||||
return
|
||||
mergeCandidates(form.value as any, result.candidates, edited)
|
||||
await nextTick()
|
||||
if (session !== editSession || !formVisible.value) return
|
||||
formRef.value?.clearValidate()
|
||||
ElMessage.success(
|
||||
Object.keys(result.invalidFields).length
|
||||
? '部分字段未识别,请手工核对补全'
|
||||
@@ -727,3 +844,51 @@
|
||||
tableRef
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.quota-cert-form-dialog {
|
||||
max-width: calc(100vw - 40px);
|
||||
--dialog-viewport-inset: 50px;
|
||||
--dialog-max-height: calc(100vh - 100px);
|
||||
|
||||
.view-section {
|
||||
padding: 0 0 20px;
|
||||
}
|
||||
|
||||
.dialog-section-title {
|
||||
margin: 0 0 14px;
|
||||
padding-left: 8px;
|
||||
border-left: 3px solid var(--section-bar-border-color);
|
||||
color: var(--section-bar-title-color);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
span.dialog-section-title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-grid-2,
|
||||
.form-grid-3 {
|
||||
.el-form-item {
|
||||
min-width: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.el-date-editor.el-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.recognition-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
|
||||
.el-select {
|
||||
width: 320px;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user