427 lines
12 KiB
Vue
427 lines
12 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="qualification-form">
|
|||
|
|
<el-form
|
|||
|
|
:model="formData"
|
|||
|
|
:rules="rules"
|
|||
|
|
ref="qualificationForm"
|
|||
|
|
label-width="110px"
|
|||
|
|
label-position="top"
|
|||
|
|
class="form-wrapper"
|
|||
|
|
>
|
|||
|
|
<!-- 资质证书上传 -->
|
|||
|
|
<el-form-item label="资质证书附件" prop="files">
|
|||
|
|
<UploadFile
|
|||
|
|
ref="uploadFileComp"
|
|||
|
|
:fileList="formData.files"
|
|||
|
|
:fileUploadRule="qualificationUploadRule"
|
|||
|
|
@file-change="(files, type) => handleFileChange(files, type)"
|
|||
|
|
@del-file="handleDelFile"
|
|||
|
|
:uploadType="uploadType"
|
|||
|
|
:maxFileTips="maxFileTips"
|
|||
|
|
type="qualification_certificate"
|
|||
|
|
/>
|
|||
|
|
</el-form-item>
|
|||
|
|
|
|||
|
|
<!-- 证书名称 -->
|
|||
|
|
<el-form-item label="证书名称" prop="certificateName">
|
|||
|
|
<el-input
|
|||
|
|
v-model="formData.certificateName"
|
|||
|
|
placeholder="文件上传后自动提取"
|
|||
|
|
class="form-control"
|
|||
|
|
maxlength="50"
|
|||
|
|
></el-input>
|
|||
|
|
</el-form-item>
|
|||
|
|
|
|||
|
|
<!-- 证书编号 -->
|
|||
|
|
<el-form-item label="证书编号" prop="certificateCode">
|
|||
|
|
<el-input
|
|||
|
|
v-model="formData.certificateCode"
|
|||
|
|
placeholder="文件上传后自动提取"
|
|||
|
|
class="form-control"
|
|||
|
|
maxlength="32"
|
|||
|
|
></el-input>
|
|||
|
|
</el-form-item>
|
|||
|
|
|
|||
|
|
<!-- 发证日期 -->
|
|||
|
|
<el-form-item label="发证日期" prop="certificateDate">
|
|||
|
|
<el-date-picker
|
|||
|
|
v-model="formData.certificateDate"
|
|||
|
|
type="date"
|
|||
|
|
placeholder="文件上传后自动提取"
|
|||
|
|
value-format="yyyy-MM-dd"
|
|||
|
|
class="form-control"
|
|||
|
|
/>
|
|||
|
|
</el-form-item>
|
|||
|
|
|
|||
|
|
<!-- 证书有效截止日期 -->
|
|||
|
|
<el-form-item label="证书有效截止日期" prop="certificateEndDate">
|
|||
|
|
<el-date-picker
|
|||
|
|
v-model="formData.certificateEndDate"
|
|||
|
|
type="date"
|
|||
|
|
placeholder="文件上传后自动提取"
|
|||
|
|
value-format="yyyy-MM-dd"
|
|||
|
|
class="form-control"
|
|||
|
|
/>
|
|||
|
|
</el-form-item>
|
|||
|
|
|
|||
|
|
<!-- 发证机构 -->
|
|||
|
|
<el-form-item label="发证机构" prop="certificateInstitution">
|
|||
|
|
<el-input
|
|||
|
|
v-model="formData.certificateInstitution"
|
|||
|
|
placeholder="文件上传后自动提取"
|
|||
|
|
class="form-control"
|
|||
|
|
maxlength="64"
|
|||
|
|
></el-input>
|
|||
|
|
</el-form-item>
|
|||
|
|
|
|||
|
|
<!-- 资质类型 -->
|
|||
|
|
<el-form-item label="资质类型" prop="qualificationType">
|
|||
|
|
<el-select
|
|||
|
|
v-model="formData.qualificationType"
|
|||
|
|
placeholder="请选择资质类型"
|
|||
|
|
clearable
|
|||
|
|
class="form-control"
|
|||
|
|
>
|
|||
|
|
<el-option
|
|||
|
|
v-for="item in qualificationTypeOptions"
|
|||
|
|
:key="item.value"
|
|||
|
|
:label="item.label"
|
|||
|
|
:value="item.value"
|
|||
|
|
/>
|
|||
|
|
</el-select>
|
|||
|
|
</el-form-item>
|
|||
|
|
</el-form>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
|
|||
|
|
import UploadFile from '@/views/common/UploadFile.vue'
|
|||
|
|
|
|||
|
|
export default {
|
|||
|
|
name: 'QualificationFormChild',
|
|||
|
|
components: { UploadFile },
|
|||
|
|
dicts: ['identification_tag'],
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
uploadType: 'docx',
|
|||
|
|
maxFileTips: '100MB',
|
|||
|
|
ocrRuleList: ['qualification_certificate'], // 资质证书OCR识别规则
|
|||
|
|
fileUploadList: [],
|
|||
|
|
// OCR识别结果映射关系
|
|||
|
|
ocrResultParams: {
|
|||
|
|
"证书名称": "certificateName",
|
|||
|
|
"证书编号": "certificateCode",
|
|||
|
|
"发证日期": "certificateDate",
|
|||
|
|
"证书有效截止日期": "certificateEndDate",
|
|||
|
|
"发证机构": "certificateInstitution"
|
|||
|
|
},
|
|||
|
|
formData: {
|
|||
|
|
files: [],
|
|||
|
|
certificateName: '',
|
|||
|
|
certificateCode: '',
|
|||
|
|
certificateDate: '',
|
|||
|
|
certificateEndDate: '',
|
|||
|
|
certificateInstitution: '',
|
|||
|
|
qualificationType: '',
|
|||
|
|
delFileList: []
|
|||
|
|
},
|
|||
|
|
qualificationTypeOptions: [],
|
|||
|
|
rules: {
|
|||
|
|
files: [
|
|||
|
|
{ required: true, message: '请上传资质证书附件', trigger: 'change' }
|
|||
|
|
],
|
|||
|
|
certificateName: [
|
|||
|
|
{ required: true, message: '请输入证书名称', trigger: 'blur' }
|
|||
|
|
],
|
|||
|
|
certificateCode: [
|
|||
|
|
{ required: true, message: '请输入证书编号', trigger: 'blur' }
|
|||
|
|
],
|
|||
|
|
certificateDate: [
|
|||
|
|
{ required: true, message: '请选择发证日期', trigger: 'change' }
|
|||
|
|
],
|
|||
|
|
certificateEndDate: [
|
|||
|
|
{ required: true, message: '请选择有效截止日期', trigger: 'change' }
|
|||
|
|
],
|
|||
|
|
qualificationType: [
|
|||
|
|
{ required: true, message: '请选择资质类型', trigger: 'change' }
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
computed: {
|
|||
|
|
qualificationUploadRule() {
|
|||
|
|
return this.fileUploadList[0]
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
watch: {
|
|||
|
|
'dict.type.identification_tag': {
|
|||
|
|
handler(newVal) {
|
|||
|
|
if (newVal && newVal.length > 0) {
|
|||
|
|
this.addOcrRule()
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
immediate: true
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
|
|||
|
|
methods: {
|
|||
|
|
loadQualificationTypeDict() {
|
|||
|
|
|
|||
|
|
// getDicts 的参数是字典类型编码:qualification_type
|
|||
|
|
this.getDicts('qualification_type').then(response => {
|
|||
|
|
this.qualificationTypeOptions = response.data.map(item => ({
|
|||
|
|
label: item.dictLabel,
|
|||
|
|
value: item.dictValue
|
|||
|
|
}))
|
|||
|
|
}).catch(error => {
|
|||
|
|
console.error('加载电压等级字典失败:', error)
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 接收父组件数据并回显
|
|||
|
|
setFormData(data) {
|
|||
|
|
// 1. 过滤“资质证书类型”的文件,排除其他业务类型文件
|
|||
|
|
const qualificationFiles = Array.isArray(data.fileList)
|
|||
|
|
? data.fileList.filter(file =>
|
|||
|
|
file && file.businessType === 'qualification_certificate' // 严格匹配业务类型
|
|||
|
|
).map(file => ({
|
|||
|
|
// 2. 补充文件预览和显示所需的完整元数据
|
|||
|
|
...file,
|
|||
|
|
// 确保 UploadFile 组件需要的字段存在(参考 UploadFile 组件的 fileList 格式)
|
|||
|
|
uid: file.uid || Date.now() + Math.random().toString(36).substr(2, 9), // 生成唯一ID
|
|||
|
|
name: file.fileName || file.name || '未命名文件', // 文件名(适配后端可能的字段名)
|
|||
|
|
lsFilePath: file.lsFilePath || file.fileUrl || '', // 预览路径(关键:用于文件预览)
|
|||
|
|
filePath: file.filePath || file.uploadPath || '', // 文件存储路径(用于删除)
|
|||
|
|
status: 'success', // 标记为已上传状态,避免组件显示“上传中”
|
|||
|
|
response: {
|
|||
|
|
fileRes: {
|
|||
|
|
fileName: file.fileName || file.name,
|
|||
|
|
filePath: file.filePath || file.uploadPath,
|
|||
|
|
uploadPath: file.filePath || file.uploadPath
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}))
|
|||
|
|
: [];
|
|||
|
|
|
|||
|
|
// 3. 赋值表单数据,确保文件列表格式正确
|
|||
|
|
this.formData = {
|
|||
|
|
...data,
|
|||
|
|
files: qualificationFiles, // 子表单绑定的文件列表字段
|
|||
|
|
delFileList: [] // 清空待删除列表
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 4. 触发 UploadFile 组件重新渲染(避免数据更新但视图未同步)
|
|||
|
|
this.$nextTick(() => {
|
|||
|
|
if (this.$refs.uploadFileComp && this.$refs.uploadFileComp.updatePreview) {
|
|||
|
|
this.$refs.uploadFileComp.updatePreview(qualificationFiles); // 若 UploadFile 有预览更新方法,主动调用
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 处理文件变更
|
|||
|
|
handleFileChange(files, type) {
|
|||
|
|
if (type === 'qualification_certificate') {
|
|||
|
|
const markedFiles = files.map(file => ({
|
|||
|
|
...file,
|
|||
|
|
businessType: 'qualification_certificate'
|
|||
|
|
}))
|
|||
|
|
this.formData.files = markedFiles
|
|||
|
|
|
|||
|
|
// 触发OCR识别
|
|||
|
|
this.handleOcrResult(markedFiles)
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 处理OCR识别结果
|
|||
|
|
handleOcrResult(files) {
|
|||
|
|
if (!files || !Array.isArray(files) || files.length === 0) return
|
|||
|
|
|
|||
|
|
this.$bus.$emit('startUpload', '正在识别证书信息')
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const firstFile = files[0]
|
|||
|
|
if (firstFile.response?.ocrResult) {
|
|||
|
|
const ocrResult = firstFile.response.ocrResult
|
|||
|
|
if (ocrResult.status_code === 200) {
|
|||
|
|
const chat_res = ocrResult.data?.chat_res
|
|||
|
|
if (chat_res && typeof chat_res === 'object') {
|
|||
|
|
// 映射识别结果到表单字段
|
|||
|
|
Object.keys(chat_res).forEach(key => {
|
|||
|
|
const formField = this.ocrResultParams[key]
|
|||
|
|
if (formField && chat_res[key]) {
|
|||
|
|
// 日期格式处理
|
|||
|
|
if (['certificateDate', 'certificateEndDate'].includes(formField)) {
|
|||
|
|
// 简单日期格式转换(可根据实际识别结果调整)
|
|||
|
|
const dateStr = chat_res[key].replace(/[^\d-]/g, '')
|
|||
|
|
this.formData[formField] = dateStr || ''
|
|||
|
|
} else {
|
|||
|
|
this.formData[formField] = chat_res[key] || ''
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
this.$message.success('OCR识别成功,已自动填充证书信息')
|
|||
|
|
// 触发表单验证
|
|||
|
|
Object.keys(this.ocrResultParams).forEach(key => {
|
|||
|
|
const formField = this.ocrResultParams[key]
|
|||
|
|
this.$refs.qualificationForm.validateField(formField)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
this.$message.error(`识别失败: ${ocrResult.status_msg || '未知错误'}`)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
this.$message.error(`处理识别结果失败: ${error.message}`)
|
|||
|
|
} finally {
|
|||
|
|
this.$bus.$emit('endUpload')
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 处理文件删除
|
|||
|
|
handleDelFile(file) {
|
|||
|
|
const filePath = file.response?.fileRes?.uploadPath || file.filePath
|
|||
|
|
if (filePath && !this.formData.delFileList.includes(filePath)) {
|
|||
|
|
this.formData.delFileList.push(filePath)
|
|||
|
|
}
|
|||
|
|
// 删除文件后清空关联字段
|
|||
|
|
if (this.formData.files.length === 0) {
|
|||
|
|
this.formData.certificateName = ''
|
|||
|
|
this.formData.certificateCode = ''
|
|||
|
|
this.formData.certificateDate = ''
|
|||
|
|
this.formData.certificateEndDate = ''
|
|||
|
|
this.formData.certificateInstitution = ''
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 表单验证
|
|||
|
|
validate() {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
if (!this.$refs.qualificationForm) {
|
|||
|
|
reject(new Error('表单实例未加载完成'))
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.$refs.qualificationForm.validate((valid) => {
|
|||
|
|
if (valid) {
|
|||
|
|
resolve(this.formData)
|
|||
|
|
} else {
|
|||
|
|
reject(new Error('资质证书信息填写不完整'))
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 重置表单
|
|||
|
|
resetForm() {
|
|||
|
|
const oldFileList = [...this.formData.files]; // 拷贝旧文件列表
|
|||
|
|
|
|||
|
|
this.$nextTick(async () => {
|
|||
|
|
this.formData.files = oldFileList;
|
|||
|
|
|
|||
|
|
await this.$nextTick();
|
|||
|
|
if (oldFileList.length > 0 && this.$refs.uploadFileComp) {
|
|||
|
|
const uploadInnerFiles = this.$refs.uploadFileComp.files;
|
|||
|
|
uploadInnerFiles.forEach(file => {
|
|||
|
|
this.$refs.uploadFileComp.handleRemove(file, []);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.formData.files = [];
|
|||
|
|
|
|||
|
|
if (this.$refs.qualificationForm) {
|
|||
|
|
this.$refs.qualificationForm.resetFields();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 步骤5:清空其他表单字段(保持原有逻辑)
|
|||
|
|
this.formData = {
|
|||
|
|
...this.formData,
|
|||
|
|
certificateName: '',
|
|||
|
|
certificateCode: '',
|
|||
|
|
certificateDate: '',
|
|||
|
|
certificateEndDate: '',
|
|||
|
|
certificateInstitution: '',
|
|||
|
|
qualificationType: '',
|
|||
|
|
delFileList: []
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 初始化OCR识别规则
|
|||
|
|
addOcrRule() {
|
|||
|
|
this.ocrRuleList.forEach(item => {
|
|||
|
|
this.ocrRule(item)
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 获取OCR识别规则
|
|||
|
|
ocrRule(type) {
|
|||
|
|
const foundItem = this.dict.type.identification_tag?.find(item => item.value === type)
|
|||
|
|
if (!foundItem) {
|
|||
|
|
this.$message.warning(`未找到${type}的识别规则配置`)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const item = {
|
|||
|
|
fileUploadType: foundItem.value,
|
|||
|
|
fields_json: foundItem.raw?.remark,
|
|||
|
|
suffix: 'mainDatabase'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.fileUploadList.push(item)
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
created() {
|
|||
|
|
this.loadQualificationTypeDict();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped lang="scss">
|
|||
|
|
.basic-info-title {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
margin: 0 0 20px 0;
|
|||
|
|
span {
|
|||
|
|
margin: 0 5px;
|
|||
|
|
font-size: 20px;
|
|||
|
|
color: #333;
|
|||
|
|
}
|
|||
|
|
img {
|
|||
|
|
width: 24px;
|
|||
|
|
height: 24px;
|
|||
|
|
object-fit: contain;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.qualification-form {
|
|||
|
|
padding: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.el-form {
|
|||
|
|
margin-top: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-control {
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.el-form-item {
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
::v-deep .el-form-item__label {
|
|||
|
|
color: #4e5969;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-wrapper {
|
|||
|
|
padding: 24px;
|
|||
|
|
background: #fff;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
</style>
|