Compare commits

...

43 Commits

Author SHA1 Message Date
刘泉佳 b492dd35ea fix: 结算单列表已指定应付列定义补充 2026-11-02 19:58:01 +08:00
刘泉佳 acb74cbc9a fix: 结算单列表指定失败标签补悬浮原因 2026-11-02 19:56:54 +08:00
刘泉佳 9a51bd2cfd feat: 结算单/付款申请单列表指定失败悬浮展示原因 2026-11-02 19:54:15 +08:00
刘泉佳 4dd54f7e4c feat: 付款申请单列表增加已指定应付列 2026-11-02 19:36:04 +08:00
刘泉佳 6705763951 feat: 查看页指定失败行支持编辑匹配金额随重推生效 2026-11-02 19:16:41 +08:00
刘泉佳 4ba18acd25 feat: 查看页重推认领收票单并放开付款匹配金额编辑 2026-11-02 18:38:18 +08:00
刘泉佳 c07371450f feat: 发票登记弹窗匹配结算单金额支持手动修改 2026-11-02 18:38:18 +08:00
刘泉佳 5fbbb5f88f feat: 结算单发票信息增加是否已指定应付列 2026-11-02 18:10:53 +08:00
刘泉佳 1ed7955764 feat: 发票登记选票暂存展示且确认后才保存推送 2026-11-02 18:10:53 +08:00
刘泉佳 daab1001db fix: 付款管理批量同步增加遮罩并统一金蝶接口30s超时常量 2026-11-02 17:15:50 +08:00
刘泉佳 f5735e6bad fix: 收票登记提交返回列表后刷新列表数据 2026-11-02 16:36:00 +08:00
刘泉佳 bd9da83175 feat: 发票登记弹窗回显已认领收票单并补齐结算基本信息 2026-11-02 16:13:00 +08:00
刘泉佳 b59e43c9b7 feat: 认领收票单候选弹窗改为分页并悬浮展示推送失败原因 2026-11-02 16:12:52 +08:00
刘泉佳 eae50ab909 feat: 发票登记移至工具栏并回显已认领收票单 2026-11-02 15:39:26 +08:00
刘泉佳 ab7491e6d6 feat: 收票登记表单去税率展示字段 2026-11-02 09:36:41 +08:00
刘泉佳 d78d100824 feat: 全额认领不再校验匹配金额合计上限 2026-10-31 16:52:22 +08:00
刘泉佳 9908b543ea fix: 项目立项补充getRisk导入修复风险查询未定义 2026-10-31 16:37:01 +08:00
刘泉佳 46e7952e49 feat: 付款结算单选收票单全额认领并新增发票登记推送指定应付 2026-10-31 16:07:12 +08:00
刘泉佳 441d62a874 feat: 收票登记先票后款表单改造(手选结算组织/发票明细区块/去关联结算单) 2026-10-31 16:07:04 +08:00
刘泉佳 1a143aa05b fix: 合同管理删除hcsRepush重复导入修复构建 2026-10-31 16:07:04 +08:00
刘泉佳 cd58e8ebc7 Merge branch 'feature/20261027-hcs-bif-all-web-recBillSync' into feature/20261031-bfi-收票收款改造-web 2026-10-31 14:18:38 +08:00
刘泉佳 11d33df46a Merge branch 'master' into feature/20261027-hcs-bif-all-web
# Conflicts:
#	src/views/business/contract-manage.vue
2026-10-31 14:12:05 +08:00
刘泉佳 06de9477e5 feat: 客商收款信息开户行失焦匹配金蝶带出联行号并保存前校验
- 开户行 label 加问号提示,失焦调 A905-3 按行名精确匹配自动回填联行号,未命中提示检查
- 保存前按联行号校验金蝶档案,查不到拦截保存,接口异常提示稍后重试
- 回填后清除联行号字段残留校验提示
2026-10-31 10:16:38 +08:00
刘泉佳 f7ee9273c6 feat: 合同与客商列表客商风险标签统一悬浮显示明细提示 2026-10-30 21:40:04 +08:00
b2894lxlx d95163663b ✨ immediate-enabled关闭时隐藏项目/合同推送合规入口
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-30 16:34:45 +08:00
b2894lxlx 5ce91ec5d5 🐛 总单选址弹窗固定label宽度,并兼容运输类型非字符串
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-30 11:47:51 +08:00
b2894lxlx 8d85311bb3 ✨ 司机身份证OCR支持正反面识别并回填有效期
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-30 11:47:21 +08:00
b2894lxlx fe4255c59b 🐛 修复正式结算旧路径空白,按单号跳转付款/收款列表
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-30 11:40:32 +08:00
b2894lxlx 083d954765 🐛 放开本账号运单复制,并避免总单选地址时提前校验
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 22:44:01 +08:00
刘泉佳 78bb38e955 🐛 收款流水认领额度改用收款金额并新增剩余可认领金额列
认领只登记本地,额度由本地累计认领控制,金蝶未匹配金额不再作为上限;批量认领校验复用剩余可认领金额口径。
2026-10-29 20:07:53 +08:00
刘泉佳 2cf966d6e3 feat: 开票申请查看展示每张发票金蝶开票状态与结果备注 2026-10-29 18:17:42 +08:00
刘泉佳 d23a9b00de refactor: 收款流水未匹配金额字段名统一为unmatchAmountRec 2026-10-29 16:13:23 +08:00
刘泉佳 d89fd9db97 feat: 收款流水列表展示匹配与结算金额字段 2026-10-29 16:06:03 +08:00
刘泉佳 94ee14b683 feat: 收款流水列表展示收款人公司与核算组织编码 2026-10-29 15:05:28 +08:00
刘泉佳 a725d3078f fix: 收款流水详情收款金额显示名称修正 2026-10-29 14:12:55 +08:00
b2894lxlx 185a0f9aec Merge remote-tracking branch 'websoft/dev' 2026-10-29 14:05:12 +08:00
b2894lxlx a528788409 🐛 修复总单地址回填、结算费用英文项与项目部门提示
常用地址级联回填不再误清空;预结算/正式结算过滤 freightJson 杂项;项目查看态业务部门/平台公司支持悬停完整信息。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 14:04:55 +08:00
刘泉佳 1957a78c00 fix: 收款流水同步接口超时时间延长到30秒 2026-10-29 12:55:03 +08:00
刘泉佳 acfe9bdd74 feat: 收款流水页面适配A301-1数据源并下线推送金蝶相关交互 2026-10-29 11:20:18 +08:00
weicw 72471f48de 🔧 类型检查改用 jsconfig 并锁定 TypeScript 5.9,恢复 vue-tsc 可用 2026-10-29 09:45:31 +08:00
weicw 415e1fd58d ✨ 维修记录列表展示关联运单与关联异常 2026-10-29 08:58:21 +08:00
weicw 114c2fd2af 🔀 合并主干 master 到 dev 2026-10-29 00:16:39 +08:00
weicw 9a603a3796 🔀 合并 master 回 dev,追平 HCS/BFI 与项目变更菜单 2026-10-27 17:44:08 +08:00
41 changed files with 1632 additions and 839 deletions
+2 -2
View File
@@ -8,7 +8,7 @@
"build:prod": "vite build --mode production && node scripts/zip-dist.mjs",
"zip:dist": "node scripts/zip-dist.mjs",
"serve": "vite preview --host",
"typecheck": "vue-tsc --noEmit"
"typecheck": "vue-tsc -p jsconfig.json --noEmit"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
@@ -49,7 +49,7 @@
"prettier": "^2.8.7",
"sass": "^1.85.1",
"terser": "^5.31.1",
"typescript": "^7.0.2",
"typescript": "5.9.3",
"unplugin-auto-import": "^0.11.2",
"vite": "^5.4.19",
"vite-plugin-compression": "^0.5.1",
+94 -295
View File
@@ -13,31 +13,31 @@ importers:
dependencies:
'@element-plus/icons-vue':
specifier: ^2.3.1
version: 2.3.2(vue@3.5.34(typescript@7.0.2))
version: 2.3.2(vue@3.5.34(typescript@5.9.3))
'@open-file-viewer/core':
specifier: ^0.1.27
version: 0.1.27(pdfjs-dist@6.1.200)
'@open-file-viewer/vue':
specifier: ^0.1.27
version: 0.1.27(@open-file-viewer/core@0.1.27(pdfjs-dist@6.1.200))(vue@3.5.34(typescript@7.0.2))
version: 0.1.27(@open-file-viewer/core@0.1.27(pdfjs-dist@6.1.200))(vue@3.5.34(typescript@5.9.3))
'@saber/nf-design-base-elp':
specifier: ^1.3.0
version: 1.3.0
'@saber/nf-form-design-elp':
specifier: ^1.6.1
version: 1.6.1(vue@3.5.34(typescript@7.0.2))
version: 1.6.1(vue@3.5.34(typescript@5.9.3))
'@saber/nf-form-elp':
specifier: ^1.6.3
version: 1.7.0
'@smallwei/avue':
specifier: ~3.9.2
version: 3.9.2(patch_hash=5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db)(@element-plus/icons-vue@2.3.2(vue@3.5.34(typescript@7.0.2)))(debug@4.4.3(supports-color@7.2.0))(element-plus@2.14.2(vue@3.5.34(typescript@7.0.2)))(vue@3.5.34(typescript@7.0.2))
version: 3.9.2(patch_hash=5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db)(@element-plus/icons-vue@2.3.2(vue@3.5.34(typescript@5.9.3)))(debug@4.4.3(supports-color@7.2.0))(element-plus@2.14.2(vue@3.5.34(typescript@5.9.3)))(vue@3.5.34(typescript@5.9.3))
animate.css:
specifier: ^4.1.1
version: 4.1.1
avue-plugin-ueditor:
specifier: ^1.0.4
version: 1.0.4(axios@1.16.0(debug@4.4.3(supports-color@7.2.0)))(vue@3.5.34(typescript@7.0.2))
version: 1.0.4(axios@1.16.0(debug@4.4.3(supports-color@7.2.0)))(vue@3.5.34(typescript@5.9.3))
axios:
specifier: ^1.13.5
version: 1.16.0(debug@4.4.3(supports-color@7.2.0))
@@ -58,7 +58,7 @@ importers:
version: 6.1.0
element-plus:
specifier: ^2.14.2
version: 2.14.2(vue@3.5.34(typescript@7.0.2))
version: 2.14.2(vue@3.5.34(typescript@5.9.3))
exceljs:
specifier: ^4.4.0
version: 4.4.0
@@ -85,29 +85,29 @@ importers:
version: 0.4.0
vue:
specifier: ^3.5.13
version: 3.5.34(typescript@7.0.2)
version: 3.5.34(typescript@5.9.3)
vue-i18n:
specifier: ^11.4.2
version: 11.4.2(vue@3.5.34(typescript@7.0.2))
version: 11.4.2(vue@3.5.34(typescript@5.9.3))
vue-pdf-embed:
specifier: ^2.1.6
version: 2.1.6(vue@3.5.34(typescript@7.0.2))
version: 2.1.6(vue@3.5.34(typescript@5.9.3))
vue-router:
specifier: ^4.3.2
version: 4.6.4(vue@3.5.34(typescript@7.0.2))
version: 4.6.4(vue@3.5.34(typescript@5.9.3))
vue3-clipboard:
specifier: ^1.0.0
version: 1.0.0(vue@3.5.34(typescript@7.0.2))
version: 1.0.0(vue@3.5.34(typescript@5.9.3))
vuex:
specifier: ^4.1.0
version: 4.1.0(vue@3.5.34(typescript@7.0.2))
version: 4.1.0(vue@3.5.34(typescript@5.9.3))
xlsx:
specifier: 0.18.5
version: 0.18.5
devDependencies:
'@vitejs/plugin-vue':
specifier: ^5.2.4
version: 5.2.4(vite@5.4.21(sass@1.99.0)(terser@5.46.2))(vue@3.5.34(typescript@7.0.2))
version: 5.2.4(vite@5.4.21(sass@1.99.0)(terser@5.46.2))(vue@3.5.34(typescript@5.9.3))
'@vue/compiler-sfc':
specifier: ^3.4.27
version: 3.5.34
@@ -121,11 +121,11 @@ importers:
specifier: ^5.31.1
version: 5.46.2
typescript:
specifier: ^7.0.2
version: 7.0.2
specifier: 5.9.3
version: 5.9.3
unplugin-auto-import:
specifier: ^0.11.2
version: 0.11.5(@vueuse/core@14.3.0(vue@3.5.34(typescript@7.0.2)))(rollup@4.60.3)
version: 0.11.5(@vueuse/core@14.3.0(vue@3.5.34(typescript@5.9.3)))(rollup@4.60.3)
vite:
specifier: ^5.4.19
version: 5.4.21(sass@1.99.0)(terser@5.46.2)
@@ -137,7 +137,7 @@ importers:
version: 0.4.0(vite@5.4.21(sass@1.99.0)(terser@5.46.2))
vue-tsc:
specifier: ^3.3.11
version: 3.3.13(typescript@7.0.2)
version: 3.3.13(typescript@5.9.3)
packages:
@@ -771,126 +771,6 @@ packages:
'@types/web-bluetooth@0.0.21':
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
'@typescript/typescript-aix-ppc64@7.0.2':
resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==}
engines: {node: '>=16.20.0'}
cpu: [ppc64]
os: [aix]
'@typescript/typescript-darwin-arm64@7.0.2':
resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [darwin]
'@typescript/typescript-darwin-x64@7.0.2':
resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [darwin]
'@typescript/typescript-freebsd-arm64@7.0.2':
resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [freebsd]
'@typescript/typescript-freebsd-x64@7.0.2':
resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [freebsd]
'@typescript/typescript-linux-arm64@7.0.2':
resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [linux]
'@typescript/typescript-linux-arm@7.0.2':
resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==}
engines: {node: '>=16.20.0'}
cpu: [arm]
os: [linux]
'@typescript/typescript-linux-loong64@7.0.2':
resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==}
engines: {node: '>=16.20.0'}
cpu: [loong64]
os: [linux]
'@typescript/typescript-linux-mips64el@7.0.2':
resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==}
engines: {node: '>=16.20.0'}
cpu: [mips64el]
os: [linux]
'@typescript/typescript-linux-ppc64@7.0.2':
resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==}
engines: {node: '>=16.20.0'}
cpu: [ppc64]
os: [linux]
'@typescript/typescript-linux-riscv64@7.0.2':
resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==}
engines: {node: '>=16.20.0'}
cpu: [riscv64]
os: [linux]
'@typescript/typescript-linux-s390x@7.0.2':
resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==}
engines: {node: '>=16.20.0'}
cpu: [s390x]
os: [linux]
'@typescript/typescript-linux-x64@7.0.2':
resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [linux]
'@typescript/typescript-netbsd-arm64@7.0.2':
resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [netbsd]
'@typescript/typescript-netbsd-x64@7.0.2':
resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [netbsd]
'@typescript/typescript-openbsd-arm64@7.0.2':
resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [openbsd]
'@typescript/typescript-openbsd-x64@7.0.2':
resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [openbsd]
'@typescript/typescript-sunos-x64@7.0.2':
resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [sunos]
'@typescript/typescript-win32-arm64@7.0.2':
resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [win32]
'@typescript/typescript-win32-x64@7.0.2':
resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [win32]
'@uppy/companion-client@2.2.2':
resolution: {integrity: sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==}
@@ -2194,9 +2074,9 @@ packages:
typedarray@0.0.6:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
typescript@7.0.2:
resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==}
engines: {node: '>=16.20.0'}
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
ufo@1.6.4:
@@ -2408,9 +2288,9 @@ snapshots:
'@ctrl/tinycolor@4.2.0': {}
'@element-plus/icons-vue@2.3.2(vue@3.5.34(typescript@7.0.2))':
'@element-plus/icons-vue@2.3.2(vue@3.5.34(typescript@5.9.3))':
dependencies:
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
'@esbuild/aix-ppc64@0.21.5':
optional: true
@@ -2651,10 +2531,10 @@ snapshots:
transitivePeerDependencies:
- geotiff
'@open-file-viewer/vue@0.1.27(@open-file-viewer/core@0.1.27(pdfjs-dist@6.1.200))(vue@3.5.34(typescript@7.0.2))':
'@open-file-viewer/vue@0.1.27(@open-file-viewer/core@0.1.27(pdfjs-dist@6.1.200))(vue@3.5.34(typescript@5.9.3))':
dependencies:
'@open-file-viewer/core': 0.1.27(pdfjs-dist@6.1.200)
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
'@parcel/watcher-android-arm64@2.5.6':
optional: true
@@ -2806,9 +2686,9 @@ snapshots:
codemirror: 5.65.21
randomcolor: 0.6.2
'@saber/nf-form-design-elp@1.6.1(vue@3.5.34(typescript@7.0.2))':
'@saber/nf-form-design-elp@1.6.1(vue@3.5.34(typescript@5.9.3))':
dependencies:
vuedraggable: 4.1.0(vue@3.5.34(typescript@7.0.2))
vuedraggable: 4.1.0(vue@3.5.34(typescript@5.9.3))
transitivePeerDependencies:
- vue
@@ -2820,16 +2700,16 @@ snapshots:
countup.js: 2.10.0
signature_pad: 5.1.3
'@smallwei/avue@3.9.2(patch_hash=5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db)(@element-plus/icons-vue@2.3.2(vue@3.5.34(typescript@7.0.2)))(debug@4.4.3(supports-color@7.2.0))(element-plus@2.14.2(vue@3.5.34(typescript@7.0.2)))(vue@3.5.34(typescript@7.0.2))':
'@smallwei/avue@3.9.2(patch_hash=5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db)(@element-plus/icons-vue@2.3.2(vue@3.5.34(typescript@5.9.3)))(debug@4.4.3(supports-color@7.2.0))(element-plus@2.14.2(vue@3.5.34(typescript@5.9.3)))(vue@3.5.34(typescript@5.9.3))':
dependencies:
'@element-plus/icons-vue': 2.3.2(vue@3.5.34(typescript@7.0.2))
'@element-plus/icons-vue': 2.3.2(vue@3.5.34(typescript@5.9.3))
axios: 0.21.4(debug@4.4.3(supports-color@7.2.0))
countup.js: 1.9.3
dayjs: 1.11.20
element-plus: 2.14.2(vue@3.5.34(typescript@7.0.2))
element-plus: 2.14.2(vue@3.5.34(typescript@5.9.3))
lodash: 4.18.1
lodash-es: 4.18.1
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
transitivePeerDependencies:
- debug
@@ -2856,66 +2736,6 @@ snapshots:
'@types/web-bluetooth@0.0.21': {}
'@typescript/typescript-aix-ppc64@7.0.2':
optional: true
'@typescript/typescript-darwin-arm64@7.0.2':
optional: true
'@typescript/typescript-darwin-x64@7.0.2':
optional: true
'@typescript/typescript-freebsd-arm64@7.0.2':
optional: true
'@typescript/typescript-freebsd-x64@7.0.2':
optional: true
'@typescript/typescript-linux-arm64@7.0.2':
optional: true
'@typescript/typescript-linux-arm@7.0.2':
optional: true
'@typescript/typescript-linux-loong64@7.0.2':
optional: true
'@typescript/typescript-linux-mips64el@7.0.2':
optional: true
'@typescript/typescript-linux-ppc64@7.0.2':
optional: true
'@typescript/typescript-linux-riscv64@7.0.2':
optional: true
'@typescript/typescript-linux-s390x@7.0.2':
optional: true
'@typescript/typescript-linux-x64@7.0.2':
optional: true
'@typescript/typescript-netbsd-arm64@7.0.2':
optional: true
'@typescript/typescript-netbsd-x64@7.0.2':
optional: true
'@typescript/typescript-openbsd-arm64@7.0.2':
optional: true
'@typescript/typescript-openbsd-x64@7.0.2':
optional: true
'@typescript/typescript-sunos-x64@7.0.2':
optional: true
'@typescript/typescript-win32-arm64@7.0.2':
optional: true
'@typescript/typescript-win32-x64@7.0.2':
optional: true
'@uppy/companion-client@2.2.2':
dependencies:
'@uppy/utils': 4.1.3
@@ -2945,10 +2765,10 @@ snapshots:
'@uppy/utils': 4.1.3
nanoid: 3.3.12
'@vitejs/plugin-vue@5.2.4(vite@5.4.21(sass@1.99.0)(terser@5.46.2))(vue@3.5.34(typescript@7.0.2))':
'@vitejs/plugin-vue@5.2.4(vite@5.4.21(sass@1.99.0)(terser@5.46.2))(vue@3.5.34(typescript@5.9.3))':
dependencies:
vite: 5.4.21(sass@1.99.0)(terser@5.46.2)
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
'@volar/language-core@2.4.28':
dependencies:
@@ -2956,13 +2776,13 @@ snapshots:
'@volar/source-map@2.4.28': {}
'@volar/typescript@2.4.28(typescript@7.0.2)':
'@volar/typescript@2.4.28(typescript@5.9.3)':
dependencies:
'@volar/language-core': 2.4.28
path-browserify: 1.0.1
vscode-uri: 3.2.0
optionalDependencies:
typescript: 7.0.2
typescript: 5.9.3
'@vue/compiler-core@3.5.34':
dependencies:
@@ -3022,51 +2842,51 @@ snapshots:
'@vue/shared': 3.5.34
csstype: 3.2.3
'@vue/server-renderer@3.5.34(vue@3.5.34(typescript@7.0.2))':
'@vue/server-renderer@3.5.34(vue@3.5.34(typescript@5.9.3))':
dependencies:
'@vue/compiler-ssr': 3.5.34
'@vue/shared': 3.5.34
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
'@vue/shared@3.5.34': {}
'@vueuse/core@14.3.0(vue@3.5.34(typescript@7.0.2))':
'@vueuse/core@14.3.0(vue@3.5.34(typescript@5.9.3))':
dependencies:
'@types/web-bluetooth': 0.0.21
'@vueuse/metadata': 14.3.0
'@vueuse/shared': 14.3.0(vue@3.5.34(typescript@7.0.2))
vue: 3.5.34(typescript@7.0.2)
'@vueuse/shared': 14.3.0(vue@3.5.34(typescript@5.9.3))
vue: 3.5.34(typescript@5.9.3)
'@vueuse/metadata@14.3.0': {}
'@vueuse/shared@14.3.0(vue@3.5.34(typescript@7.0.2))':
'@vueuse/shared@14.3.0(vue@3.5.34(typescript@5.9.3))':
dependencies:
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
'@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
'@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
dependencies:
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
dom7: 3.0.0
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
dom7: 4.0.6
is-url: 1.2.4
lodash.throttle: 4.1.1
nanoid: 5.1.11
slate: 0.123.0
snabbdom: 3.6.3
'@wangeditor-next/code-highlight@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)':
'@wangeditor-next/code-highlight@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)':
dependencies:
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
dom7: 4.0.6
prismjs: 1.30.0
slate: 0.123.0
snabbdom: 3.6.3
'@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
'@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
dependencies:
'@types/event-emitter': 0.3.5
'@uppy/core': 2.3.4
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
dom7: 3.0.0
dom7: 4.0.6
event-emitter: 0.3.5
html-void-elements: 3.0.0
i18next: 23.16.8
@@ -3087,13 +2907,13 @@ snapshots:
dependencies:
'@uppy/core': 2.3.4
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
'@wangeditor-next/basic-modules': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/code-highlight': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/list-module': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/table-module': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/upload-image-module': 2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/video-module': 2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/basic-modules': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/code-highlight': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/list-module': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/table-module': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/upload-image-module': 2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/video-module': 2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
dom7: 4.0.6
is-hotkey: 0.2.0
lodash.camelcase: 4.3.0
@@ -3106,16 +2926,16 @@ snapshots:
slate: 0.123.0
snabbdom: 3.6.3
'@wangeditor-next/list-module@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)':
'@wangeditor-next/list-module@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)':
dependencies:
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
dom7: 4.0.6
slate: 0.123.0
snabbdom: 3.6.3
'@wangeditor-next/table-module@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
'@wangeditor-next/table-module@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
dependencies:
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
dom7: 4.0.6
lodash.debounce: 4.0.8
lodash.throttle: 4.1.1
@@ -3123,22 +2943,22 @@ snapshots:
slate: 0.123.0
snabbdom: 3.6.3
'@wangeditor-next/upload-image-module@2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.123.0)(snabbdom@3.6.3)':
'@wangeditor-next/upload-image-module@2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.123.0)(snabbdom@3.6.3)':
dependencies:
'@uppy/core': 2.3.4
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
'@wangeditor-next/basic-modules': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/basic-modules': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
dom7: 4.0.6
lodash.foreach: 4.5.0
slate: 0.123.0
snabbdom: 3.6.3
'@wangeditor-next/video-module@2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
'@wangeditor-next/video-module@2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
dependencies:
'@uppy/core': 2.3.4
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
dom7: 4.0.6
nanoid: 5.1.11
slate: 0.123.0
@@ -3185,10 +3005,10 @@ snapshots:
slate-history: 0.66.0(slate@0.72.8)
snabbdom: 3.6.3
'@wangeditor/editor-for-vue@5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.34(typescript@7.0.2))':
'@wangeditor/editor-for-vue@5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.34(typescript@5.9.3))':
dependencies:
'@wangeditor/editor': 5.1.23
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
'@wangeditor/editor@5.1.23':
dependencies:
@@ -3317,12 +3137,12 @@ snapshots:
asynckit@0.4.0: {}
avue-plugin-ueditor@1.0.4(axios@1.16.0(debug@4.4.3(supports-color@7.2.0)))(vue@3.5.34(typescript@7.0.2)):
avue-plugin-ueditor@1.0.4(axios@1.16.0(debug@4.4.3(supports-color@7.2.0)))(vue@3.5.34(typescript@5.9.3)):
dependencies:
'@wangeditor/editor': 5.1.23
'@wangeditor/editor-for-vue': 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.34(typescript@7.0.2))
'@wangeditor/editor-for-vue': 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.34(typescript@5.9.3))
axios: 1.16.0(debug@4.4.3(supports-color@7.2.0))
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
axios@0.21.4(debug@4.4.3(supports-color@7.2.0)):
dependencies:
@@ -3575,15 +3395,15 @@ snapshots:
tslib: 2.3.0
zrender: 6.1.0
element-plus@2.14.2(vue@3.5.34(typescript@7.0.2)):
element-plus@2.14.2(vue@3.5.34(typescript@5.9.3)):
dependencies:
'@ctrl/tinycolor': 4.2.0
'@element-plus/icons-vue': 2.3.2(vue@3.5.34(typescript@7.0.2))
'@element-plus/icons-vue': 2.3.2(vue@3.5.34(typescript@5.9.3))
'@floating-ui/dom': 1.7.6
'@popperjs/core': '@sxzz/popperjs-es@2.11.8'
'@types/lodash': 4.17.24
'@types/lodash-es': 4.17.12
'@vueuse/core': 14.3.0(vue@3.5.34(typescript@7.0.2))
'@vueuse/core': 14.3.0(vue@3.5.34(typescript@5.9.3))
async-validator: 4.2.5
dayjs: 1.11.20
lodash: 4.18.1
@@ -3591,7 +3411,7 @@ snapshots:
lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.18.1)(lodash@4.18.1)
memoize-one: 6.0.0
normalize-wheel-es: 1.2.0
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
vue-component-type-helpers: 3.3.6
end-of-stream@1.4.5:
@@ -4352,28 +4172,7 @@ snapshots:
typedarray@0.0.6: {}
typescript@7.0.2:
optionalDependencies:
'@typescript/typescript-aix-ppc64': 7.0.2
'@typescript/typescript-darwin-arm64': 7.0.2
'@typescript/typescript-darwin-x64': 7.0.2
'@typescript/typescript-freebsd-arm64': 7.0.2
'@typescript/typescript-freebsd-x64': 7.0.2
'@typescript/typescript-linux-arm': 7.0.2
'@typescript/typescript-linux-arm64': 7.0.2
'@typescript/typescript-linux-loong64': 7.0.2
'@typescript/typescript-linux-mips64el': 7.0.2
'@typescript/typescript-linux-ppc64': 7.0.2
'@typescript/typescript-linux-riscv64': 7.0.2
'@typescript/typescript-linux-s390x': 7.0.2
'@typescript/typescript-linux-x64': 7.0.2
'@typescript/typescript-netbsd-arm64': 7.0.2
'@typescript/typescript-netbsd-x64': 7.0.2
'@typescript/typescript-openbsd-arm64': 7.0.2
'@typescript/typescript-openbsd-x64': 7.0.2
'@typescript/typescript-sunos-x64': 7.0.2
'@typescript/typescript-win32-arm64': 7.0.2
'@typescript/typescript-win32-x64': 7.0.2
typescript@5.9.3: {}
ufo@1.6.4: {}
@@ -4397,7 +4196,7 @@ snapshots:
universalify@2.0.1: {}
unplugin-auto-import@0.11.5(@vueuse/core@14.3.0(vue@3.5.34(typescript@7.0.2)))(rollup@4.60.3):
unplugin-auto-import@0.11.5(@vueuse/core@14.3.0(vue@3.5.34(typescript@5.9.3)))(rollup@4.60.3):
dependencies:
'@antfu/utils': 0.7.10
'@rollup/pluginutils': 5.3.0(rollup@4.60.3)
@@ -4406,7 +4205,7 @@ snapshots:
unimport: 1.3.0(rollup@4.60.3)
unplugin: 1.16.1
optionalDependencies:
'@vueuse/core': 14.3.0(vue@3.5.34(typescript@7.0.2))
'@vueuse/core': 14.3.0(vue@3.5.34(typescript@5.9.3))
transitivePeerDependencies:
- rollup
@@ -4465,54 +4264,54 @@ snapshots:
vue-component-type-helpers@3.3.6: {}
vue-i18n@11.4.2(vue@3.5.34(typescript@7.0.2)):
vue-i18n@11.4.2(vue@3.5.34(typescript@5.9.3)):
dependencies:
'@intlify/core-base': 11.4.2
'@intlify/devtools-types': 11.4.2
'@intlify/shared': 11.4.2
'@vue/devtools-api': 6.6.4
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
vue-pdf-embed@2.1.6(vue@3.5.34(typescript@7.0.2)):
vue-pdf-embed@2.1.6(vue@3.5.34(typescript@5.9.3)):
dependencies:
pdfjs-dist: 6.2.108
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
vue-router@4.6.4(vue@3.5.34(typescript@7.0.2)):
vue-router@4.6.4(vue@3.5.34(typescript@5.9.3)):
dependencies:
'@vue/devtools-api': 6.6.4
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
vue-tsc@3.3.13(typescript@7.0.2):
vue-tsc@3.3.13(typescript@5.9.3):
dependencies:
'@volar/typescript': 2.4.28(typescript@7.0.2)
'@volar/typescript': 2.4.28(typescript@5.9.3)
'@vue/language-core': 3.3.13
typescript: 7.0.2
typescript: 5.9.3
vue3-clipboard@1.0.0(vue@3.5.34(typescript@7.0.2)):
vue3-clipboard@1.0.0(vue@3.5.34(typescript@5.9.3)):
dependencies:
clipboard: 2.0.11
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
vue@3.5.34(typescript@7.0.2):
vue@3.5.34(typescript@5.9.3):
dependencies:
'@vue/compiler-dom': 3.5.34
'@vue/compiler-sfc': 3.5.34
'@vue/runtime-dom': 3.5.34
'@vue/server-renderer': 3.5.34(vue@3.5.34(typescript@7.0.2))
'@vue/server-renderer': 3.5.34(vue@3.5.34(typescript@5.9.3))
'@vue/shared': 3.5.34
optionalDependencies:
typescript: 7.0.2
typescript: 5.9.3
vuedraggable@4.1.0(vue@3.5.34(typescript@7.0.2)):
vuedraggable@4.1.0(vue@3.5.34(typescript@5.9.3)):
dependencies:
sortablejs: 1.14.0
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
vuex@4.1.0(vue@3.5.34(typescript@7.0.2)):
vuex@4.1.0(vue@3.5.34(typescript@5.9.3)):
dependencies:
'@vue/devtools-api': 6.6.4
vue: 3.5.34(typescript@7.0.2)
vue: 3.5.34(typescript@5.9.3)
webpack-virtual-modules@0.6.2: {}
+37
View File
@@ -2,6 +2,43 @@ import request from '@/axios';
const baseUrl = '/blade-transport/hcs';
/** 进程内缓存,避免列表页重复请求推送开关 */
let immediateEnabledCache = null;
let immediateEnabledPromise = null;
/**
* 查询 HCS 即时推送开关(thirdParty.hcs.push.immediate-enabled)。
* 关闭时项目管理/合同管理隐藏「推送合规」「批量推送合规」。
*/
export const getPushConfig = () =>
request({
url: `${baseUrl}/push-config`,
method: 'get',
});
export const loadHcsImmediateEnabled = () => {
if (immediateEnabledCache !== null) {
return Promise.resolve(immediateEnabledCache);
}
if (immediateEnabledPromise) {
return immediateEnabledPromise;
}
immediateEnabledPromise = getPushConfig()
.then(res => {
const data = res?.data?.data ?? res?.data ?? {};
immediateEnabledCache = data.immediateEnabled === true || data.immediateEnabled === 'true';
return immediateEnabledCache;
})
.catch(() => {
immediateEnabledCache = false;
return false;
})
.finally(() => {
immediateEnabledPromise = null;
});
return immediateEnabledPromise;
};
// 合规推送涉及 HCS 远程多次调用(风险校验+附件转传+起草),可达 20s+,覆盖全局 10s 超时
export const repush = (bizType, id) =>
request({
+9
View File
@@ -38,6 +38,15 @@ export const returnBill = data => request({ url: `${baseUrl}/return`, method: 'p
export const voidBill = data => request({ url: `${baseUrl}/void`, method: 'post', data });
export const pushKingdee = ids =>
request({ url: `${baseUrl}/push-kingdee`, method: 'post', timeout: 30000, data: ids });
// 先票后款:发票明细查询(收票登记明细区块按票号带出发票明细行)
export const invoiceDetailQuery = invoiceNo =>
request({ url: `${baseUrl}/invoice-detail-query`, method: 'get', params: { invoiceNo } });
// 先票后款:结算单认领收票单候选分页(审批通过且未被有效应付结算单认领)
export const claimCandidates = params =>
request({ url: `${baseUrl}/claim-candidates`, method: 'get', params });
// 先票后款:结算单已认领收票登记列表(发票登记对话框回显已认领票)
export const claimedReceipts = formalSettlementId =>
request({ url: `${baseUrl}/claimed-list`, method: 'get', params: { formalSettlementId } });
export const approvalStatusOptions = [
{ label: '草稿', value: 'draft' },
+3 -3
View File
@@ -27,13 +27,13 @@ export const returnBill = data => request({ url: `${baseUrl}/return`, method: 'p
export const voidBill = data => request({ url: `${baseUrl}/void`, method: 'post', data });
// 同步金蝶涉及多次金蝶远程接口调用(A103-2 预检 + A103-1/A103-3 推送),单笔可达 20s+,覆盖全局 10s 超时
export const syncKingdee = id =>
request({ url: `${baseUrl}/sync-kingdee`, method: 'post', timeout: 30000, params: { id } });
request({ url: `${baseUrl}/sync-kingdee`, method: 'post', timeout: APPROVE_TIMEOUT, params: { id } });
// 批量同步逐单单据级事务串行推送,耗时随单量线性增长
export const syncKingdeeBatch = ids =>
request({ url: `${baseUrl}/sync-kingdee-batch`, method: 'post', timeout: 30000, data: ids });
request({ url: `${baseUrl}/sync-kingdee-batch`, method: 'post', timeout: APPROVE_TIMEOUT, data: ids });
// 回写任务按 paymentNo 分批远程查询 A103-2/A103-4,同样可能超过全局超时
export const syncKingdeeResult = () =>
request({ url: `${baseUrl}/sync-kingdee-result`, method: 'post', timeout: 30000 });
request({ url: `${baseUrl}/sync-kingdee-result`, method: 'post', timeout: APPROVE_TIMEOUT });
export const paymentTypeOptions = [
{ label: '项目预付', value: 'project_advance' },
-8
View File
@@ -8,20 +8,12 @@ export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get'
export const updateAttachments = data =>
request({ url: `${baseUrl}/attachments`, method: 'post', data });
export const voidClaim = id => request({ url: `${baseUrl}/void`, method: 'post', params: { id } });
export const queryKingdeeResult = () =>
request({ url: `${baseUrl}/query-kingdee-result`, method: 'post' });
export const claimStatusOptions = [
{ label: '已认领', value: 'claimed' },
{ label: '已作废', value: 'voided' },
];
export const pushStatusOptions = [
{ label: '未推送', value: 'none' },
{ label: '已推送', value: 'pushed' },
{ label: '推送失败', value: 'failed' },
];
export const kingdeeBillStatusOptions = [
{ label: '未生成', value: 'none' },
{ label: '已推送', value: 'pushed' },
+4 -9
View File
@@ -2,6 +2,9 @@ import request from '@/axios';
const baseUrl = '/blade-transport/receipt-flow';
/** 收款流水同步需远程拉取金蝶收款单并逐条落库,覆盖全局 10s 超时 */
export const SYNC_TIMEOUT = 30000;
export const getList = (current, size, params) =>
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
@@ -12,9 +15,7 @@ export const getSettlementCandidates = (keyword, flowId) =>
params: { keyword, flowId },
});
export const claim = data => request({ url: `${baseUrl}/claim`, method: 'post', data });
export const sync = () => request({ url: `${baseUrl}/sync`, method: 'post' });
export const pushKingdee = flowId =>
request({ url: `${baseUrl}/push-kingdee`, method: 'post', params: { flowId } });
export const sync = () => request({ url: `${baseUrl}/sync`, method: 'post', timeout: SYNC_TIMEOUT });
export const claimStatusOptions = [
{ label: '未认领', value: 'unclaimed' },
@@ -22,12 +23,6 @@ export const claimStatusOptions = [
{ label: '认领完成', value: 'claimed' },
];
export const pushStatusOptions = [
{ label: '未推送', value: 'none' },
{ label: '已推送', value: 'pushed' },
{ label: '推送失败', value: 'failed' },
];
export const kingdeeBillStatusOptions = [
{ label: '已推送', value: 'pushed' },
{ label: '审核通过', value: 'approved' },
+16
View File
@@ -58,5 +58,21 @@ export const applyPayments = data =>
request({ url: `${baseUrl}/apply-payments`, method: 'post', data });
export const claimInvoices = data =>
request({ url: `${baseUrl}/claim-invoices`, method: 'post', data });
// 先票后款:发票登记确认推送(认领收票单逐单 A302-6 指定应付),涉及多次金蝶远程调用,覆盖全局超时
export const invoiceAssignKingdee = id =>
request({
url: `${baseUrl}/invoice-assign-kingdee`,
method: 'post',
timeout: 30000,
params: { id },
});
// 先票后款:查看页重推认领收票单(收票单A303-2+指定应付A302-6 一步式),涉及多次金蝶远程调用,覆盖全局超时
export const repushClaimedInvoice = (id, invoiceNo, matchedAmount) =>
request({
url: `${baseUrl}/repush-claimed-invoice`,
method: 'post',
timeout: 30000,
params: { id, invoiceNo, matchedAmount },
});
export const queryInvoicePool = params =>
request({ url: `${baseUrl}/invoice-pool`, method: 'get', params });
+24
View File
@@ -175,6 +175,30 @@ export const querySupplierCode = creditCode => {
});
};
// 金蝶开户行查询(A905-3):收款信息填写开户行失焦后精确匹配带出联行号
export const queryKingdeeBank = bankName => {
return request({
url: '/blade-transport/customer-archive/query-bank',
method: 'get',
timeout: 30000,
params: {
bankName,
},
});
};
// 金蝶联行号校验(A905-3):收款信息保存前按联行号精确匹配,查不到不允许保存
export const queryKingdeeBankByNumber = cnapsCode => {
return request({
url: '/blade-transport/customer-archive/query-bank-by-number',
method: 'get',
timeout: 30000,
params: {
cnapsCode,
},
});
};
export const syncKingdee = id => {
return request({
url: '/blade-transport/customer-archive/sync-kingdee',
+1
View File
@@ -170,6 +170,7 @@ export const paymentApplicationOption = ({
{ label: '当前处理人', prop: 'currentProcessor', minWidth: 120, formatter: textFormatter },
{ label: '金蝶单据号', prop: 'kingdeeBillNo', minWidth: 130, formatter: textFormatter },
{ label: '金蝶单据状态', prop: 'kingdeeStatusName', slot: true, minWidth: 130 },
{ label: '已指定应付', prop: 'kingdeeAssignStatus', slot: true, minWidth: 120 },
{ label: '金蝶锁状态', prop: 'kingdeeLockStatus', slot: true, minWidth: 150 },
{ label: '附件同步', prop: 'attachmentSyncStatusName', minWidth: 100, formatter: textFormatter },
],
+2 -2
View File
@@ -1,7 +1,7 @@
export const receiptClaimRecordTableColumns = [
{ prop: 'receiptNoticeNo', label: '认领通知单', minWidth: 150, link: true },
{ prop: 'receiptNoticeNo', label: '收款单号', minWidth: 150, link: true },
{ prop: 'payerName', label: '付款人', minWidth: 130 },
{ prop: 'receiptAmount', label: '付款金额', minWidth: 130, money: true },
{ prop: 'receiptAmount', label: '收款金额', minWidth: 130, money: true },
{ prop: 'counterpartyName', label: '对方户名', minWidth: 150 },
{ prop: 'counterpartyAccount', label: '对方账号', minWidth: 170 },
{ prop: 'counterpartyBank', label: '对方开户行', minWidth: 150 },
+12 -7
View File
@@ -1,18 +1,23 @@
export const receiptFlowTableColumns = [
{ prop: 'receiptNoticeNo', label: '认领通知单', minWidth: 150, link: true },
{ prop: 'receiptNoticeNo', label: '收款单号', minWidth: 150, link: true },
{ prop: 'payerName', label: '付款人', minWidth: 130 },
{ prop: 'receiptAmount', label: '付款金额', minWidth: 130, money: true },
{ prop: 'receiptAmount', label: '收款金额', minWidth: 130, money: true },
{ prop: 'counterpartyName', label: '对方户名', minWidth: 150 },
{ prop: 'counterpartyAccount', label: '对方账号', minWidth: 170 },
{ prop: 'counterpartyBank', label: '对方开户行', minWidth: 150 },
{ prop: 'payeeName', label: '收款人(公司)', minWidth: 150 },
//{ prop: 'orgNumber', label: '收款公司编码', minWidth: 130 },
//{ prop: 'openOrgNumber', label: '核算组织编码', minWidth: 130 },
{ prop: 'summary', label: '摘要', minWidth: 160 },
{ prop: 'transactionTime', label: '交易时间', minWidth: 170 },
{ prop: 'claimStatusName', label: '认领状态', minWidth: 110, status: true },
{ prop: 'kingdeeClaimStatusName', label: '公告状态', minWidth: 110 },
{ prop: 'claimedAmount', label: '已认领金额', minWidth: 130, money: true },
{ prop: 'unclaimedAmount', label: '金蝶未认领金额', minWidth: 140, money: true },
{ prop: 'pushStatusName', label: '推送状态', minWidth: 110, status: true },
{ prop: 'pushFailReason', label: '推送失败原因', minWidth: 160 },
{ prop: 'kingdeeBillNo', label: '金蝶收款单号', minWidth: 150 },
{ prop: 'remainingAmount', label: '剩余可认领金额', minWidth: 140, money: true },
{ prop: 'unmatchAmountRec', label: '未匹配金额', minWidth: 140, money: true },
//{ prop: 'matchAmountRec', label: '已匹配收款金额', minWidth: 140, money: true },
//{ prop: 'matchAmountPay', label: '已匹配付款金额', minWidth: 140, money: true },
//{ prop: 'unmatchAmountPay', label: '未匹配付款金额', minWidth: 140, money: true },
//{ prop: 'settleAmount', label: '已结算金额', minWidth: 130, money: true },
//{ prop: 'unsettleAmount', label: '未结算金额', minWidth: 130, money: true },
{ prop: 'kingdeeBillStatusName', label: '金蝶状态', minWidth: 110 },
];
@@ -87,6 +87,7 @@ const COLUMN_EXTRA = {
searchPlaceholder: '请选择',
},
kingdeeLockStatus: { slot: true },
kingdeeAssignStatus: { slot: true },
createTime: { width: 180 },
};
@@ -26,6 +26,7 @@ export const formalSettlementTableColumns = [
{ label: '金蝶单据号', prop: 'kingdeeBillNo', minWidth: 150 },
{ label: '金蝶单据状态', prop: 'kingdeeSyncStatus', minWidth: 135 },
{ label: '金蝶锁状态', prop: 'kingdeeLockStatus', minWidth: 150 },
{ label: '已指定应付', prop: 'kingdeeAssignStatus', minWidth: 120 },
{ label: '附件同步', prop: 'attachmentSyncStatusName', minWidth: 100 },
{ label: '创建人', prop: 'createUserName', minWidth: 110 },
{ label: '创建时间', prop: 'createTime', minWidth: 170 },
+16
View File
@@ -104,6 +104,22 @@ export default [
},
],
},
{
// 兼容旧菜单路径:按 detailNo 解析应付/应收后跳转到拆分后的列表页
path: '/settlement/formal-settlement',
component: Layout,
children: [
{
path: '',
name: '正式结算单兼容跳转',
meta: {
keepAlive: false,
activeMenu: '/settlement/payment-settlement',
},
component: loadView(() => import('@/views/settlement/formal-settlement-redirect.vue')),
},
],
},
{
path: '/settlement/formal-settlement/form',
component: Layout,
+19 -1
View File
@@ -280,6 +280,23 @@ export default {
.filter(Boolean)[0] || '';
},
applyDefaultDeptSearch() {
// 超管默认不限组织,避免预填当前组织后看不到其他组织/无组织数据。
if (this.isAdmin) {
const deptColumn = this.findColumn(this.option.column, 'searchDeptId');
if (deptColumn) {
deptColumn.searchValue = undefined;
}
const nextQuery = { ...this.query };
delete nextQuery.deptId;
delete nextQuery.searchDeptId;
this.query = nextQuery;
this.$nextTick(() => {
if (this.$refs.crud?.searchForm) {
this.$refs.crud.searchForm.searchDeptId = undefined;
}
});
return;
}
const deptId = this.defaultDeptId();
const deptColumn = this.findColumn(this.option.column, 'searchDeptId');
if (!deptId || !deptColumn) return;
@@ -298,7 +315,8 @@ export default {
if (deptColumn) {
deptColumn.dicData = this.deptOptions;
}
// dicData 异步到位后再回填,避免下拉选项为空时清空已选组织,导致请求丢失 deptId。
// 超管不回填默认组织;普通用户在 dicData 到位后再回填,避免下拉清空已选组织。
if (this.isAdmin) return;
const deptId = String(this.query.deptId || this.defaultDeptId() || '');
if (!deptId) return;
if (deptColumn) {
@@ -352,7 +352,12 @@
@opened="loadAddressList"
>
<div class="route-dialog__search common-address-dialog__search">
<el-form :model="addressQuery" label-position="right" label-width="auto">
<el-form
class="dialog-form-label-fixed"
:model="addressQuery"
label-position="right"
label-width="96px"
>
<el-row :gutter="16">
<el-col :span="6">
<el-form-item label="地址名称">
@@ -446,7 +451,12 @@
@opened="loadStationList"
>
<div class="route-dialog__search">
<el-form :model="stationQuery" label-position="right" label-width="auto">
<el-form
class="dialog-form-label-fixed"
:model="stationQuery"
label-position="right"
label-width="96px"
>
<el-row :gutter="16">
<el-col :span="8"><el-form-item label="站点名称"><el-input v-model="stationQuery.name" clearable placeholder="请输入" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="详细地址"><el-input v-model="stationQuery.detailAddress" clearable placeholder="请输入" /></el-form-item></el-col>
@@ -652,6 +662,8 @@ export default {
addressQuery: {},
addressPage: { current: 1, size: 10, total: 0 },
addressRegionPaths: { departure: [], arrival: [] },
// 程序回填省市区时抑制 cascader @change,避免空值回调把已填地址清空
suppressAddressRegionChange: false,
regionOptions: [],
regionLoading: false,
regionRequest: null,
@@ -889,26 +901,63 @@ export default {
findRegionPathByName(options = [], regionName = '', parents = []) {
const target = String(regionName || '').replace(/\s+/g, '');
if (!target) return [];
let suffixMatch = [];
for (const item of options) {
const nextParents = [...parents, item];
const name = nextParents.map(region => region.title || region.name || '').join('');
if (name === target) return nextParents.map(region => region.id);
// 常用地址可能缺省名,允许「市+区」后缀命中完整省市区路径
if (!suffixMatch.length && name.endsWith(target) && target.length >= 2) {
suffixMatch = nextParents.map(region => region.id);
}
const path = this.findRegionPathByName(item.children, target, nextParents);
if (path.length) return path;
}
return [];
return suffixMatch;
},
findRegionPathByCode(options = [], regionCode = '', parents = []) {
const target = String(regionCode || '').trim();
if (!target) return [];
for (const item of options) {
const nextParents = [...parents, item];
if (String(item.id) === target) return nextParents.map(region => region.id);
if (String(item.id) === target || String(item.value || '') === target) {
return nextParents.map(region => region.id);
}
const path = this.findRegionPathByCode(item.children, target, nextParents);
if (path.length) return path;
}
return [];
},
resolveRegionPath(field) {
if (!field) return [];
const codePath = this.findRegionPathByCode(
this.regionOptions,
field.model[field.regionCode]
);
if (codePath.length) return codePath;
const namePath = this.findRegionPathByName(this.regionOptions, field.model[field.name]);
if (namePath.length) return namePath;
const composed = [field.model[field.provinceName], field.model[field.cityName], field.model[field.districtName]]
.filter(Boolean)
.join('');
return this.findRegionPathByName(this.regionOptions, composed);
},
setAddressRegionPath(target, path = []) {
const nextPath = Array.isArray(path) ? path.map(item => String(item)) : [];
const current = this.addressRegionPaths[target] || [];
if (
current.length === nextPath.length &&
current.every((item, index) => String(item) === String(nextPath[index]))
) {
return;
}
this.suppressAddressRegionChange = true;
// Vue3 直接写动态 key,避免整体替换对象导致其它 cascader 重建并触发空 @change
this.addressRegionPaths[target] = nextPath;
this.$nextTick(() => {
this.suppressAddressRegionChange = false;
});
},
regionLabels(path = []) {
let options = this.regionOptions;
const labels = [];
@@ -947,10 +996,22 @@ export default {
this.setAddressRegionNames(field, [provinceMatch?.[0], cityMatch?.[0], districtMatch?.[0]]);
},
handleAddressRegionChange(target, value) {
if (this.suppressAddressRegionChange) return;
const field = this.addressModel(target);
const path = Array.isArray(value) ? value.map(item => String(item)) : [];
this.addressRegionPaths = { ...this.addressRegionPaths, [target]: path };
this.setAddressRegionPath(target, path);
if (!field) return;
// 程序回填/重建过程中 cascader 可能短暂抛出空值,不能据此清空已选常用地址
if (!path.length) {
const hasExisting =
String(field.model[field.name] || '').trim() ||
String(field.model[field.address] || '').trim() ||
String(field.model[field.regionCode] || '').trim();
if (hasExisting) {
this.$nextTick(() => this.syncAddressRegionPath(target));
return;
}
}
const labels = this.regionLabels(path);
field.model[field.name] = labels.join('');
field.model[field.regionCode] = path.at(-1) || '';
@@ -959,19 +1020,29 @@ export default {
},
syncAddressRegionPath(target) {
const field = this.addressModel(target);
if (!field) return;
const codePath = this.findRegionPathByCode(this.regionOptions, field.model[field.regionCode]);
const path = codePath.length
? codePath
: this.findRegionPathByName(this.regionOptions, field.model[field.name]);
this.addressRegionPaths = { ...this.addressRegionPaths, [target]: path };
if (!field || !this.regionOptions.length) return;
const path = this.resolveRegionPath(field);
this.setAddressRegionPath(target, path);
if (path.length) this.setAddressRegionNames(field, this.regionLabels(path));
},
syncAddressRegionPaths() {
if (!this.regionOptions.length) return;
['departure', 'arrival', ...this.form.routes.map((route, index) => `route-${index}`)].forEach(target => {
this.syncAddressRegionPath(target);
});
this.suppressAddressRegionChange = true;
try {
['departure', 'arrival', ...this.form.routes.map((route, index) => `route-${index}`)].forEach(
target => {
const field = this.addressModel(target);
if (!field) return;
const path = this.resolveRegionPath(field);
this.addressRegionPaths[target] = path;
if (path.length) this.setAddressRegionNames(field, this.regionLabels(path));
}
);
} finally {
this.$nextTick(() => {
this.suppressAddressRegionChange = false;
});
}
},
async loadDetail(id) {
const res = await api.getDetail(id);
@@ -1145,13 +1216,15 @@ export default {
};
},
addressTransportType(target) {
if (target.startsWith('route-')) {
return this.form.routes[Number(target.slice(6))]?.transportType || '';
const key = String(target || '');
if (key.startsWith('route-')) {
return String(this.form.routes[Number(key.slice(6))]?.transportType || '');
}
return target === 'arrival' ? this.form.finalTransportType : this.form.routes[0]?.transportType || '';
if (key === 'arrival') return String(this.form.finalTransportType || '');
return String(this.form.routes[0]?.transportType || '');
},
addressTransportMode(target) {
const type = this.addressTransportType(target);
const type = String(this.addressTransportType(target) || '');
if (type.includes('铁路')) return 'rail';
if (type.includes('水路') || type.includes('水运') || type.includes('海运')) return 'water';
if (type.includes('航空') || type.includes('空运')) return 'air';
@@ -1168,25 +1241,11 @@ export default {
// 展示模式由已选数据决定:有站点编码则显示站点名,否则省市区;暂不按运输方式强制站点限制。
return Boolean(siteCode && siteCode !== '/');
},
hasAddressValue(target) {
const field = this.addressModel(target);
if (!field) return false;
return Boolean(
String(field.model[field.address] || '').trim() || String(field.model[field.name] || '').trim()
);
},
tipEmptyAddressDetail(target) {
// 替代原「请先选择运输方式」placeholder:空地址点击时 toast,已有值不再提示。
if (!this.hasAddressValue(target)) {
this.$message.warning('请输入详细地址');
}
},
resolveAddressRegionCode(row = {}) {
return row.regionCode || row.districtCode || row.areaCode || row.adcode || '';
},
openCommonAddress(target) {
if (this.isBasicRouteReadonly) return;
this.tipEmptyAddressDetail(target);
this.addressTarget = target;
// 暂不按运输方式限制站点:统一走常用地址选择。
this.addressQuery = {};
@@ -1233,8 +1292,9 @@ export default {
this.stationPage.current = 1;
this.loadStationList();
},
selectStation(row) {
const target = this.addressModel(this.addressTarget);
async selectStation(row) {
const targetKey = this.addressTarget;
const target = this.addressModel(targetKey);
if (!target) return;
const station = this.normalizeStation(row);
target.model[target.name] = station.stationName || station.regionName;
@@ -1245,7 +1305,9 @@ export default {
target.model[target.siteCode] = station.stationCode;
this.setAddressRegionNamesFromRow(target, station);
this.stationDialogVisible = false;
this.loadRegionOptions().then(() => this.syncAddressRegionPath(this.addressTarget));
await this.loadRegionOptions();
await this.$nextTick();
this.syncAddressRegionPath(targetKey);
},
async loadAddressList() {
this.addressLoading = true;
@@ -1271,8 +1333,9 @@ export default {
this.addressPage.current = 1;
this.loadAddressList();
},
selectAddress(row) {
const target = this.addressModel(this.addressTarget);
async selectAddress(row) {
const targetKey = this.addressTarget;
const target = this.addressModel(targetKey);
if (!target) return;
target.model[target.name] = row.regionName || row.addressName || '';
target.model[target.address] = row.detailAddress || row.address || '';
@@ -1284,11 +1347,13 @@ export default {
target.model[target.siteCode] = '';
this.setAddressRegionNamesFromRow(target, row);
this.addressDialogVisible = false;
this.loadRegionOptions().then(() => this.syncAddressRegionPath(this.addressTarget));
await this.loadRegionOptions();
// nextTick 确保途经点动态 key 已挂载 cascader 后再回填路径
await this.$nextTick();
this.syncAddressRegionPath(targetKey);
},
handleAddressDetailClick(target) {
if (this.isBasicRouteReadonly) return;
this.tipEmptyAddressDetail(target);
this.openAddressMap(target);
},
openAddressMap(target) {
@@ -1803,12 +1868,23 @@ export default {
},
addRoute() {
if (this.isBasicRouteReadonly) return;
this.form.routes.push(this.createRoute());
const index = this.form.routes.length;
this.form.routes.push(this.createRoute(index));
this.addressRegionPaths[`route-${index}`] = [];
},
removeRoute(index) {
if (this.isBasicRouteReadonly) return;
this.form.routes.splice(index, 1);
this.syncAddressRegionPaths();
const nextPaths = { departure: this.addressRegionPaths.departure || [], arrival: this.addressRegionPaths.arrival || [] };
this.form.routes.forEach((route, routeIndex) => {
nextPaths[`route-${routeIndex}`] = this.addressRegionPaths[`route-${routeIndex >= index ? routeIndex + 1 : routeIndex}`] || [];
});
this.suppressAddressRegionChange = true;
this.addressRegionPaths = nextPaths;
this.$nextTick(() => {
this.suppressAddressRegionChange = false;
this.syncAddressRegionPaths();
});
},
goodsKey(row = {}) {
return `${row.cargoName || ''}\u0000${row.cargoType || ''}`;
@@ -5116,11 +5116,16 @@ export default {
);
},
canCopy(row) {
if (!row) return false;
// 归属本账号可见范围的运单允许复制。跨组织但项目归属本账号的记录会被标成只读,
// 只读仅拦截编辑/删除,不拦截复制。具备查看、新增、编辑或复制权限即可使用。
return (
this.hasAction('copy') &&
this.hasPermission(`${this.config.permission}_copy`) &&
!this.readonlyScope &&
!row.readonly
(this.hasPermission(`${this.config.permission}_copy`) ||
this.hasPermission(`${this.config.permission}_add`) ||
this.hasPermission(`${this.config.permission}_edit`) ||
this.hasPermission(`${this.config.permission}_view`))
);
},
canEnable(row) {
+43 -9
View File
@@ -33,6 +33,7 @@
批量导出
</el-button>
<el-button
v-if="hcsImmediateEnabled"
type="warning"
plain
:loading="hcsRepushingId === 'batch'"
@@ -92,15 +93,24 @@
<span v-else>-</span>
</template>
<!-- 客商风险:等级与金蝶黑名单组合展示(推送链路按金蝶命中口径落库),复用公共展示方法 -->
<!-- 客商风险:等级与金蝶黑名单组合展示(推送链路按金蝶命中口径落库),复用公共展示方法;非无风险标签悬浮见明细 -->
<template #customerRisk="{ row }">
<el-tag
<el-tooltip
v-if="contractRiskTagName(row)"
:type="isContractRisky(row) ? 'danger' : row.hcsRiskLevel === '1' ? 'warning' : 'success'"
class="status-text"
placement="top"
effect="dark"
popper-class="customer-risk-popper"
>
{{ contractRiskTagName(row) }}
</el-tag>
<template #content>
<div>{{ row.hcsRiskDetail || contractRiskTagName(row) }}</div>
</template>
<el-tag
:type="isContractRisky(row) ? 'danger' : row.hcsRiskLevel === '1' ? 'warning' : 'success'"
class="status-text"
>
{{ contractRiskTagName(row) }}
</el-tag>
</el-tooltip>
<span v-else>-</span>
</template>
@@ -124,7 +134,11 @@
>
{{ operation.label }}
</el-link>
<el-link v-if="canHcsRepush(row)" type="warning" @click="handleHcsRepush(row)">
<el-link
v-if="hcsImmediateEnabled && canHcsRepush(row)"
type="warning"
@click="handleHcsRepush(row)"
>
{{ hcsRepushingId === row.id ? '推送中…' : '推送合规' }}
</el-link>
<el-link v-if="canDelete(row)" type="danger" @click="removeRow(row)">删除</el-link>
@@ -1102,7 +1116,12 @@ import 'nprogress/nprogress.css';
import { exportBlob } from '@/api/common';
import * as api from '@/api/business/contract-manage';
import { createFromContract } from '@/api/business/contract-change';
import { repush as hcsRepush, repushBatch as hcsRepushBatch, getRisk } from '@/api/business/hcs';
import {
getRisk,
loadHcsImmediateEnabled,
repush as hcsRepush,
repushBatch as hcsRepushBatch,
} from '@/api/business/hcs';
import { isContractBlack, isRiskyTag, resolveRiskTagName } from '@/utils/risk-display';
import { Position } from '@element-plus/icons-vue';
import { getMkPublicDetail } from '@/api/mk-process';
@@ -1440,8 +1459,9 @@ export default {
billCycleTypeOptions: ['固定截单日', '自然月', '自定义多周期'],
detailLoading: false,
detailRow: {},
// 合规(HCS)推送/对账状态
// 合规(HCS)推送/对账状态;受 thirdParty.hcs.push.immediate-enabled 控制显隐
hcsRepushingId: null,
hcsImmediateEnabled: false,
detailContractFileRows: [],
detailAttachmentRows: [],
detailBillingPlanRows: [],
@@ -1712,6 +1732,13 @@ export default {
.catch(() => {
this.mkEnabled = false;
});
loadHcsImmediateEnabled()
.then(enabled => {
this.hcsImmediateEnabled = !!enabled;
})
.catch(() => {
this.hcsImmediateEnabled = false;
});
}
if (this.isFormPage) this.initFormPage();
if (this.isDetailPage) this.initDetailPage();
@@ -1781,6 +1808,7 @@ export default {
},
canHcsRepush(row) {
return (
this.hcsImmediateEnabled &&
['approved', 'change_approved'].includes(row.approvalStatus) &&
row.hcsSyncStatus !== 'synced'
);
@@ -3820,4 +3848,10 @@ export default {
white-space: normal;
line-height: 1.2;
}
/* 风险客商悬浮提示(同客商列表页):popper 挂在 body 下,约束宽度 */
.customer-risk-popper {
max-width: 360px;
line-height: 1.6;
}
</style>
+2 -2
View File
@@ -4023,7 +4023,7 @@ export default {
exists.add(key);
}
});
// 承运商类型:新增后须仍存在共同的承运商合同乙方(按客户合同甲方匹配后取交集)
// 承运商类型:新增后须仍存在共同的承运商合同乙方(按客户合同乙方=承运商合同甲方匹配后取交集)
if (
String(this.dialogForm.carrierType || '').trim() === '承运商' &&
nextRows.length > this.selectedWaybillRows.length
@@ -4744,7 +4744,7 @@ export default {
}
this.taskCarrierLoading = true;
try {
// 配载单只有一个项目;传入子运单后按客户合同甲方匹配并取承运商合同乙方交集
// 配载单只有一个项目;传入子运单后按客户合同乙方=承运商合同甲方匹配并取承运商合同乙方交集
const projectId = this.selectedWaybillRows.map(row => row.projectId).find(Boolean);
const waybillIds = this.selectedWaybillRows.map(row => row.id).filter(Boolean);
const response = await loadingApi.getCarrierContracts(
+107 -30
View File
@@ -55,7 +55,7 @@
<el-button
type="warning"
plain
v-if="!readonlyScope"
v-if="!readonlyScope && hcsImmediateEnabled"
:loading="hcsRepushingId === 'batch'"
@click="handleHcsBatchRepush"
>
@@ -154,7 +154,7 @@
{{ operation.label }}
</el-link>
<el-link
v-if="canHcsRepush(row)"
v-if="hcsImmediateEnabled && canHcsRepush(row)"
type="warning"
@click="handleHcsRepush(row)"
>
@@ -249,35 +249,57 @@
/>
</el-form-item>
<el-form-item label="业务部门" prop="businessDeptId">
<el-cascader
v-model="businessDeptCascaderValue"
:options="businessDeptTreeOptions"
:props="deptCascaderProps"
placeholder="请选择"
filterable
clearable
:disabled="dialogReadonly"
/>
<el-tooltip
placement="top"
:disabled="!shouldShowBusinessDeptTooltip"
:show-after="200"
>
<template #content>
<div class="project-apply-form__field-tooltip">{{ businessDeptTooltipText }}</div>
</template>
<span class="project-apply-form__field-tooltip-trigger">
<el-cascader
v-model="businessDeptCascaderValue"
:options="businessDeptTreeOptions"
:props="deptCascaderProps"
placeholder="请选择"
filterable
clearable
:disabled="dialogReadonly"
/>
</span>
</el-tooltip>
</el-form-item>
<el-form-item label="平台公司" prop="undertakeDeptId">
<el-select
v-model="form.undertakeDeptId"
placeholder="请选择"
multiple
collapse-tags
collapse-tags-tooltip
clearable
filterable
:disabled="dialogReadonly"
@change="handleUndertakeDeptChange"
<el-tooltip
placement="top"
:disabled="!shouldShowUndertakeDeptTooltip"
:show-after="200"
>
<el-option
v-for="item in platformCompanyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<template #content>
<div class="project-apply-form__field-tooltip">{{ undertakeDeptTooltipText }}</div>
</template>
<span class="project-apply-form__field-tooltip-trigger">
<el-select
v-model="form.undertakeDeptId"
placeholder="请选择"
multiple
collapse-tags
collapse-tags-tooltip
clearable
filterable
:disabled="dialogReadonly"
@change="handleUndertakeDeptChange"
>
<el-option
v-for="item in platformCompanyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</span>
</el-tooltip>
</el-form-item>
<el-form-item label="项目由来" prop="projectSource">
<el-select
@@ -962,7 +984,12 @@
import { exportBlob } from '@/api/common';
import { defineAsyncComponent } from 'vue';
import * as api from '@/api/business/project-apply';
import { repush as hcsRepush, repushBatch as hcsRepushBatch } from '@/api/business/hcs';
import {
getRisk,
loadHcsImmediateEnabled,
repush as hcsRepush,
repushBatch as hcsRepushBatch,
} from '@/api/business/hcs';
import { Position } from '@element-plus/icons-vue';
import { getMkPublicDetail } from '@/api/mk-process';
import {
@@ -1174,8 +1201,9 @@ export default {
{ label: '高风险', value: 'high', statKey: 'high' },
{ label: '中风险', value: 'medium', statKey: 'medium' },
],
// 合规(HCS)推送状态
// 合规(HCS)推送状态;受 thirdParty.hcs.push.immediate-enabled 控制显隐
hcsRepushingId: null,
hcsImmediateEnabled: false,
page: {
pageSize: 10,
pageSizes: [10, 20, 50, 100],
@@ -1402,6 +1430,33 @@ export default {
this.handleBusinessDeptChange(this.form.businessDeptId);
},
},
// 查看态禁用控件无 hover,用完整路径/名称做 tooltip
businessDeptTooltipText() {
const pathIds = this.businessDeptCascaderValue || [];
if (pathIds.length && this.businessDeptTreeOptions?.length) {
const labels = [];
let nodes = this.businessDeptTreeOptions;
for (const id of pathIds) {
const node = (nodes || []).find(item => String(item.value) === String(id));
if (!node) break;
labels.push(node.label);
nodes = node.children;
}
if (labels.length) return labels.join(' / ');
}
return String(this.form.businessDeptName || '').trim();
},
undertakeDeptTooltipText() {
const name = String(this.form.undertakeDeptName || '').trim();
if (name) return name;
return this.resolveUndertakeDeptNames(this.parseUndertakeDeptIds(this.form.undertakeDeptId));
},
shouldShowBusinessDeptTooltip() {
return this.dialogReadonly && !!this.businessDeptTooltipText;
},
shouldShowUndertakeDeptTooltip() {
return this.dialogReadonly && !!this.undertakeDeptTooltipText;
},
permissionList() {
return {
addBtn: this.canCreate,
@@ -1507,6 +1562,13 @@ export default {
.catch(() => {
this.mkEnabled = false;
});
loadHcsImmediateEnabled()
.then(enabled => {
this.hcsImmediateEnabled = !!enabled;
})
.catch(() => {
this.hcsImmediateEnabled = false;
});
this.loadDeptOptions();
this.loadCargoTypeOptions();
this.loadTransportTypeOptions();
@@ -1741,6 +1803,7 @@ export default {
},
canHcsRepush(row) {
return (
this.hcsImmediateEnabled &&
!this.readonlyScope &&
['approved', 'change_approved'].includes(row.approvalStatus) &&
row.hcsSyncStatus !== 'synced'
@@ -3272,6 +3335,20 @@ export default {
width: 100%;
}
&__field-tooltip-trigger {
display: block;
width: 100%;
}
&__field-tooltip {
max-width: 480px;
max-height: 240px;
overflow: auto;
white-space: pre-wrap;
word-break: break-word;
line-height: 1.5;
}
:deep(.el-form-item) {
//margin-bottom: 24px;
}
+1 -1
View File
@@ -6,7 +6,7 @@
:config="config"
:crud-option="option"
:detail-id="detailId"
:menu-width="250"
:menu-width="320"
standalone-form-page
/>
</template>
+124 -233
View File
@@ -53,15 +53,6 @@
</el-tooltip>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="税率">
<el-input
:model-value="formatRate(form.taxRate)"
disabled
placeholder="从金蝶票据池带出"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="开票金额">
<el-input
@@ -109,34 +100,50 @@
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="关联结算单" prop="settlementIds">
<el-tooltip
:content="settlementLabel"
:disabled="!settlementLabel"
placement="top"
:show-after="200"
>
<div class="receipt-form-page__overflow-trigger">
<el-input
:model-value="settlementLabel"
readonly
placeholder="请选择应付正式结算单"
>
<template v-if="!readonly" #append>
<el-tooltip content="选择付款结算单" placement="top">
<el-button :icon="Search" @click="openSettlementDialog" />
</el-tooltip>
</template>
</el-input>
</div>
</el-tooltip>
<el-form-item label="结算组织" prop="deptId">
<el-cascader
v-if="!readonly"
:model-value="deptCascaderValue"
:options="deptTreeOptions"
:props="deptCascaderProps"
placeholder="请选择结算组织(合同承办部门)"
filterable
clearable
@change="handleDeptChange"
/>
<el-input v-else :model-value="form.deptName" disabled />
</el-form-item>
</el-col>
</el-row>
</section-card>
<section-card title="发票明细">
<el-table :data="invoiceItems" border empty-text="选择金蝶票据池发票后带出发票明细行">
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column prop="goodsName" label="货物名称" min-width="160" show-overflow-tooltip />
<el-table-column prop="specModel" label="规格型号" min-width="120" show-overflow-tooltip />
<el-table-column prop="unit" label="单位" min-width="80" align="center" />
<el-table-column label="数量" min-width="100" align="right">
<template #default="{ row }">{{ formatQuantity(row.num) }}</template>
</el-table-column>
<el-table-column label="单价" min-width="110" align="right">
<template #default="{ row }">{{ formatMoney(row.unitPrice) }}</template>
</el-table-column>
<el-table-column label="金额" min-width="120" align="right">
<template #default="{ row }">{{ formatMoney(row.detailAmount) }}</template>
</el-table-column>
<el-table-column label="税率" min-width="90" align="center">
<template #default="{ row }">{{ formatRateText(row.taxRate) }}</template>
</el-table-column>
<el-table-column label="税额" min-width="110" align="right">
<template #default="{ row }">{{ formatMoney(row.taxAmount) }}</template>
</el-table-column>
<el-table-column prop="goodsCode" label="商品编码" min-width="140" show-overflow-tooltip />
</el-table>
</section-card>
<section-card title="结算信息">
<el-table :data="form.settlements" border>
<el-table :data="form.settlements" border empty-text="暂无认领数据(认领关系由付款结算单侧维护)">
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column
label="结算单号"
@@ -156,17 +163,8 @@
<el-table-column label="已收票金额" min-width="150" align="right">
<template #default="{ row }">{{ formatMoney(row.receivedInvoiceAmount) }}</template>
</el-table-column>
<el-table-column label="分摊发票金额" min-width="180">
<template #default="{ row }">
<el-input-number
v-model="row.allocatedInvoiceAmount"
:disabled="readonly"
:min="0"
:max="remainingAmount(row)"
:precision="2"
:controls="false"
/>
</template>
<el-table-column label="分摊发票金额" min-width="150" align="right">
<template #default="{ row }">{{ formatMoney(row.allocatedInvoiceAmount) }}</template>
</el-table-column>
</el-table>
</section-card>
@@ -216,71 +214,6 @@
:query-api="apiQueryInvoicePool"
@confirm="handleInvoicePicked"
/>
<el-dialog
v-model="settlementDialog.visible"
title="选择应付正式结算单"
width="86%"
append-to-body
>
<div class="receipt-form-page__dialog-search">
<el-input
v-model="settlementDialog.keyword"
clearable
placeholder="结算单号、项目或合同"
@keyup.enter="loadSettlementCandidates"
/>
<el-button type="primary" @click="loadSettlementCandidates">查询</el-button>
</div>
<el-table
ref="settlementTable"
v-loading="settlementDialog.loading"
:data="settlementDialog.rows"
row-key="id"
border
@selection-change="settlementDialog.selection = $event"
>
<el-table-column type="selection" width="52" align="center" reserve-selection />
<el-table-column
prop="formalSettlementNo"
label="结算单号"
min-width="170"
show-overflow-tooltip
/>
<el-table-column
prop="projectName"
label="所属项目"
min-width="150"
show-overflow-tooltip
/>
<el-table-column prop="deptName" label="所属组织" min-width="150" show-overflow-tooltip />
<el-table-column
prop="contractNo"
label="合同编号"
min-width="150"
show-overflow-tooltip
/>
<el-table-column prop="payerName" label="付款方" min-width="150" show-overflow-tooltip />
<el-table-column prop="payeeName" label="收款方" min-width="150" show-overflow-tooltip />
<el-table-column label="结算总金额" min-width="140" align="right">
<template #default="{ row }">{{ formatMoney(row.settlementAmount) }}</template>
</el-table-column>
<el-table-column label="已收票金额" min-width="140" align="right">
<template #default="{ row }">{{ formatMoney(row.receivedInvoiceAmount) }}</template>
</el-table-column>
<el-table-column label="操作" width="100" fixed="right" align="center">
<template #default="{ row }">
<el-link type="primary" @click.stop="selectSettlement(row)">选择</el-link>
</template>
</el-table-column>
</el-table>
<template #footer>
<el-button @click="settlementDialog.visible = false">取消</el-button>
<el-button type="primary" @click="confirmSettlements">
确定{{ settlementDialog.selection.length ? `(${settlementDialog.selection.length})` : '' }}
</el-button>
</template>
</el-dialog>
</basic-container>
</template>
@@ -288,11 +221,15 @@
import { Search } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import * as api from '@/api/payment/invoiceReceipt';
import { getDeptTree } from '@/api/system/dept';
import KingdeeInvoicePoolDialog from '@/components/kingdee-invoice-pool-dialog/main.vue';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
import { getMkPublicDetail } from '@/api/mk-process';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
// 提交成功后带标记返回,列表页(keep-alive)据此重新拉取
const invoiceReceiptListRefreshKey = 'invoice-receipt-list-refresh';
const emptyForm = () => ({
id: null,
invoiceNo: '',
@@ -310,6 +247,7 @@ const emptyForm = () => ({
customerEmails: [],
departmentEmails: [],
projectName: '',
deptId: null,
deptName: '',
payerName: '',
payeeName: '',
@@ -330,26 +268,24 @@ export default {
form: emptyForm(),
customerEmailOptions: [],
departmentEmailOptions: [],
invoiceItems: [],
deptTreeOptions: [],
deptMap: new Map(),
// 结算组织=合同承办部门:部门树级联单选(取末级 id),后端沿部门树上溯平台公司取金蝶组织
deptCascaderProps: { checkStrictly: true, emitPath: true },
invoiceDialog: {
visible: false,
},
settlementDialog: {
visible: false,
loading: false,
keyword: '',
rows: [],
selection: [],
},
rules: {
invoiceNo: [{ required: true, validator: this.validateInvoice, trigger: 'change' }],
settlementIds: [{ required: true, validator: this.validateSettlements, trigger: 'change' }],
deptId: [{ required: true, validator: this.validateDept, trigger: 'change' }],
customerEmails: [{ validator: this.validateEmails, trigger: 'change' }],
departmentEmails: [{ validator: this.validateEmails, trigger: 'change' }],
},
};
},
computed: {
...mapGetters(['permission']),
...mapGetters(['permission', 'userInfo']),
recordId() {
return this.$route.query.id || '';
},
@@ -363,14 +299,11 @@ export default {
const code = this.recordId ? 'invoice_receipt_edit' : 'invoice_receipt_add';
return this.hasPermission(code);
},
settlementLabel() {
return this.form.settlements
.map(item => item.formalSettlementNo)
.filter(Boolean)
.join(',');
},
settlementIds() {
return this.form.settlements.map(item => item.formalSettlementId || item.settlementId);
deptCascaderValue() {
if (this.form.deptId === null || this.form.deptId === undefined || this.form.deptId === '') {
return [];
}
return [String(this.form.deptId)];
},
},
created() {
@@ -406,6 +339,7 @@ export default {
.filter(Boolean);
},
async initialize() {
this.loadDepartments();
if (this.isPublicViewPage) {
await this.loadPublicDetail();
return;
@@ -419,14 +353,13 @@ export default {
}
const data = this.unwrapData(await api.getDetail(this.recordId));
this.applyDetail(data);
if (this.settlementIds.length) await this.loadReferenceInformation();
},
async loadPublicDetail() {
if (!this.recordId) return;
const data = this.unwrapData(await getMkPublicDetail('invoice-receipt', this.recordId));
this.applyDetail(data);
},
applyDetail(data) {
async applyDetail(data) {
this.form = {
...emptyForm(),
...data,
@@ -438,6 +371,7 @@ export default {
})),
attachments: this.parse(data.attachmentsJson),
};
await this.loadInvoiceDetail();
},
validateInvoice(rule, value, callback) {
if (!value) {
@@ -446,9 +380,9 @@ export default {
}
callback();
},
validateSettlements(rule, value, callback) {
if (!this.form.settlements.length) {
callback(new Error('请选择应付正式结算单'));
validateDept(rule, value, callback) {
if (this.form.deptId === null || this.form.deptId === undefined || this.form.deptId === '') {
callback(new Error('请选择结算组织'));
return;
}
callback();
@@ -469,7 +403,7 @@ export default {
openInvoiceDialog() {
this.invoiceDialog.visible = true;
},
confirmInvoice(row, options = {}) {
async confirmInvoice(row, options = {}) {
const selected = row;
if (!selected) {
this.$message.warning('请选择一张金蝶票据池发票');
@@ -504,92 +438,63 @@ export default {
...new Set([...this.departmentEmailOptions, ...this.form.departmentEmails]),
];
this.invoiceDialog.visible = false;
await this.loadInvoiceDetail();
this.$refs.formRef?.validateField('invoiceNo').catch(() => {});
return true;
},
async openSettlementDialog() {
this.settlementDialog.visible = true;
this.settlementDialog.selection = [];
await this.loadSettlementCandidates();
await this.$nextTick();
const selectedIds = new Set(this.settlementIds.map(id => String(id)));
this.settlementDialog.rows.forEach(row => {
if (selectedIds.has(String(row.id))) {
this.$refs.settlementTable?.toggleRowSelection(row, true);
}
});
},
async loadSettlementCandidates() {
this.settlementDialog.loading = true;
// 发票明细区块:按票号带出金蝶发票明细行(A801-1 镜像,推送 A303-2 分录同源)
async loadInvoiceDetail() {
if (!this.form.invoiceNo) {
this.invoiceItems = [];
return;
}
try {
this.settlementDialog.rows =
this.unwrapData(
await api.getSettlementCandidates(this.settlementDialog.keyword, this.form.id)
) || [];
} finally {
this.settlementDialog.loading = false;
this.invoiceItems = this.unwrapData(await api.invoiceDetailQuery(this.form.invoiceNo)) || [];
} catch {
this.invoiceItems = [];
}
},
async confirmSettlements() {
const rows = this.settlementDialog.selection;
if (!rows.length) {
this.$message.warning('请至少选择一张应付正式结算单');
return;
}
const first = rows[0];
const incompatible = rows.some(
item =>
String(item.projectId) !== String(first.projectId) ||
String(item.deptId) !== String(first.deptId) ||
item.payerName !== first.payerName ||
item.payeeName !== first.payeeName
);
if (incompatible) {
this.$message.warning('关联结算单必须属于同一项目、组织及收付款方');
return;
}
const previousAmount = new Map(
this.form.settlements.map(item => [
String(item.formalSettlementId || item.settlementId),
item.allocatedInvoiceAmount,
])
);
this.form.settlements = rows.map(item => ({
...item,
formalSettlementId: item.id,
settlementId: item.id,
allocatedInvoiceAmount: previousAmount.has(String(item.id))
? Number(previousAmount.get(String(item.id)) || 0)
: rows.length === 1
? Number(item.settlementAmount || 0)
: 0,
loadDepartments() {
// 结算组织取租户部门树(过滤外部组织),选中取末级部门 id
getDeptTree(this.userInfo?.tenantId)
.then(response => {
const rows = this.excludeExternalOrganization(this.unwrapData(response) || []);
this.deptTreeOptions = this.toDeptCascaderOptions(rows);
this.deptMap = new Map();
this.flattenDepartments(rows).forEach(item =>
this.deptMap.set(String(item.id), item.title || item.deptName || item.name)
);
})
.catch(() => {});
},
excludeExternalOrganization(list = []) {
return list.reduce((result, item) => {
const deptName = item.title || item.deptName || item.name;
if (String(deptName || '').trim() === '外部组织') return result;
result.push({
...item,
children: item.children?.length
? this.excludeExternalOrganization(item.children)
: item.children,
});
return result;
}, []);
},
toDeptCascaderOptions(list = []) {
return list.map(item => ({
label: item.title || item.deptName || item.name,
value: item.id == null || item.id === '' ? '' : String(item.id),
children: item.children?.length ? this.toDeptCascaderOptions(item.children) : undefined,
}));
this.form.projectName = first.projectName;
this.form.deptName = first.deptName;
this.form.payerName = first.payerName;
this.form.payeeName = first.payeeName;
this.settlementDialog.visible = false;
await this.loadReferenceInformation();
this.$refs.formRef?.validateField('settlementIds').catch(() => {});
},
selectSettlement(row) {
this.$refs.settlementTable?.toggleRowSelection(row);
flattenDepartments(rows) {
return (rows || []).flatMap(item => [item, ...this.flattenDepartments(item.children || [])]);
},
async loadReferenceInformation() {
const data = this.unwrapData(await api.getReferenceInformation(this.settlementIds.join(',')));
this.customerEmailOptions = data.customerEmails || [];
this.departmentEmailOptions = [
...new Set([...this.departmentEmailOptions, ...(data.departmentEmails || [])]),
];
if (!this.form.customerEmails.length && this.customerEmailOptions.length) {
this.form.customerEmails = [this.customerEmailOptions[0]];
}
},
remainingAmount(row) {
return Math.max(
Number(row.settlementAmount || 0) - Number(row.receivedInvoiceAmount || 0),
0
);
handleDeptChange(value) {
const deptId = Array.isArray(value) ? value.at(-1) : value;
this.form.deptId = deptId || null;
this.form.deptName = deptId ? this.deptMap.get(String(deptId)) || '' : '';
this.$refs.formRef?.validateField('deptId').catch(() => {});
},
openSettlementDetail(row) {
const id = row.formalSettlementId || row.settlementId || row.id;
@@ -602,15 +507,6 @@ export default {
validateBusiness() {
const invoiceAmount = Number(this.form.invoiceAmount || 0);
if (invoiceAmount <= 0) throw new Error('开票金额必须大于0');
for (const row of this.form.settlements) {
const allocated = Number(row.allocatedInvoiceAmount);
if (!Number.isFinite(allocated) || allocated < 0) {
throw new Error('分摊发票金额必须大于或等于0');
}
if (allocated - this.remainingAmount(row) > 0.001) {
throw new Error(`结算单${row.formalSettlementNo}的累计收票金额不能超过结算总金额`);
}
}
},
payload() {
return {
@@ -631,10 +527,9 @@ export default {
departmentEmails: this.form.departmentEmails.join(';'),
attachmentsJson: JSON.stringify(this.form.attachments || []),
remark: this.form.remark,
settlements: this.form.settlements.map(item => ({
settlementId: item.formalSettlementId || item.settlementId,
allocatedInvoiceAmount: item.allocatedInvoiceAmount,
})),
// 结算组织=页面手选部门(先票后款不挂结算单,认领关系由结算单侧维护,不再传 settlements)
deptId: this.form.deptId,
deptName: this.form.deptName,
};
},
async saveDraft() {
@@ -663,6 +558,7 @@ export default {
mkEnabled: await loadMkEnabled().catch(() => false),
});
this.$message.success('提交成功');
sessionStorage.setItem(invoiceReceiptListRefreshKey, '1');
this.goBack();
},
async syncReferenceData() {
@@ -678,8 +574,7 @@ export default {
this.$message.warning('金蝶票池未查询到该发票');
return;
}
if (!this.confirmInvoice(invoice, { preserveUserInput: true })) return;
if (this.settlementIds.length) await this.loadReferenceInformation();
if (!(await this.confirmInvoice(invoice, { preserveUserInput: true }))) return;
this.$message.success('同步成功');
},
goBack() {
@@ -688,8 +583,13 @@ export default {
formatMoney(value) {
return Number(value || 0).toFixed(2);
},
formatRate(value) {
return value === null || value === undefined || value === '' ? '' : `${Number(value)}%`;
formatQuantity(value) {
const number = Number(value);
return Number.isFinite(number) ? String(number) : '-';
},
formatRateText(value) {
if (value === null || value === undefined || value === '') return '-';
return `${value}%`;
},
},
};
@@ -738,15 +638,6 @@ export default {
:global(.avue-layout--horizontal .receipt-form-page__actions) {
left: 0;
}
.receipt-form-page__dialog-search {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-bottom: 12px;
}
.receipt-form-page__dialog-search .el-input {
width: 360px;
}
.receipt-form-page :deep(.el-table) {
--el-table-border-color: #eff1f7;
}
+25
View File
@@ -223,6 +223,10 @@ import organizationSearch from '@/mixins/organization-search';
import { getMKApprovalUrl } from '@/api/system/business-process';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
// 表单提交成功后带标记返回,keep-alive 缓存的列表页据此重新拉取
const invoiceReceiptListRefreshKey = 'invoice-receipt-list-refresh';
const invoiceReceiptListRoute = '/payment/invoice-receipt';
const emptyQuery = () => ({
invoiceNo: '',
invoiceDate: '',
@@ -281,7 +285,28 @@ export default {
this.mkEnabled = false;
});
},
activated() {
this.consumeListRefreshFlag();
},
watch: {
$route(to, from) {
if (
to.path === invoiceReceiptListRoute &&
from?.path?.includes('/invoice-receipt/form')
) {
this.consumeListRefreshFlag();
}
},
},
methods: {
/** 表单提交成功后带标记返回时,重新拉取列表 */
consumeListRefreshFlag() {
if (!sessionStorage.getItem(invoiceReceiptListRefreshKey)) {
return;
}
sessionStorage.removeItem(invoiceReceiptListRefreshKey);
this.loadTable();
},
hasPermission(code) {
return this.permission?.[code] !== false;
},
+24
View File
@@ -80,6 +80,23 @@
</el-tooltip>
</template>
<template #kingdeeAssignStatus="{ row }">
<el-tooltip
:disabled="row.kingdeeAssignStatus !== 'failed'"
:content="row.kingdeeAssignFailReason || '收票单指定应付失败,可在对应付款结算单查看页重新推送'"
placement="top"
>
<el-tag
v-if="row.kingdeeAssignStatus"
:type="{ assigned: 'success', failed: 'danger' }[row.kingdeeAssignStatus] || 'info'"
size="small"
>
{{ { assigned: '已指定应付', failed: '指定失败' }[row.kingdeeAssignStatus] || '未指定' }}
</el-tag>
<span v-else>-</span>
</el-tooltip>
</template>
<template #menu="{ row }">
<el-link
v-if="hasPermission('payment_application_view')"
@@ -409,6 +426,12 @@ export default {
return;
}
this.syncLoading = true;
// 批量同步金蝶逐单串行推送耗时较长,加遮罩防重复操作(照审批通过遮罩)
const loading = this.$loading({
lock: true,
text: '正在批量同步金蝶,请稍候…',
background: 'rgba(255, 255, 255, 0.6)',
});
try {
const data = this.unwrapData(await api.syncKingdeeBatch(rows.map(row => row.id))) || [];
const failedList = data.filter(item => String(item).includes('同步失败'));
@@ -425,6 +448,7 @@ export default {
this.warnAttachmentNotUploaded(rows.map(row => row.id));
} finally {
this.syncLoading = false;
loading.close();
}
},
// 附件联带同步提示:同步成功但附件未全部上传时追加 warning,重推即自动补传
@@ -4,7 +4,7 @@
<section-card title="收款信息">
<el-row :gutter="32">
<el-col :span="6">
<el-form-item label="认领通知单">
<el-form-item label="收款单号">
<el-input v-model="form.receiptNoticeNo" disabled />
</el-form-item>
</el-col>
+4 -34
View File
@@ -3,7 +3,7 @@
<section-card v-if="searchVisible" class="claim-record-page__search">
<el-form :model="query" label-position="right" label-width="88px" @submit.prevent>
<div class="claim-record-page__search-grid">
<el-form-item label="认领通知单">
<el-form-item label="收款单号">
<el-input v-model="query.receiptNoticeNo" clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="对方户名">
@@ -56,16 +56,6 @@
<el-form-item v-show="searchExpanded" label="所属组织">
<el-input v-model="query.claimerDeptName" clearable placeholder="请输入" />
</el-form-item>
<el-form-item v-show="searchExpanded" label="推送状态">
<el-select v-model="query.pushStatus" clearable placeholder="请选择">
<el-option
v-for="item in pushStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-show="searchExpanded" label="金蝶单据状态">
<el-select v-model="query.kingdeeBillStatus" clearable placeholder="请选择">
<el-option
@@ -93,9 +83,6 @@
<section class="claim-record-page__table-panel">
<div class="claim-record-page__toolbar">
<el-button :loading="refreshing" @click="handleRefreshKingdeeResult">
查询金蝶认领结果
</el-button>
<table-header-tools
:columns="columns"
v-model:visible-column-keys="visibleColumnKeys"
@@ -131,11 +118,7 @@
<el-table-column label="操作" width="120" fixed="right" align="center">
<template #default="{ row }">
<el-link
v-if="
hasPermission('receipt_claim_record_void') &&
row.claimStatus === 'claimed' &&
row.pushStatus !== 'pushed'
"
v-if="hasPermission('receipt_claim_record_void') && row.claimStatus === 'claimed'"
type="danger"
@click="handleVoid(row)"
>作废</el-link
@@ -181,7 +164,6 @@ const emptyQuery = () => ({
claimDateRange: [],
claimerDeptName: '',
kingdeeBillStatus: '',
pushStatus: '',
});
export default {
@@ -195,17 +177,15 @@ export default {
searchExpanded: false,
// 与 avue-crud 一致:搜索项数量超过 searchIndex(默认一行 4 项)时才显示“展开/收起”
searchIndex: 4,
searchItemCount: 11,
searchItemCount: 10,
searchVisible: true,
tableSize: 'default',
claimStatusOptions: api.claimStatusOptions,
kingdeeBillStatusOptions: api.kingdeeBillStatusOptions,
pushStatusOptions: api.pushStatusOptions,
columns: receiptClaimRecordTableColumns,
visibleColumnKeys: receiptClaimRecordTableColumns.map(column => column.prop),
rows: [],
loading: false,
refreshing: false,
page: { current: 1, size: 10, total: 0 },
};
},
@@ -272,7 +252,7 @@ export default {
},
async handleVoid(row) {
await this.$confirm(
'作废后将回退本次认领金额,是否继续?',
'作废后将回退本次认领金额,并同步作废合同合规系统认领记录,是否继续?',
'作废认领记录',
{ type: 'warning', confirmButtonText: '确定', cancelButtonText: '取消' }
);
@@ -280,16 +260,6 @@ export default {
this.$message.success('作废成功');
await this.loadTable();
},
async handleRefreshKingdeeResult() {
this.refreshing = true;
try {
const updated = Number(this.unwrapData(await api.queryKingdeeResult()) || 0);
this.$message.success(`金蝶认领结果回写完成,更新${updated}条`);
await this.loadTable();
} finally {
this.refreshing = false;
}
},
hasPermission(code) {
return this.isAdmin || this.validData(this.permission?.[code], false);
},
+8 -14
View File
@@ -7,7 +7,7 @@
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column
prop="receiptNoticeNo"
label="认领通知单"
label="收款单号"
min-width="160"
show-overflow-tooltip
/>
@@ -43,8 +43,8 @@
<el-table-column label="已认领金额" min-width="130" align="right">
<template #default="{ row }">{{ formatMoney(row.claimedAmount) }}</template>
</el-table-column>
<el-table-column label="金蝶未认领金额" min-width="140" align="right">
<template #default="{ row }">{{ formatMoney(row.unclaimedAmount) }}</template>
<el-table-column label="未匹配金额" min-width="140" align="right">
<template #default="{ row }">{{ formatMoney(row.unmatchAmountRec) }}</template>
</el-table-column>
<el-table-column label="剩余可认领金额" min-width="140" align="right">
<template #default="{ row }">{{ formatMoney(flowRemaining(row)) }}</template>
@@ -96,7 +96,7 @@
formatMoney(row.settlement.claimedReceiptAmount)
}}</template>
</el-table-column>
<el-table-column label="认领通知单" min-width="180">
<el-table-column label="收款单号" min-width="180">
<template #default="{ row }">{{ row.flow.receiptNoticeNo || '-' }}</template>
</el-table-column>
<el-table-column label="收款金额" min-width="140" align="right">
@@ -302,10 +302,8 @@ export default {
);
},
flowRemaining(flow) {
const unclaimed = Number(flow.unclaimedAmount || 0);
const receipt = Number(flow.receiptAmount || 0);
const ceiling = unclaimed > 0 && receipt > 0 ? Math.min(unclaimed, receipt) : unclaimed || receipt;
return Math.max(ceiling - Number(flow.claimedAmount || 0), 0);
// 认领只登记本地:剩余可认领 = 收款金额 − 已认领金额
return Math.max(Number(flow.receiptAmount || 0) - Number(flow.claimedAmount || 0), 0);
},
maxAllocation(row) {
const current = Number(row.settlement.allocations?.[row.flow.id] || 0);
@@ -355,12 +353,8 @@ export default {
}
for (const flow of this.flows) {
const total = this.flowAllocatedTotal(flow.id);
const remaining = Math.max(
Number(flow.unclaimedAmount || 0) - Number(flow.claimedAmount || 0),
0
);
if (total > remaining + 0.001) {
this.$message.warning(`流水${flow.receiptNoticeNo}的累计认领金额不能超过公告未认领金额`);
if (total > this.flowRemaining(flow) + 0.001) {
this.$message.warning(`流水${flow.receiptNoticeNo}的累计认领金额不能超过剩余可认领金额`);
return;
}
}
+7 -8
View File
@@ -12,7 +12,7 @@
<section-card title="收款信息">
<el-row :gutter="32">
<el-col :span="6"
><el-form-item label="认领通知单"
><el-form-item label="收款单号"
><el-input v-model="form.receiptNoticeNo" disabled /></el-form-item
></el-col>
<el-col :span="6"
@@ -48,8 +48,8 @@
><el-input :model-value="formatMoney(form.claimedAmount)" disabled /></el-form-item
></el-col>
<el-col :span="6"
><el-form-item label="金蝶未认领金额"
><el-input :model-value="formatMoney(form.unclaimedAmount)" disabled /></el-form-item
><el-form-item label="未匹配金额"
><el-input :model-value="formatMoney(form.unmatchAmountRec)" disabled /></el-form-item
></el-col>
<el-col :span="6"
><el-form-item label="剩余可认领金额"
@@ -205,7 +205,7 @@ const emptyForm = () => ({
transactionTime: '',
detailSerialNo: '',
claimedAmount: 0,
unclaimedAmount: 0,
unmatchAmountRec: 0,
claimerName: '',
claimerDeptName: '',
claimDate: '',
@@ -252,11 +252,10 @@ export default {
);
},
claimableAmount() {
const unclaimed = Number(this.form.unclaimedAmount || 0);
// 认领只登记本地:剩余可认领 = 收款金额 − 已认领金额
const receipt = Number(this.form.receiptAmount || 0);
const ceiling =
unclaimed > 0 && receipt > 0 ? Math.min(unclaimed, receipt) : unclaimed || receipt;
return Math.max(ceiling - Number(this.form.claimedAmount || 0), 0);
const claimed = Number(this.form.claimedAmount || 0);
return Math.max(receipt - claimed, 0);
},
},
created() {
+10 -77
View File
@@ -3,7 +3,7 @@
<section v-if="searchVisible" class="receipt-flow-page__search">
<el-form :model="query" label-position="right" label-width="88px" @submit.prevent>
<div class="receipt-flow-page__search-grid">
<el-form-item label="认领通知单">
<el-form-item label="收款单号">
<el-input v-model="query.receiptNoticeNo" clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="对方户名">
@@ -15,16 +15,6 @@
<el-form-item label="对方账号">
<el-input v-model="query.counterpartyAccount" clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="推送状态">
<el-select v-model="query.pushStatus" clearable placeholder="请选择">
<el-option
v-for="item in pushStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="认领状态">
<el-select v-model="query.claimStatus" clearable placeholder="请选择">
<el-option
@@ -159,23 +149,10 @@
<span v-else>{{ displayValue(row[column.prop]) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="170" fixed="right" align="center">
<el-table-column label="操作" width="100" fixed="right" align="center">
<template #default="{ row }">
<el-link
v-if="canClaim(row)"
type="primary"
:disabled="pushingId === row.id"
@click="openClaim(row)"
>认领</el-link
>
<el-link
v-if="canPush(row)"
type="warning"
:disabled="pushingId === row.id"
@click="handlePush(row)"
>推送金蝶</el-link
>
<span v-if="!canClaim(row) && !canPush(row)">-</span>
<el-link v-if="canClaim(row)" type="primary" @click="openClaim(row)">认领</el-link>
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
@@ -201,13 +178,13 @@
>
<div v-loading="detailDialog.loading">
<el-descriptions :column="3" border>
<el-descriptions-item label="认领通知单">
<el-descriptions-item label="收款单号">
{{ displayValue(detailDialog.data.receiptNoticeNo) }}
</el-descriptions-item>
<el-descriptions-item label="付款人">
{{ displayValue(detailDialog.data.payerName) }}
</el-descriptions-item>
<el-descriptions-item label="付款金额">
<el-descriptions-item label="收款金额">
{{ formatMoney(detailDialog.data.receiptAmount) }}
</el-descriptions-item>
<el-descriptions-item label="交易时间">
@@ -236,13 +213,8 @@
<el-descriptions-item label="剩余可认领金额">
{{ formatMoney(detailDialog.data.remainingAmount) }}
</el-descriptions-item>
<el-descriptions-item label="金蝶未认领金额">
{{ formatMoney(detailDialog.data.unclaimedAmount) }}
</el-descriptions-item>
<el-descriptions-item label="推送状态">
<el-tag :type="statusType(detailDialog.data, { prop: 'pushStatusName' })">
{{ displayValue(detailDialog.data.pushStatusName) }}
</el-tag>
<el-descriptions-item label="未匹配金额">
{{ formatMoney(detailDialog.data.unmatchAmountRec) }}
</el-descriptions-item>
<el-descriptions-item label="金蝶收款单号">
{{ displayValue(detailDialog.data.kingdeeBillNo) }}
@@ -250,9 +222,6 @@
<el-descriptions-item label="金蝶状态">
{{ displayValue(detailDialog.data.kingdeeBillStatusName) }}
</el-descriptions-item>
<el-descriptions-item label="推送失败原因">
{{ displayValue(detailDialog.data.pushFailReason) }}
</el-descriptions-item>
<el-descriptions-item label="摘要">
{{ displayValue(detailDialog.data.summary) }}
</el-descriptions-item>
@@ -267,9 +236,7 @@
</el-table-column>
<el-table-column prop="operatorName" label="认领人" min-width="120" align="center" />
<el-table-column label="状态变化" min-width="170" align="center">
<template #default="{ row }">
{{ row.actionType === 'push' ? row.actionName : claimStatusChange(row) }}
</template>
<template #default="{ row }">{{ claimStatusChange(row) }}</template>
</el-table-column>
<el-table-column prop="content" label="认领说明" min-width="220" />
</el-table>
@@ -297,7 +264,6 @@ const emptyQuery = () => ({
summary: '',
detailSerialNo: '',
claimStatus: '',
pushStatus: '',
kingdeeBillStatus: '',
receiptAmountStart: undefined,
receiptAmountEnd: undefined,
@@ -314,7 +280,6 @@ export default {
ArrowUp,
query: emptyQuery(),
claimStatusOptions: api.claimStatusOptions,
pushStatusOptions: api.pushStatusOptions,
kingdeeBillStatusOptions: api.kingdeeBillStatusOptions,
columns: receiptFlowTableColumns,
visibleColumnKeys: receiptFlowTableColumns.map(column => column.prop),
@@ -324,7 +289,6 @@ export default {
selectedRows: [],
loading: false,
syncing: false,
pushingId: null,
searchExpanded: false,
// 与 avue-crud 一致:搜索项数量超过 searchIndex(默认一行 4 项)时才显示“展开/收起”
searchIndex: 4,
@@ -389,7 +353,7 @@ export default {
this.syncing = true;
try {
const syncedCount = Number(this.unwrapData(await api.sync()) || 0);
this.$message.success(`流水同步完成,共同步${syncedCount}条认领公告`);
this.$message.success(`流水同步完成,共同步${syncedCount}条收款流水`);
await this.loadTable();
} finally {
this.syncing = false;
@@ -448,38 +412,7 @@ export default {
canClaim(row) {
return this.hasPermission('receipt_flow_claim') && Number(row.remainingAmount || 0) > 0;
},
canPush(row) {
return (
this.hasPermission('receipt_flow_push') &&
!!row.billId &&
row.claimStatus === 'claimed' &&
row.pushStatus !== 'pushed'
);
},
async handlePush(row) {
try {
await this.$confirm(
`确认将流水【${row.receiptNoticeNo || row.detailSerialNo || ''}】推送金蝶生成收款单?`,
'推送金蝶',
{ type: 'warning' }
);
} catch {
return;
}
this.pushingId = row.id;
try {
const data = this.unwrapData(await api.pushKingdee(row.id));
const billNo = data?.kingdeeBillNo;
this.$message.success(billNo ? `推送成功,金蝶收款单号:${billNo}` : '推送成功');
await this.loadTable();
} finally {
this.pushingId = null;
}
},
statusType(row, column) {
if (column?.prop === 'pushStatusName') {
return { pushed: 'success', failed: 'danger' }[row.pushStatus] || 'info';
}
return (
{ claimed: 'success', partial: 'warning', unclaimed: 'info' }[row.claimStatus] || ''
);
@@ -440,8 +440,15 @@
/>
<section-card title="发票信息">
<div class="formal-editor__invoice-claim">
<el-button type="primary" @click="openInvoiceClaimDialog">认领查询</el-button>
<div v-if="editable" class="formal-editor__invoice-claim">
<!-- 先票后款:付款结算单选审批通过的收票单(全额认领);收款结算单维持原票池认领 -->
<el-button
v-if="form.settlementType !== 'receivable'"
type="primary"
@click="openInvoiceReceiptDialog"
>选择收票单</el-button
>
<el-button v-else type="primary" @click="openInvoiceClaimDialog">认领查询</el-button>
</div>
<el-table :data="invoices" border>
<el-table-column type="index" label="序号" width="64" align="center" />
@@ -452,11 +459,6 @@
<el-table-column prop="invoiceType" label="发票类型" min-width="150" align="center">
<template #default="{ row }">{{ displayValue(row.invoiceType) }}</template>
</el-table-column>
<el-table-column prop="taxRate" label="税率" min-width="100" align="center">
<template #default="{ row }">
{{ row.taxRate === null || row.taxRate === undefined ? '-' : `${row.taxRate}%` }}
</template>
</el-table-column>
<el-table-column
prop="invoiceAmount"
label="发票金额(含税)"
@@ -480,17 +482,39 @@
align="center"
>
<template #default="{ row }">
<!-- 匹配结算单金额支持手动修改:编辑态全放开;查看页仅指定失败行(failed)开放,重推时一并保存生效 -->
<el-input-number
v-if="editable"
v-if="canEditMatchedAmount(row)"
v-model="row.matchedAmount"
:min="0"
:max="Number(row.availableInvoiceAmount || 0)"
:max="Number(row.availableInvoiceAmount || row.invoiceAmount || 0)"
:precision="2"
:controls="false"
/>
<span v-else>{{ formatMoney(row.matchedAmount) }}</span>
</template>
</el-table-column>
<el-table-column label="是否已指定应付" min-width="130" align="center">
<template #default="{ row }">
<el-tooltip
:disabled="row.kingdeePushStatus !== 'failed'"
:content="row.kingdeePushMsg || '推送金蝶失败,可重新推送'"
placement="top"
>
<el-tag
:type="
{ assigned: 'success', failed: 'danger' }[row.kingdeePushStatus] || 'info'
"
size="small"
>
{{
{ assigned: '已指定应付', failed: '指定失败' }[row.kingdeePushStatus] ||
'未指定'
}}
</el-tag>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" width="220" fixed="right" align="center">
<template #default="{ row, $index }">
<div class="formal-editor__links">
@@ -506,6 +530,14 @@
@click="downloadInvoiceAttachment(row)"
>下载</el-link
>
<!-- 查看页重推入口:付款结算单已同步金蝶且认领票未指定应付(failed/pushed) -->
<el-link
v-if="canRepushInvoice(row)"
type="warning"
:disabled="repushing"
@click="repushInvoice(row)"
>重新推送</el-link
>
<el-link v-if="editable" type="danger" @click="removeInvoice($index)">删除</el-link>
</div>
</template>
@@ -546,6 +578,11 @@
@confirm="handleInvoiceClaimConfirm"
/>
<invoice-receipt-candidates-dialog
v-model="invoiceReceiptDialog.visible"
@confirm="handleInvoiceReceiptConfirm"
/>
<section-card
v-if="readonly"
:title="form.settlementType === 'receivable' ? '收款信息' : '付款信息'"
@@ -559,7 +596,7 @@
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column
prop="receiptNoticeNo"
label="认领通知单"
label="收款单号"
min-width="160"
align="center"
/>
@@ -1053,6 +1090,7 @@ import {
getNextNo,
getReceiptClaims,
claimInvoices as claimInvoicesApi,
repushClaimedInvoice,
queryInvoicePool,
save,
syncKingdee,
@@ -1068,6 +1106,7 @@ import {
settlementDetailPath,
} from '@/utils/settlement-detail-link';
import { getBfiExchangeRate } from '@/api/base/currency';
import InvoiceReceiptCandidatesDialog from './invoice-receipt-candidates-dialog.vue';
import {
createFormalSettlementForm,
formalSettlementFormFields,
@@ -1110,6 +1149,7 @@ export default {
components: {
InfoFilled,
KingdeeInvoicePoolDialog,
InvoiceReceiptCandidatesDialog,
ChangeRecordDetailDialog,
ProjectAdvanceWriteOff,
},
@@ -1149,6 +1189,8 @@ export default {
attachments: [],
selectedAttachmentRows: [],
invoices: [],
// 查看页认领收票单重推进行中(行级操作守卫)
repushing: false,
// 开票申请经金蝶回写的发票结果(只读展示,含板式文件/数电PDF下载地址)
issuedInvoices: [],
// 金蝶侧发票附件查看/下载进行中(行组级 loading 守卫)
@@ -1158,6 +1200,10 @@ export default {
selected: [],
confirming: false,
},
// 先票后款:付款结算单选收票单(审批通过+未认领)弹窗
invoiceReceiptDialog: {
visible: false,
},
attachmentUploadFiles: [],
attachmentTypeOptions: [],
attachmentFileTypes: [
@@ -2651,6 +2697,81 @@ export default {
openInvoiceClaimDialog() {
this.invoiceClaimDialog.visible = true;
},
// 先票后款:选收票单(审批通过+未被认领),确认后按发票号全额认领
openInvoiceReceiptDialog() {
this.invoiceReceiptDialog.visible = true;
},
// 查看页重推入口展示条件:只读 + 付款结算单 + 审批通过 + 金蝶应付单已同步 + 该票未指定应付(failed/pushed)
canRepushInvoice(row) {
return (
!this.editable &&
this.form.settlementType !== 'receivable' &&
this.form.approvalStatus === 'approved' &&
this.form.kingdeeSyncStatus === 'synced' &&
['failed', 'pushed'].includes(row.kingdeePushStatus || '')
);
},
// 查看页匹配金额编辑仅开放指定失败行(failed);pushed 行等待自动补偿指定,金额保持只读
canEditMatchedAmount(row) {
return (
this.editable ||
(this.form.settlementType !== 'receivable' &&
this.form.approvalStatus === 'approved' &&
this.form.kingdeeSyncStatus === 'synced' &&
row.kingdeePushStatus === 'failed')
);
},
// 重推 = 收票单A303-2 + 指定应付A302-6 一步式,服务端返回结果描述;完成后重载详情刷新状态
async repushInvoice(row) {
try {
await this.$confirm(`确认重新推送发票${row.invoiceNo || ''}到金蝶并指定应付?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
});
} catch {
return;
}
this.repushing = true;
try {
const data = this.unwrapData(
await repushClaimedInvoice(this.form.id, row.invoiceNo, Number(row.matchedAmount || 0))
);
this.$message.success(data || '重新推送成功');
await this.loadFormalDetail(this.form.id);
} finally {
this.repushing = false;
}
},
async handleInvoiceReceiptConfirm(rows) {
// 收票单行 → 结算单发票行:匹配金额固定=发票金额(全额认领),附件契约归一为单数 attachmentJson
const invoices = rows.map(row => ({
...row,
attachmentJson: row.attachmentJson || row.attachmentsJson,
invoiceType: row.invoiceType || '',
taxRate: row.taxRate ?? null,
availableInvoiceAmount: Number(row.invoiceAmount || 0),
matchedAmount: Number(row.invoiceAmount || 0),
}));
if (!this.form.id) {
const existingNumbers = new Set(this.invoices.map(item => String(item.invoiceNo)));
this.invoices = [
...this.invoices,
...invoices.filter(row => !existingNumbers.has(String(row.invoiceNo))),
];
this.$message.success(`已选择${invoices.length}条收票单,保存正式结算单后生效`);
return;
}
const existingNumbers = new Set(this.invoices.map(item => String(item.invoiceNo)));
const newInvoices = invoices.filter(row => !existingNumbers.has(String(row.invoiceNo)));
if (!newInvoices.length) {
this.$message.warning('所选收票单均已认领');
return;
}
await claimInvoicesApi({ formalSettlementId: this.form.id, invoices: newInvoices });
await this.loadFormalDetail(this.form.id);
this.$message.success('收票单认领成功,已同步正式结算单及关联付款单据');
},
invoicePoolQueryApi(params) {
return queryInvoicePool(params);
},
@@ -2765,10 +2886,7 @@ export default {
}
matchedTotal += matchedAmount;
}
if (matchedTotal > this.resolveInvoiceClaimSettlementAmount()) {
this.$message.warning('发票匹配结算单金额合计不能超过结算金额');
return false;
}
// 先票后款全额认领:匹配金额=发票全额,可超过结算金额,不再校验合计上限(收票状态按覆盖判定)
return true;
},
removeInvoice(index) {
@@ -3260,10 +3378,47 @@ export default {
return files.length ? JSON.stringify(files) : '';
},
parseFeeItems(value) {
const raw = this.parseRawFeeItems(value);
const metaKeys = new Set([
'currency',
'totalFreightAmount',
'otherFreightAmount',
'freightItems',
'freightTotal',
'totalFreight',
'totalAmount',
'freight',
'freightAmount',
'transportFee',
'otherFeeTotal',
'otherAmount',
]);
const result = {};
let otherFeeAmount = 0;
Object.entries(raw).forEach(([key, amount]) => {
if (!key) return;
if (key === 'otherFreightAmount' || key === 'otherFeeTotal' || key === 'otherAmount') {
otherFeeAmount += Number(amount || 0);
return;
}
if (metaKeys.has(key)) return;
if (amount && typeof amount === 'object') return;
result[key] = amount;
});
const hasOtherFee = Object.keys(result).some(
name => String(name).includes('其它费') || String(name).includes('其他费')
);
if (otherFeeAmount && !hasOtherFee) {
result['其它费用'] = otherFeeAmount;
}
return result;
},
parseRawFeeItems(value) {
if (!value) return {};
if (typeof value === 'object') return value;
if (typeof value === 'object') return { ...value };
try {
return JSON.parse(value);
const parsed = JSON.parse(value);
return parsed && typeof parsed === 'object' ? parsed : {};
} catch {
return {};
}
@@ -23,6 +23,15 @@
@click="$emit('payment')"
>付款申请</el-button
>
<!-- 先票后款:付款结算单审批通过后可发票登记(关联收票单并推送指定应付),选中一条付款结算单操作 -->
<el-button
v-if="showPaymentAction && hasPermission('invoice_register')"
type="primary"
plain
:disabled="!selection.length"
@click="emitSingle('invoiceRegister')"
>发票登记</el-button
>
<el-button
v-if="hasPermission('sync')"
type="primary"
@@ -79,6 +88,23 @@
<span v-else>-</span>
</template>
<template #kingdeeAssignStatus="{ row }">
<el-tooltip
:disabled="row.kingdeeAssignStatus !== 'failed'"
:content="row.kingdeeAssignFailReason || '收票单指定应付失败,可在查看页对失败行重新推送'"
placement="top"
>
<el-tag
v-if="row.kingdeeAssignStatus"
:type="{ assigned: 'success', failed: 'danger' }[row.kingdeeAssignStatus] || 'info'"
size="small"
>
{{ { assigned: '已指定应付', failed: '指定失败' }[row.kingdeeAssignStatus] || '未指定' }}
</el-tag>
<span v-else>-</span>
</el-tooltip>
</template>
<template #menu="{ row }">
<el-link
v-for="action in rowActions(row)"
@@ -0,0 +1,186 @@
<template>
<el-dialog
v-model="visible"
title="选择收票单"
width="86%"
append-to-body
destroy-on-close
@open="handleOpen"
>
<div class="invoice-candidates-dialog__search">
<el-input
v-model="keyword"
clearable
placeholder="发票号 / 销方(开票单位) / 购方(受票单位)"
@keyup.enter="handleQuery"
/>
<el-button type="primary" :loading="loading" @click="handleQuery">查询</el-button>
</div>
<el-table
ref="candidatesTable"
v-loading="loading"
:data="rows"
row-key="id"
border
height="460"
@selection-change="selection = $event"
>
<el-table-column type="selection" width="52" fixed="left" align="center" reserve-selection />
<el-table-column prop="invoiceNo" label="发票号码" min-width="180" />
<el-table-column prop="invoiceDate" label="开票日期" min-width="110" align="center">
<template #default="{ row }">{{ row.invoiceDate || '-' }}</template>
</el-table-column>
<el-table-column prop="invoiceType" label="发票类型" min-width="140" />
<el-table-column prop="issuerName" label="开票单位(销方)" min-width="180" show-overflow-tooltip />
<el-table-column prop="receiverName" label="受票单位(购方)" min-width="180" show-overflow-tooltip />
<el-table-column label="发票金额(含税)" min-width="140" align="right">
<template #default="{ row }">{{ formatMoney(row.invoiceAmount) }}</template>
</el-table-column>
<el-table-column label="推送状态" min-width="120" align="center">
<template #default="{ row }">
<el-tooltip
:disabled="row.kingdeePushStatus !== 'failed'"
:content="row.kingdeePushMsg || '推送金蝶失败,可在收票登记列表重推'"
placement="top"
>
<el-tag :type="pushStatusType(row.kingdeePushStatus)" size="small">
{{ pushStatusName(row.kingdeePushStatus) }}
</el-tag>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<div class="invoice-candidates-dialog__pagination">
<el-pagination
v-model:current-page="page.current"
v-model:page-size="page.size"
:total="page.total"
layout="total, sizes, prev, pager, next, jumper"
@current-change="load"
@size-change="handleSizeChange"
/>
</div>
<div class="invoice-candidates-dialog__tip">
仅列出审批通过且未被其他付款结算单认领的收票单
</div>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="handleConfirm">
确定{{ selection.length ? `(${selection.length})` : '' }}
</el-button>
</template>
</el-dialog>
</template>
<script>
import { claimCandidates } from '@/api/payment/invoiceReceipt';
export default {
name: 'InvoiceReceiptCandidatesDialog',
props: {
modelValue: { type: Boolean, required: true },
},
emits: ['update:modelValue', 'confirm'],
data() {
return {
loading: false,
keyword: '',
rows: [],
selection: [],
page: { current: 1, size: 10, total: 0 },
};
},
computed: {
visible: {
get() {
return this.modelValue;
},
set(value) {
this.$emit('update:modelValue', value);
},
},
},
methods: {
unwrap(response) {
const body = response?.data || response || {};
return body?.data || body;
},
handleOpen() {
this.keyword = '';
this.page = { current: 1, size: 10, total: 0 };
this.selection = [];
this.$refs.candidatesTable?.clearSelection?.();
this.load();
},
handleQuery() {
this.page.current = 1;
this.load();
},
handleSizeChange() {
this.page.current = 1;
this.load();
},
async load() {
this.loading = true;
try {
const data = this.unwrap(
await claimCandidates({
current: this.page.current,
size: this.page.size,
keyword: this.keyword,
})
);
this.rows = data?.records || [];
this.page.total = Number(data?.total || 0);
} finally {
this.loading = false;
}
},
handleConfirm() {
if (!this.selection.length) {
this.$message.warning('请至少选择一条收票单');
return;
}
this.$emit('confirm', this.selection.map(row => ({ ...row })));
this.visible = false;
},
pushStatusName(value) {
return (
{ pushed: '已保存收票单', assigned: '已指定应付', failed: '推送失败', unsynced: '未推送' }[
value
] || '未推送'
);
},
pushStatusType(value) {
return { pushed: 'warning', assigned: 'success', failed: 'danger', unsynced: 'info' }[value] || 'info';
},
formatMoney(value) {
const number = Number(value);
return Number.isFinite(number) ? number.toFixed(2) : '-';
},
},
};
</script>
<style lang="scss" scoped>
.invoice-candidates-dialog__search {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-bottom: 12px;
.el-input {
width: 360px;
}
}
.invoice-candidates-dialog__pagination {
display: flex;
justify-content: flex-end;
margin-top: 12px;
}
.invoice-candidates-dialog__tip {
margin-top: 8px;
color: #909399;
font-size: 12px;
}
</style>
@@ -3061,6 +3061,42 @@ export default {
});
},
parseFeeItems(value) {
const raw = this.parseRawFeeItems(value);
const metaKeys = new Set([
'currency',
'totalFreightAmount',
'otherFreightAmount',
'freightItems',
'freightTotal',
'totalFreight',
'totalAmount',
'freight',
'freightAmount',
'transportFee',
'otherFeeTotal',
'otherAmount',
]);
const result = {};
let otherFeeAmount = 0;
Object.entries(raw).forEach(([key, amount]) => {
if (!key) return;
if (key === 'otherFreightAmount' || key === 'otherFeeTotal' || key === 'otherAmount') {
otherFeeAmount += Number(amount || 0);
return;
}
if (metaKeys.has(key)) return;
if (amount && typeof amount === 'object') return;
result[key] = amount;
});
const hasOtherFee = Object.keys(result).some(
name => String(name).includes('其它费') || String(name).includes('其他费')
);
if (otherFeeAmount && !hasOtherFee) {
result['其它费用'] = otherFeeAmount;
}
return result;
},
parseRawFeeItems(value) {
if (!value) return {};
if (typeof value === 'object') return { ...value };
try {
@@ -0,0 +1,42 @@
<template>
<div v-loading="true" class="formal-settlement-redirect" />
</template>
<script>
import * as api from '@/api/settlement/formalSettlement';
import { settlementListPath } from '@/utils/settlement-menu';
/**
* 菜单拆分为付款/收款结算单后,旧路径 /settlement/formal-settlement 已无菜单路由。
* 本页按结算单号解析类型后 replace 到对应列表页,并保留 query(含 detailNo)。
*/
export default {
name: 'FormalSettlementRedirect',
async created() {
const detailNo = String(this.$route.query.detailNo || '').trim();
let settlementType = '';
if (detailNo) {
try {
const response = await api.getList(1, 10, { formalSettlementNo: detailNo });
const body = response?.data || response || {};
const data = body?.data || body;
const row = (data.records || []).find(item => item.formalSettlementNo === detailNo);
settlementType = row?.settlementType || '';
} catch (error) {
settlementType = '';
}
}
const path = settlementListPath(settlementType);
await this.$router.replace({
path,
query: { ...this.$route.query },
});
},
};
</script>
<style scoped>
.formal-settlement-redirect {
min-height: 240px;
}
</style>
+228 -5
View File
@@ -98,6 +98,125 @@
></template
>
</el-dialog>
<!-- 先票后款路径B:结算单审批后发票登记(关联收票单 → 确认推送指定应付) -->
<el-dialog
v-model="invoiceRegisterDialog.visible"
title="发票登记"
width="86%"
append-to-body
class="invoice-register-dialog"
>
<section-card title="结算基本信息">
<el-descriptions :column="4" border size="small">
<el-descriptions-item label="结算单号">
{{ invoiceRegisterDialog.row.formalSettlementNo || '-' }}
</el-descriptions-item>
<el-descriptions-item label="项目名称">
{{ invoiceRegisterDialog.row.projectName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="所属组织">
{{ invoiceRegisterDialog.row.deptName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="合同编号">
{{ invoiceRegisterDialog.row.contractNo || '-' }}
</el-descriptions-item>
<el-descriptions-item label="付款方">
{{ invoiceRegisterDialog.row.payerName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="收款方">
{{ invoiceRegisterDialog.row.payeeName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="结算类型">
{{ settlementTypeName(invoiceRegisterDialog.row.settlementType) }}
</el-descriptions-item>
<el-descriptions-item label="业务模式">
{{ invoiceRegisterDialog.row.businessModelName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="结算总金额">
{{ formatMoney(invoiceRegisterDialog.row.settlementAmount, invoiceRegisterDialog.row.currency) }}
</el-descriptions-item>
<el-descriptions-item label="已匹配发票金额">
{{ formatMoney(invoiceRegisterDialog.row.invoiceAmount, invoiceRegisterDialog.row.currency) }}
</el-descriptions-item>
<el-descriptions-item label="创建人">
{{ invoiceRegisterDialog.row.createUserName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="创建时间">
{{ invoiceRegisterDialog.row.createTime || '-' }}
</el-descriptions-item>
<el-descriptions-item label="金蝶同步状态" :span="2">
{{ kingdeeSyncName(invoiceRegisterDialog.row.kingdeeSyncStatus) }}
</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">
{{ invoiceRegisterDialog.row.remark || '-' }}
</el-descriptions-item>
</el-descriptions>
</section-card>
<section-card title="认领收票单">
<div class="invoice-register-dialog__actions">
<el-button
type="primary"
plain
:disabled="invoiceRegisterDialog.saving"
@click="candidatesDialog.visible = true"
>选择收票单</el-button
>
</div>
<el-table :data="invoiceRegisterDialog.candidates" border class="invoice-register-dialog__table">
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column prop="invoiceNo" label="发票号" min-width="160" align="center" />
<el-table-column prop="invoiceDate" label="开票日期" min-width="120" align="center" />
<el-table-column prop="invoiceType" label="发票类型" min-width="140" align="center" />
<el-table-column prop="issuerName" label="开票单位(销方)" min-width="170" show-overflow-tooltip />
<el-table-column label="发票金额(含税)" min-width="140" align="right">
<template #default="{ row }">{{ formatMoney(row.invoiceAmount) }}</template>
</el-table-column>
<el-table-column label="匹配结算单金额" min-width="160" align="right">
<template #default="{ row }">
<!-- 匹配金额支持手动修改(默认=发票金额全额),已认领回显行只读 -->
<el-input-number
v-if="!row.claimed"
v-model="row.matchedAmount"
:min="0"
:max="Number(row.invoiceAmount || 0)"
:precision="2"
:controls="false"
/>
<span v-else>{{ formatMoney(row.matchedAmount || row.invoiceAmount) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="90" fixed="right" align="center">
<template #default="{ row, $index }">
<!-- 已认领票回显:认领保存为增量逻辑不支持移除,仅新增票可移除 -->
<el-tag v-if="row.claimed" size="small">已认领</el-tag>
<el-link
v-else
type="danger"
:disabled="invoiceRegisterDialog.saving"
@click="invoiceRegisterDialog.candidates.splice($index, 1)"
>移除</el-link
>
</template>
</el-table-column>
</el-table>
</section-card>
<template #footer>
<el-button @click="invoiceRegisterDialog.visible = false">取消</el-button>
<el-button
type="primary"
:loading="invoiceRegisterDialog.saving"
:disabled="!invoiceRegisterDialog.candidates.length"
@click="submitInvoiceRegister"
>保存认领</el-button
>
</template>
</el-dialog>
<invoice-receipt-candidates-dialog
v-model="candidatesDialog.visible"
@confirm="addInvoiceRegisterCandidates"
/>
</basic-container>
</template>
@@ -113,16 +232,18 @@ import { isAdministrator } from '@/utils/auth';
import { downloadFile } from '@/utils/util';
import * as api from '@/api/settlement/formalSettlement';
import * as paymentApi from '@/api/payment/paymentApplication';
import { formalSettlementOption } from '@/option/settlement/formalSettlement';
import * as invoiceReceiptApi from '@/api/payment/invoiceReceipt';
import { formalSettlementOption, kingdeeSyncName } from '@/option/settlement/formalSettlement';
import organizationSearch from '@/mixins/organization-search';
import FormalSettlementEditor from './components/formal-settlement-editor.vue';
import FormalSettlementTablePanel from './components/formal-settlement-table-panel.vue';
import InvoiceReceiptCandidatesDialog from './components/invoice-receipt-candidates-dialog.vue';
import { submitMkApprovalFlow, loadMkEnabled } from '@/utils/mk-approval';
import { getMKApprovalUrl } from '@/api/system/business-process';
export default {
name: 'FormalSettlement',
components: { FormalSettlementEditor, FormalSettlementTablePanel },
components: { FormalSettlementEditor, FormalSettlementTablePanel, InvoiceReceiptCandidatesDialog },
mixins: [organizationSearch],
props: {
/** 菜单拆分后锁定结算类型:payable=付款结算单,receivable=收款结算单 */
@@ -151,7 +272,19 @@ export default {
rows: [],
form: { appliedAmount: 0, remark: '' },
},
// 先票后款路径B:发票登记(关联收票单 + 确认推送指定应付)
invoiceRegisterDialog: {
visible: false,
saving: false,
row: {},
candidates: [],
},
candidatesDialog: {
visible: false,
},
mkEnabled: false,
// keep-alive 多标签下仅激活实例响应 detailNo,避免缓存页重复弹窗
isPageActive: true,
};
},
computed: {
@@ -222,11 +355,10 @@ export default {
},
},
created() {
// 标签机制按 fullPath 缓存同页多实例:仅当前路由实例应响应 detailNo 跳转,防止缓存旧实例也弹窗
this.ownFullPath = this.$route.fullPath;
this.loadOrganizationOptions();
},
mounted() {
this.isPageActive = true;
this.openRouteDetail();
loadMkEnabled()
.then(enabled => {
@@ -237,10 +369,18 @@ export default {
});
},
activated() {
this.isPageActive = true;
if (sessionStorage.getItem('formal-settlement-list-refresh') === '1') {
sessionStorage.removeItem('formal-settlement-list-refresh');
this.onLoad(this.page);
}
// 从其他页带 detailNo 跳回时,激活态补开详情
if (this.$route.query.detailNo) {
this.openRouteDetail();
}
},
deactivated() {
this.isPageActive = false;
},
methods: {
hasPermission(actionOrCode) {
@@ -288,6 +428,7 @@ export default {
return: () => this.handleReturn(row),
void: () => this.handleVoid(row),
print: () => this.handlePrint(row),
invoiceRegister: () => this.openInvoiceRegisterDialog(row),
};
(handlers[type] || (() => {}))();
},
@@ -320,7 +461,7 @@ export default {
},
async openRouteDetail(detailNo = this.$route.query.detailNo) {
const formalSettlementNo = String(detailNo || '').trim();
if (!formalSettlementNo || this.ownFullPath !== this.$route.fullPath) return;
if (!formalSettlementNo || !this.isPageActive) return;
const response = await api.getList(1, 10, { formalSettlementNo });
const data = this.unwrapData(response);
const row = (data.records || []).find(item => item.formalSettlementNo === formalSettlementNo);
@@ -328,6 +469,18 @@ export default {
this.$message.warning(`未找到对应的${this.billLabel}`);
return;
}
// 锁定菜单(付款/收款)下若单号属于另一类型,跳到对应列表页再打开
if (
this.lockedSettlementType &&
row.settlementType &&
row.settlementType !== this.lockedSettlementType
) {
await this.$router.replace({
path: settlementListPath(row.settlementType),
query: { detailNo: formalSettlementNo },
});
return;
}
if (!this.lockedSettlementType) {
this.activeSettlementType = row.settlementType || this.activeSettlementType;
}
@@ -575,6 +728,68 @@ export default {
this.paymentDialog.loading = false;
}
},
// 先票后款路径B:发票登记——认领收票单(claim-invoices 走增量认领并同步收票侧关系)
// 打开时回显本单已认领收票单(claimed 标记禁止移除),已认领过票的可直接保存推送补指定应付
async openInvoiceRegisterDialog(row) {
if ((row?.approvalStatus || '') !== 'approved') {
this.$message.warning('仅审批通过的付款结算单允许发票登记');
return;
}
this.invoiceRegisterDialog.row = row || {};
this.invoiceRegisterDialog.candidates = [];
this.invoiceRegisterDialog.visible = true;
try {
const claimed = this.unwrapData(await invoiceReceiptApi.claimedReceipts(row.id)) || [];
this.invoiceRegisterDialog.candidates = claimed.map(item => ({ ...item, claimed: true }));
} catch (error) {
this.invoiceRegisterDialog.candidates = [];
}
},
addInvoiceRegisterCandidates(rows) {
const existing = new Set(
this.invoiceRegisterDialog.candidates.map(item => String(item.invoiceNo))
);
const added = rows.filter(row => !existing.has(String(row.invoiceNo)));
if (!added.length) {
this.$message.warning('所选收票单均已登记');
return;
}
// 全额认领:匹配金额固定=发票金额(仅暂存展示,点保存认领确认后才落库)
this.invoiceRegisterDialog.candidates.push(
...added.map(row => ({ ...row, matchedAmount: Number(row.invoiceAmount || 0) }))
);
},
async submitInvoiceRegister() {
const settlement = this.invoiceRegisterDialog.row;
const invoices = this.invoiceRegisterDialog.candidates.map(row => ({
...row,
attachmentJson: row.attachmentJson || row.attachmentsJson,
invoiceType: row.invoiceType || '',
taxRate: row.taxRate ?? null,
availableInvoiceAmount: Number(row.invoiceAmount || 0),
matchedAmount: Number(row.matchedAmount || row.invoiceAmount || 0),
}));
try {
// 先确认后保存:确定才认领并推送指定应付,取消不保存任何数据
await this.$confirm('确认认领并推送金蝶指定应付?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
});
} catch {
return;
}
this.invoiceRegisterDialog.saving = true;
try {
await api.claimInvoices({ formalSettlementId: settlement.id, invoices });
const data = this.unwrapData(await api.invoiceAssignKingdee(settlement.id));
this.$message.success(data || '发票登记推送金蝶成功');
this.invoiceRegisterDialog.visible = false;
this.onLoad(this.page);
} finally {
this.invoiceRegisterDialog.saving = false;
}
},
handlePrint(row) {
if (!row) {
row = this.selectedOne('打印');
@@ -624,6 +839,9 @@ export default {
params.settlementType = this.lockedSettlementType || this.activeSettlementType;
return params;
},
settlementTypeName(value) {
return { payable: '付款', receivable: '收款' }[value] || '-';
},
unwrapData(response) {
const body = response?.data || response || {};
return body?.data || body;
@@ -664,6 +882,11 @@ export default {
margin-bottom: 12px;
color: #606266;
}
.invoice-register-dialog__actions {
display: flex;
justify-content: flex-end;
margin-bottom: 8px;
}
:deep(.formal-page.basic-container .basic-container__card > .el-card__body) {
padding: 0;
}
@@ -1914,7 +1914,7 @@ export default {
return;
}
if (this.settlementType === 'payable' && column.prop === 'formalSettlementNo') {
this.openSettlementDetail('/settlement/formal-settlement', row.formalSettlementNo);
this.openSettlementDetail('/settlement/payment-settlement', row.formalSettlementNo);
return;
}
this.openDetailDialog(row);
@@ -190,6 +190,7 @@ import { kingdeeStatusType } from '@/utils/kingdee';
import SettlementAdjustmentEditor from './components/settlement-adjustment-editor.vue';
import SettlementTableTools from './components/settlement-table-tools.vue';
import { isAdministrator } from '@/utils/auth';
import { settlementListPath } from '@/utils/settlement-menu';
const emptyQuery = () => ({
adjustmentNo: '',
@@ -290,20 +291,23 @@ export default {
openView(row) {
this.editor = { visible: true, id: row.id, readonly: true };
},
// 单据号→本单查看弹窗;结算单号→跳正式结算列表页并自动打开该单详情(页面支持 ?detailNo=)
// 单据号→本单查看弹窗;结算单号→跳付款/收款结算列表页并自动打开该单详情(页面支持 ?detailNo=)
handleTableLink(row, column) {
if (column.prop === 'formalSettlementNo') {
this.openSettlementDetail(row.formalSettlementNo);
this.openSettlementDetail(row.formalSettlementNo, row.settlementType);
return;
}
this.openView(row);
},
openSettlementDetail(detailNo) {
openSettlementDetail(detailNo, settlementType) {
if (!detailNo) {
this.$message.info('当前记录未关联结算单详情');
return;
}
this.$router.push({ path: '/settlement/formal-settlement', query: { detailNo } });
this.$router.push({
path: settlementListPath(settlementType),
query: { detailNo },
});
},
isEditable(row) {
return ['draft', 'returned'].includes(row.approvalStatus);
+112 -18
View File
@@ -303,6 +303,42 @@
/>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="身份证有效期" prop="idCardValidEnd">
<div class="date-range">
<el-date-picker
v-model="driverForm.idCardValidStart"
type="date"
value-format="YYYY-MM-DD"
placeholder="起"
:disabled="driverForm.idCardLongTerm === 1"
@change="validateFormField('idCardValidEnd')"
/>
<el-date-picker
v-model="driverForm.idCardValidEnd"
type="date"
value-format="YYYY-MM-DD"
placeholder="止"
:disabled="driverForm.idCardLongTerm === 1"
@change="validateFormField('idCardValidEnd')"
/>
</div>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="长期有效" prop="idCardLongTerm">
<el-checkbox
v-model="driverForm.idCardLongTerm"
:true-label="1"
:false-label="0"
@change="handleIdCardLongTermChange"
>
是
</el-checkbox>
</el-form-item>
</el-col>
</el-row>
</section-card>
<section-card title="驾驶证信息">
@@ -701,6 +737,9 @@ const emptyForm = () => ({
posts: '',
idCardFront: '',
idCardBack: '',
idCardValidStart: '',
idCardValidEnd: '',
idCardLongTerm: 0,
headPhoto: '',
drivingType: '',
drivingLicenseNo: '',
@@ -1260,12 +1299,13 @@ export default {
},
handleIdCardUploadSuccess(prop, url) {
this.setImage(prop, url);
if (prop !== 'idCardFront') {
return;
this.recognizeIdCard(url, prop);
},
handleIdCardLongTermChange() {
if (this.driverForm.idCardLongTerm === 1) {
this.driverForm.idCardValidEnd = '';
}
this.recognizeBaiduOcr(url, 'id_card', 'front', '身份证', data => {
this.applyIdCardRecognition(data);
});
this.validateFormField('idCardValidEnd');
},
handleDrivingLicenseUploadSuccess(prop, url) {
this.setImage(prop, url);
@@ -1308,7 +1348,7 @@ export default {
loading.close();
});
},
recognizeIdCard(url = '') {
recognizeIdCard(url = '', prop = 'idCardFront') {
if (!url) {
this.$message.warning('身份证图片上传成功,未获取到图片地址,无法自动识别');
return;
@@ -1321,7 +1361,7 @@ export default {
recognitionIDCard(url)
.then(res => {
const data = res.data.data || {};
this.applyIdCardRecognition(data);
this.applyIdCardRecognition(data, prop);
this.$message.success('身份证识别完成');
})
.catch(() => {
@@ -1444,45 +1484,62 @@ export default {
this.driverForm.drivingLicenseLongTerm = 0;
}
},
applyIdCardRecognition(data = {}) {
applyIdCardRecognition(data = {}, prop = 'idCardFront') {
// 正反面都走同一 ShenFenZheng 接口;反面结果可能也带姓名/民族等字段,
// 反面只补空、不覆盖正面已填值,避免国徽页误识别冲掉正面信息。
const fromBack = prop === 'idCardBack';
const canWrite = (current, next) => !!next && (!fromBack || !current);
const driverName = data.name || data.driverName || this.getOcrValue(data, ['name', '姓名']);
const idCardNo = String(
data.idNo ||
data.idCardNo ||
data.idCard ||
this.getOcrValue(data, ['idNo', 'idCardNo', 'number', '公民身份号码', '身份证号'])
).toUpperCase();
this.getOcrValue(data, ['idNo', 'idCardNo', 'number', '公民身份号码', '身份证号']) ||
''
)
.trim()
.toUpperCase();
const ocrGender = data.gender || this.getOcrValue(data, ['gender', '性别']);
const ocrNation =
data.nation ||
data.ethnicGroup ||
this.getOcrValue(data, ['ethnic_group', 'nation', '民族']);
const ocrAddress = data.address || this.getOcrValue(data, ['address', '住址', '地址']);
if (driverName) {
const ocrBirthday =
data.birthday || this.getOcrValue(data, ['birthday', '出生', '出生年月', '出生日期']);
if (canWrite(this.driverForm.driverName, driverName)) {
this.driverForm.driverName = driverName;
}
if (ocrGender) {
if (canWrite(this.driverForm.gender, ocrGender)) {
this.driverForm.gender = ocrGender;
}
if (ocrNation) {
if (canWrite(this.driverForm.nation, ocrNation)) {
this.driverForm.nation = this.normalizeNation(ocrNation);
}
if (ocrAddress) {
if (canWrite(this.driverForm.address, ocrAddress)) {
this.driverForm.address = String(ocrAddress).trim();
}
if (idCardNo) {
if (ocrBirthday) {
const birthday = this.normalizeBirthday(ocrBirthday);
if (canWrite(this.driverForm.birthday, birthday)) {
this.driverForm.birthday = birthday;
}
}
if (idCardNo && canWrite(this.driverForm.idCardNo, idCardNo)) {
this.driverForm.idCardNo = idCardNo;
if (!this.driverForm.qualificationNo) {
this.driverForm.qualificationNo = idCardNo;
}
const idCardInfo = this.parseIdCardInfo(idCardNo);
if (idCardInfo.birthday) {
if (!this.driverForm.birthday && idCardInfo.birthday) {
this.driverForm.birthday = idCardInfo.birthday;
}
if (!this.driverForm.gender && idCardInfo.gender) {
this.driverForm.gender = idCardInfo.gender;
}
}
// 有效期:正反面都可回填;反面通常才有,识别到就覆盖
this.applyIdCardValidity(data);
this.$nextTick(() => {
[
'driverName',
@@ -1492,11 +1549,46 @@ export default {
'nation',
'qualificationNo',
'address',
].forEach(prop => {
this.$refs.driverForm?.validateField(prop);
'idCardValidEnd',
].forEach(field => {
this.$refs.driverForm?.validateField(field);
});
});
},
applyIdCardValidity(data = {}) {
const validStart =
data.validDateStart ||
data.idCardValidStart ||
this.getOcrValue(data, ['签发日期', '有效起始日期', '有效期限起', '身份证_签发日期']);
const validEndRaw =
data.validDateEnd ||
data.idCardValidEnd ||
this.getOcrValue(data, [
'失效日期',
'有效期限',
'有效期限止',
'身份证_有效期限',
'身份证_失效日期',
]);
const longTerm = data.longTerm ?? data.idCardLongTerm;
const validEndText = String(validEndRaw || '').trim();
if (longTerm === 1 || longTerm === '1' || /长期|永久/.test(validEndText)) {
this.driverForm.idCardLongTerm = 1;
this.driverForm.idCardValidEnd = '';
} else if (validEndText) {
const endDate = this.normalizeBirthday(validEndText);
if (endDate) {
this.driverForm.idCardValidEnd = endDate;
this.driverForm.idCardLongTerm = 0;
}
}
if (validStart) {
const startDate = this.normalizeBirthday(validStart);
if (startDate) {
this.driverForm.idCardValidStart = startDate;
}
}
},
getOcrValue(data = {}, keys = []) {
const keyInfos = data.keyInfos || data.keyInfo || data.ocrKeyInfos || [];
const list = Array.isArray(keyInfos) ? keyInfos : [];
@@ -1675,6 +1767,8 @@ export default {
const row = {
...this.driverForm,
birthday: this.normalizeBirthday(this.driverForm.birthday),
idCardValidStart: this.normalizeBirthday(this.driverForm.idCardValidStart),
idCardValidEnd: this.normalizeBirthday(this.driverForm.idCardValidEnd),
drivingLicenseStartDate: this.normalizeBirthday(this.driverForm.drivingLicenseStartDate),
drivingLicenseEndDate: this.normalizeBirthday(this.driverForm.drivingLicenseEndDate),
qualificationStartDate: this.normalizeBirthday(this.driverForm.qualificationStartDate),
+76 -16
View File
@@ -49,21 +49,24 @@
</template>
<template #customerRisk="{ row }">
<el-tooltip
v-if="customerRowRisky(row)"
v-if="customerRowRiskName(row) && customerRowRiskName(row) !== '无风险'"
placement="top"
effect="dark"
popper-class="customer-risk-popper"
>
<template #content>
<div>{{ row.hcsRiskDetail || '该客商为高风险/黑名单客商' }}</div>
<div>{{ row.hcsRiskDetail || customerRowRiskName(row) }}</div>
</template>
<el-tag type="danger" class="status-text">
<el-tag
:type="customerRowRisky(row) ? 'danger' : row.hcsRiskLevel === '1' ? 'warning' : 'success'"
class="status-text"
>
{{ customerRowRiskName(row) }}
</el-tag>
</el-tooltip>
<el-tag
v-else-if="customerRowRiskName(row)"
:type="row.hcsRiskLevel === '1' ? 'warning' : 'success'"
type="success"
class="status-text"
>
{{ customerRowRiskName(row) }}
@@ -1097,8 +1100,18 @@
<el-form-item label="开户人姓名" prop="accountHolderName">
<el-input v-model="receiptForm.accountHolderName" maxlength="50" />
</el-form-item>
<el-form-item label="开户行" prop="bankName">
<el-input v-model="receiptForm.bankName" maxlength="100" />
<el-form-item prop="bankName">
<template #label>
<span class="receipt-bank-label">
开户行
<el-tooltip placement="top" effect="dark" content="请填写银行全称,系统将自动匹配金蝶档案带出联行号">
<el-icon class="receipt-bank-help">
<el-icon-question-filled />
</el-icon>
</el-tooltip>
</span>
</template>
<el-input v-model="receiptForm.bankName" maxlength="100" @blur="fetchReceiptCnapsCode" />
</el-form-item>
<el-form-item label="联行号" prop="cnapsCode">
<el-input v-model="receiptForm.cnapsCode" maxlength="32" />
@@ -1605,6 +1618,8 @@ import {
remove,
getScoreTemplate,
recognizeBusinessLicenseOcr,
queryKingdeeBank,
queryKingdeeBankByNumber,
queryKingdeeCustomer,
querySupplierCode,
syncKingdee,
@@ -3654,18 +3669,50 @@ export default {
this.receiptForm = this.emptyReceiptAccount();
this.$refs.receiptForm?.clearValidate();
},
saveReceipt() {
this.$refs.receiptForm.validate(valid => {
if (!valid) return;
const receipt = this.normalizeReceipt(this.receiptForm);
if (this.receiptIndex > -1) {
this.archiveForm.receiptAccounts.splice(this.receiptIndex, 1, receipt);
// 开户行失焦后按行名精确匹配金蝶行名行号档案(A905-3),命中自动带出联行号,未命中提示检查
async fetchReceiptCnapsCode() {
const bankName = String(this.receiptForm.bankName || '').trim();
if (!bankName) return;
try {
const res = await queryKingdeeBank(bankName);
// 防止响应返回时用户已修改开户行
if (String(this.receiptForm.bankName || '').trim() !== bankName) return;
const bank = res?.data?.data || null;
if (bank && bank.unionNumber) {
this.receiptForm.cnapsCode = bank.unionNumber;
this.$refs.receiptForm?.clearValidate('cnapsCode');
} else {
this.archiveForm.receiptAccounts.push(receipt);
this.handleDetailCurrentChange('receipt', this.getDetailPageCount('receipt'));
this.$message.warning('未查询到金蝶开户行信息,请检查开户行名称');
}
this.receiptBox = false;
});
} catch (error) {
// 忽略查询异常,联行号留空由用户手填
}
},
async saveReceipt() {
const valid = await this.$refs.receiptForm.validate().catch(() => false);
if (!valid) return;
// 保存前按联行号精确校验金蝶行名行号档案(A905-3),查不到不允许保存
const cnapsCode = String(this.receiptForm.cnapsCode || '').trim();
let bank = null;
try {
const res = await queryKingdeeBankByNumber(cnapsCode);
bank = res?.data?.data || null;
} catch (error) {
this.$message.warning('金蝶联行号校验失败,请稍后重试');
return;
}
if (!bank || !bank.unionNumber) {
this.$message.warning('未查询到金蝶联行号信息,不能保存,请检查联行号');
return;
}
const receipt = this.normalizeReceipt(this.receiptForm);
if (this.receiptIndex > -1) {
this.archiveForm.receiptAccounts.splice(this.receiptIndex, 1, receipt);
} else {
this.archiveForm.receiptAccounts.push(receipt);
this.handleDetailCurrentChange('receipt', this.getDetailPageCount('receipt'));
}
this.receiptBox = false;
},
deleteReceipt(index) {
this.$confirm('确定删除该收款信息?', '提示', {
@@ -6124,6 +6171,19 @@ export default {
:deep(.el-select) {
width: 60%;
}
.receipt-bank-label {
display: inline-flex;
align-items: center;
gap: 4px;
}
.receipt-bank-help {
color: #a8abb2;
cursor: help;
font-size: 14px;
flex: none;
}
}
.receipt-dialog__footer-actions {
+25
View File
@@ -179,6 +179,14 @@ const createTimeRangeMap = {
createTimeRange: ['createTimeStart', 'createTimeEnd'],
};
/** 关联异常列:一级 / 二级(如「车辆异常 / 车辆故障维修」),无关联显示 — */
function formatExceptionSource(row) {
const parts = [row?.exceptionType, row?.exceptionReason].filter(
item => item !== undefined && item !== null && String(item).trim() !== ''
);
return parts.length ? parts.join(' / ') : '—';
}
const exportColumns = [
{ prop: 'vehicleType', label: '车船类型' },
{ prop: 'vehicleNo', label: '车牌号/船号' },
@@ -284,6 +292,23 @@ export default {
{ max: 30, message: '最多 30 个字符', trigger: 'change' },
],
},
// 关联信息只读展示:由异常处置录入的记录带出运单与异常类型,PC 新增/编辑不产生关联
{
label: '关联运单',
prop: 'waybillNo',
minWidth: 150,
addDisplay: false,
editDisplay: false,
formatter: row => row.waybillNo || '—',
},
{
label: '关联异常',
prop: 'exceptionReason',
minWidth: 180,
addDisplay: false,
editDisplay: false,
formatter: formatExceptionSource,
},
{
label: '维修人',
prop: 'maintainer',