推送项目重构代码

This commit is contained in:
2026-05-31 14:19:15 +08:00
parent a28ea44cab
commit dcc66aa4a9
30 changed files with 1112 additions and 1021 deletions

View File

@@ -9,10 +9,6 @@
</div>
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" size="small" class="metal-form">
<el-alert v-if="ocrAvailable === false" type="error" :closable="false" show-icon style="margin-bottom:14px">
<span slot="title">发票识别服务已停止请联系信息化部门在服务恢复前您暂时无法上传发票附件</span>
</el-alert>
<div class="form-summary">
<div class="summary-left">
<div class="summary-title">发起日常报销</div>
@@ -57,27 +53,27 @@
</el-col>
</el-row>
<!-- 报销单据附件OCR触发区 -->
<!-- 报销单据附件 PDF 电子发票 -->
<el-form-item label="报销单据附件" prop="accessoryApplyIds">
<file-upload v-model="form.accessoryApplyIds" :limit="200" :file-size="50"
:file-type="['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx']" multiple
:disabled="ocrAvailable === false"
:file-type="['pdf']" multiple
@delete="onFileDelete" />
<div class="hint-text">
{{ ocrAvailable === false ? '识别服务不可用,暂时无法上传' : '上传发票、收据、付款截图等(支持 PDF/图片,上传后自动识别明细)' }}
仅支持 PDF 电子发票含数电票/电子普通发票/电子专用发票上传后自动解析金额与明细<br/>
扫描件 / 图片票 / 纸质票请先在开票平台下载 PDF 原件再上传
</div>
</el-form-item>
<!-- OCR 识别中提示 -->
<!-- 发票解析中提示 -->
<div v-if="anyOcrLoading" class="ocr-thinking">
<i class="el-icon-loading"></i>
<span>模型思考中正在识别发票内容</span>
<span>正在解析发票 PDF</span>
</div>
<!-- 发票明细条目表 -->
<div class="block-title">
发票明细
<span class="block-title-hint">上传发票后自动识别识别失败或无发票时可手动添加</span>
<span class="block-title-hint">上传 PDF 后自动解析解析失败或无发票时可手动添加</span>
<el-button size="mini" type="primary" plain icon="el-icon-plus" @click="addManualItem" style="margin-left:12px;vertical-align:middle">手动添加条目</el-button>
</div>
<div class="invoice-table" v-if="invoiceItems.length">
@@ -108,7 +104,7 @@
</el-tooltip>
<el-upload v-else :action="uploadFileUrl" :headers="uploadHeaders" :show-file-list="false"
:data="{ isPublic: 1 }" :on-success="(res) => onRowFileSuccess(res, idx)"
accept=".pdf,.jpg,.jpeg,.png" class="row-upload">
accept=".pdf" class="row-upload">
<el-button size="mini" type="text" icon="el-icon-paperclip"></el-button>
</el-upload>
</div>
@@ -243,15 +239,17 @@
</template>
<script>
import { addReimburseReq, checkReimburseOcrHealth, listFlowNode, listFlowTemplate, ocrReimburseInvoice } from '@/api/hrm';
import { addReimburseReq, listFlowNode, listFlowTemplate, ocrReimburseInvoice } from '@/api/hrm';
import { getEmployeeByUserId } from '@/api/hrm/employee';
import { ccFlowTask } from '@/api/hrm/flow';
import FileUpload from '@/components/FileUpload';
import { getToken } from '@/utils/auth';
import UserMultiSelect from '@/components/UserSelect/multi.vue';
import UserSelect from '@/components/UserSelect/single.vue';
import approverNameMixin from '@/views/hrm/minix/approverNameMixin.js';
export default {
mixins: [approverNameMixin],
name: 'HrmReimburseRequest',
components: { FileUpload, UserSelect, UserMultiSelect },
data () {
@@ -267,8 +265,6 @@ export default {
assigneeUserId: null,
assigneeUserName: '',
reimburseTypeOptions: ['差旅费', '招待费', '办公费', '交通费', '通讯费', '其他'],
// OCR服务状态null=检测中true=正常false=不可用
ocrAvailable: null,
uploadFileUrl: process.env.VUE_APP_BASE_API + '/system/oss/upload',
uploadHeaders: { Authorization: 'Bearer ' + getToken() },
// 发票明细条目
@@ -308,7 +304,7 @@ export default {
created () {
this.loadCurrentEmployee()
this.loadTemplates()
this.checkOcrHealth()
this.loadUserNameMap()
},
computed: {
currentApplicantText () {
@@ -346,15 +342,6 @@ export default {
onCcUsersSelected (users) { this.ccForm.selectedUsers = users || [] },
removeCcUser (user) { this.ccForm.selectedUsers = this.ccForm.selectedUsers.filter(u => u.userId !== user.userId) },
async checkOcrHealth () {
try {
const res = await checkReimburseOcrHealth()
this.ocrAvailable = res.code === 200 && res.data === true
} catch (e) {
this.ocrAvailable = false
}
},
async triggerOcr (ossId) {
if (this.ocrDoneSet.has(ossId)) return
this.ocrDoneSet.add(ossId)
@@ -396,8 +383,12 @@ export default {
this.invoiceItems.push({ ossId: ossId, itemName: '', reason: '', amount: 0, sortNo: this.invoiceItems.length })
}
} catch (e) {
console.error('[OCR] 识别失败', e)
this.$message.warning('发票识别失败,请手动填写事由和金额')
console.error('[Invoice] 解析失败', e)
const msg = (e && e.msg) || (e && e.message) || ''
this.$message.warning(
'发票解析失败,请确认上传的是开票平台下载的正规 PDF 电子发票(数电票 / 电子普通发票 / 电子专用发票)。' +
(msg ? ' 错误信息:' + msg : '')
)
this.invoiceItems.push({ ossId: ossId, itemName: '', reason: '', amount: 0, sortNo: this.invoiceItems.length })
} finally {
this.$set(this.ocrLoadingMap, ossId, false)
@@ -469,19 +460,6 @@ export default {
this.flowLoading = false
}
},
nodePreviewText (n, idx) {
const typeMap = { approve: '审批', cc: '抄送' }
const ruleMap = { fixed_user: '指定人员', role: '指定角色', position: '指定岗位', leader: '直属上级' }
const nodeType = typeMap[n.nodeType] || '节点'
const rule = ruleMap[n.approverRule] || '规则'
let detail = ''
try {
const arr = Array.isArray(n.approverValue) ? n.approverValue : JSON.parse(n.approverValue || '[]')
if (arr.length) detail = `${arr.join('、')}`
} catch (e) { detail = n.approverValue ? `${n.approverValue}` : '' }
const text = `${nodeType}${rule}${detail}`
return idx === this.flowNodes.length - 1 ? `${text} → 结束` : text
},
normalizeOssIds (val) {
if (!val) return ''
if (typeof val === 'string') return val
@@ -820,4 +798,6 @@ export default {
.approve-row .k { font-size: 14px; color: #606266; }
.selected-users { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
@import "./_form-compact.scss";
</style>