smart-bid-web/src/views/enterpriseLibrary/finance/components/child/FinanceReportUpload.vue

435 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<div class="basic-info-title">
<img src="@/assets/enterpriseLibrary/basic-info.png" alt="财务报告">
<span>财务报告</span>
</div>
<el-form
:model="formData"
:rules="rules"
ref="financeReportForm"
label-width="110px"
label-position="top"
>
<el-row :gutter="24">
<el-col :span="8">
<el-form-item label="财务报告附件" prop="fileList">
<UploadFile
:fileList="formData.fileList"
:fileUploadRule="financeReportUploadRule"
@file-change="(files, type) => handleFileChange(files, type)"
@del-file="handleDelFile"
:uploadType="uploadType"
:maxFileTips="maxFileTips"
@docx-content="handleDocxContent"
:limitUploadNum="1"
type="finance_report"
:show-file-list="true"
@upload-fail="handleUploadFail"
@validate-fail="handleUploadFail"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24" class="info-row">
<el-col :span="8" class="info-col">
<el-form-item label="文件名" prop="fileName">
<el-input
v-model="formData.fileName"
placeholder="文件上传后自动提取"
class="form-control"
show-word-limit
maxlength="64"
></el-input>
</el-form-item>
</el-col>
<el-col :span="8" class="info-col">
<el-form-item label="报告年份" prop="reportYear">
<el-date-picker
v-model="formData.reportYear"
type="year"
placeholder="选择年份"
class="form-control"
value-format="yyyy"
:disabled-date="disabledFutureYear"
></el-date-picker>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</template>
<script>
import UploadFile from '@/views/common/UploadFile.vue'
export default {
name: 'FinanceReportUpload',
components: { UploadFile },
dicts: ['identification_tag'],
data() {
return {
uploadType: 'docx',
maxFileTips: '1MB',
ocrRuleList: ['finance_report'],
fileUploadList: [],
ocrResultParams: {
"报告年份": "reportYear"
},
formData: {
fileList: [],
fileName: '',
reportYear: '',
delFileList: []
},
rules: {
fileList: [
{ required: true, message: '请上传财务报告附件', trigger: 'change' },
],
fileName: [
{ required: true, message: '文件名需等待文件上传后自动提取', trigger: 'blur' }
],
reportYear: [
{ required: true, message: '请选择报告年份', trigger: 'change' }
]
},
isReplacingFile: false // 新增:标记是否正在替换文件
}
},
computed: {
financeReportUploadRule() {
return this.fileUploadList[0]
}
},
watch: {
'dict.type.identification_tag': {
handler(newVal) {
if (newVal && newVal.length > 0) {
this.addOcrRule()
}
},
immediate: true
}
},
methods: {
handleUploadFail() {
this.formData.fileName = '';
this.formData.reportYear = '';
},
handleDocxContent(text) {
// 模拟OCR返回格式将docx文本包装成现有逻辑可处理的结构
const mockOcrResult = {
status_code: 200,
data: {
chat_res: this.extractInfoFromDocx(text) // 从文本中提取关键信息
}
};
// 复用OCR结果处理逻辑填充报告年份
this.handleOcrResultWithMock(mockOcrResult);
},
// 从docx文本中提取信息核心根据业务规则匹配关键词
extractInfoFromDocx(text) {
console.log("text", text);
// 优化正则匹配“年份2026”“年份:2026”“2026年”“2026年度”等格式
const yearMatch = text.match(
/(年份|报告年份|年度)\s*[:]\s*(\d{4})|(\d{4})\s*(年|年度)/
);
console.log("yearMatch", yearMatch);
// 提取匹配到的年份(优先取冒号后的年份,再取数字+年/年度的格式)
const year = yearMatch ? (yearMatch[2] || yearMatch[3]) : '';
return { "报告年份": year };
},
// 复用原OCR结果处理逻辑
handleOcrResultWithMock(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 === 'reportYear' && chat_res[key]) {
const year = chat_res[key].replace(/[^\d]/g, '');
this.formData[formField] = year.length === 4 ? year : '';
}
});
this.$message.success('docx内容解析成功已自动填充报告年份');
this.$refs.financeReportForm.validateField('reportYear');
}
} else {
this.$message.error(`年份提取失败: ${ocrResult.status_msg || '未找到有效年份'}`);
}
},
setFormData(data) {
// 财务报告文件
const financeFiles = Array.isArray(data.fileList)
? data.fileList.filter(file => file && file.businessType === 'finance_report')
.map(file => ({
name: file.name || file.fileName || '未知文件',
filePath: file.filePath,
lsFilePath: file.lsFilePath,
fileType: file.fileType || '2',
businessType: 'finance_report'
}))
: [];
let reportYear = data.reportYear || '';
if (reportYear) {
if (typeof reportYear === 'string') {
reportYear = reportYear.split('-')[0];
}
if (typeof reportYear === 'number') {
reportYear = reportYear.toString();
}
}
this.formData = {
...(data || {}),
fileList: financeFiles,
fileName: financeFiles.length > 0 ? this.getFileName(financeFiles[0]) : '',
delFileList: [],
reportYear: reportYear,
}
},
getFileName(file) {
const fullName = file.name || file.fileName || '未知文件'
const lastDotIndex = fullName.lastIndexOf('.')
return lastDotIndex > 0 ? fullName.substring(0, lastDotIndex) : fullName
},
getFileExt(file) {
if (!file) return ''
const fileName = file.name || file.fileName || ''
const extMatch = fileName.match(/\.([a-zA-Z0-9]+)$/)
return extMatch ? extMatch[1].toLowerCase() : ''
},
handleFileChange(files, type) {
if (type === 'finance_report' && files instanceof Array) {
if (files.length > 0 && files[0].response) {
this.isReplacingFile = false;
// 处理旧文件删除(保持不变)
if (this.formData.fileList.length > 0) {
const oldFile = this.formData.fileList[0];
const delPath = oldFile?.response?.fileRes?.filePath || oldFile?.filePath || null;
if (delPath && !this.formData.delFileList.includes(delPath)) {
this.formData.delFileList.push(delPath);
}
}
const markedFiles = files.map(file => ({
...file,
businessType: 'finance_report'
})).slice(-1);
this.formData.fileList = markedFiles;
// 提取文件名(保持不变)
if (markedFiles.length > 0) {
const originalFileName = this.getFileName(markedFiles[0])
const safeFileName = originalFileName.replace(/--+/g, '-')
this.formData.fileName = safeFileName
this.$refs.financeReportForm.validateField('fileName')
}
//根据文件类型选择解析方式
const firstFile = markedFiles[0];
const fileExt = this.getFileExt(firstFile).toLowerCase(); // 获取文件后缀
if (['doc', 'docx'].includes(fileExt)) {
// doc/docx文件只执行你的自定义解析不调用OCR
} else if (['pdf', 'jpg', 'jpeg', 'png'].includes(fileExt)) {
// pdf/图片执行原OCR逻辑
this.handleOcrResult(markedFiles);
} else {
// 其他格式:提示不支持
this.$message.warning('不支持的文件格式,无法解析年份');
}
} else if (!this.isReplacingFile) {
this.formData.fileList = [];
this.formData.fileName = '';
}
}
},
handleDelFile(file) {
// 标记为替换操作,保留文件列表结构等待新文件
this.isReplacingFile = true;
const delPath = file?.response?.fileRes?.filePath || file?.filePath || null;
if (delPath && !this.formData.delFileList.includes(delPath)) {
this.formData.delFileList.push(delPath);
}
},
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
if (chat_res && typeof chat_res === 'object') {
Object.keys(chat_res).forEach(key => {
const formField = this.ocrResultParams[key]
if (formField === 'reportYear' && chat_res[key]) {
const year = chat_res[key].replace(/[^\d]/g, '')
this.formData[formField] = year.length === 4 ? year : ''
}
})
this.$message.success('OCR识别成功已自动填充报告年份')
this.$refs.financeReportForm.validateField('reportYear')
}
} else {
this.$message.error(`年份识别失败: ${ocrResult.status_msg || '未知错误'}`)
}
}
} catch (error) {
this.$message.error(`处理年份结果失败: ${error.message}`)
} finally {
this.$bus.$emit('endUpload')
}
},
disabledFutureYear(date) {
return date > new Date(new Date().getFullYear(), 11, 31)
},
validate() {
return new Promise((resolve, reject) => {
if (!this.$refs.financeReportForm) {
reject(new Error('表单实例未加载完成'))
return
}
this.$refs.financeReportForm.validate((valid) => {
if (valid) {
resolve(this.formData)
} else {
reject(new Error('财务报告信息填写不完整'))
}
})
})
},
resetForm() {
if (this.$refs.financeReportForm) {
this.$refs.financeReportForm.resetFields()
}
this.formData = {
fileList: [],
fileName: '',
reportYear: '',
delFileList: []
}
this.isReplacingFile = false;
},
addOcrRule() {
this.ocrRuleList.forEach(item => {
this.ocrRule(item)
})
},
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)
}
}
}
</script>
<style scoped lang="scss">
.basic-info-title {
display: flex;
align-items: center;
margin: 10px 0;
span {
margin: 0 5px;
font-size: 20px;
}
img {
width: 24px;
height: 24px;
object-fit: contain;
}
}
.el-form {
margin-top: 15px;
padding: 0 15px;
}
.form-control {
width: 100%;
}
.el-form-item {
margin-bottom: 20px;
}
.upload-tip {
color: #909399;
font-size: 12px;
margin-top: 5px;
line-height: 1.5;
}
::v-deep .el-form-item__label {
color: #4e5969;
font-weight: 500;
}
::v-deep .el-input.is-readonly .el-input__inner {
background-color: #f5f7fa;
cursor: default;
}
::v-deep .upload-container {
::v-deep .el-upload-dragger {
height: 180px;
transition: border-color 0.3s;
}
::v-deep .el-upload-list__item {
margin-top: 10px;
}
::v-deep .el-upload-list__item-name {
display: inline-block;
max-width: 80%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
</style>