Compare commits
283 Commits
master
...
frontend_1
| Author | SHA1 | Date |
|---|---|---|
|
|
70332d353c | |
|
|
c81faba391 | |
|
|
a14993e1b8 | |
|
|
86fe86d72d | |
|
|
9cc5c64fee | |
|
|
054c0a1789 | |
|
|
d4e33c4fd3 | |
|
|
467e7f74d5 | |
|
|
73cacea552 | |
|
|
a249239e77 | |
|
|
415b9b3e46 | |
|
|
625519c2c2 | |
|
|
6e6d29511c | |
|
|
43fc48a3a5 | |
|
|
87a3901694 | |
|
|
2d5cffb78e | |
|
|
0b693760df | |
|
|
a9ed35793a | |
|
|
0106da0c29 | |
|
|
2c6547f5ec | |
|
|
4e4792459c | |
|
|
aba73af268 | |
|
|
34e9047763 | |
|
|
e3d77525dd | |
|
|
818454311f | |
|
|
4d911c93ae | |
|
|
934b2990f1 | |
|
|
6d4923ac8b | |
|
|
2913740e36 | |
|
|
3883b75b3b | |
|
|
300c1f63b2 | |
|
|
4b2e8538b1 | |
|
|
cabdd4c531 | |
|
|
3d94027618 | |
|
|
972d121580 | |
|
|
239009a59b | |
|
|
4bd4fae390 | |
|
|
40f40baf5e | |
|
|
c10a81f271 | |
|
|
f3f7a99b10 | |
|
|
09fb371292 | |
|
|
61ba3c9680 | |
|
|
d0d6919b11 | |
|
|
9ee56a83a7 | |
|
|
954be37aa4 | |
|
|
79010a0f36 | |
|
|
134ca89b9a | |
|
|
eb1f4fb7b3 | |
|
|
348eb137a8 | |
|
|
c440cb0357 | |
|
|
a5b4446202 | |
|
|
d55fc11e24 | |
|
|
3b84d8d87c | |
|
|
1545771196 | |
|
|
2aaccc1193 | |
|
|
0c451fcbed | |
|
|
3b41b6b3d3 | |
|
|
015ba6da70 | |
|
|
c246975914 | |
|
|
8c2ecc89c8 | |
|
|
5fb2faecdc | |
|
|
671bbd5692 | |
|
|
8acc68141d | |
|
|
71e8eb1e37 | |
|
|
7597a444b9 | |
|
|
d01a1faa1a | |
|
|
c27bfd26cf | |
|
|
11cdcdb83c | |
|
|
802b169818 | |
|
|
341000a3b0 | |
|
|
a1748ed05b | |
|
|
1c601a908e | |
|
|
8bab6d7d17 | |
|
|
02f4c47383 | |
|
|
ea52bec18d | |
|
|
3584c72284 | |
|
|
ee84487fe0 | |
|
|
8aa2b92fa6 | |
|
|
6bb9738dc5 | |
|
|
e992a62a5b | |
|
|
5749d20c80 | |
|
|
a7e4d9c1f4 | |
|
|
f68242d2e7 | |
|
|
98b9097ff2 | |
|
|
475229295a | |
|
|
73a7f6d6ca | |
|
|
2fdbafa0c3 | |
|
|
d9effe68c0 | |
|
|
00f91b394a | |
|
|
1e96076576 | |
|
|
66d315069c | |
|
|
af74c4033d | |
|
|
1aa1e592af | |
|
|
cca2215a60 | |
|
|
626d1f1b09 | |
|
|
d6b41e7cb2 | |
|
|
8be71600a2 | |
|
|
1735901ffe | |
|
|
27ac07ad0a | |
|
|
c2bbeb7cdc | |
|
|
4bdbc1daf7 | |
|
|
ae727d0fdd | |
|
|
515abca80f | |
|
|
773f400add | |
|
|
b274c5e4af | |
|
|
af6d748b86 | |
|
|
fd5a2e60e4 | |
|
|
ce4044bf8c | |
|
|
53b84e0c9b | |
|
|
c410d4e2e3 | |
|
|
f7516a9ffe | |
|
|
778f855ee9 | |
|
|
feb99bb10e | |
|
|
b7067082ed | |
|
|
9a8fe13f28 | |
|
|
4ac353a83a | |
|
|
7f39e3029f | |
|
|
086af70f22 | |
|
|
0e96989aaa | |
|
|
9fd0d3caf4 | |
|
|
6328264f90 | |
|
|
2ebd64ab41 | |
|
|
6a26d412c4 | |
|
|
504379f3e3 | |
|
|
81c561d28a | |
|
|
8784a143f1 | |
|
|
9439b82081 | |
|
|
e036fe7e10 | |
|
|
7ffd5135cc | |
|
|
f03fc06949 | |
|
|
4d8e4fa01d | |
|
|
980a8147ce | |
|
|
60a9bdab2e | |
|
|
e9c73d1925 | |
|
|
6f3b51f461 | |
|
|
5eec808514 | |
|
|
9e8edf01b8 | |
|
|
baae63153a | |
|
|
9a491ab1c7 | |
|
|
1f86f382df | |
|
|
7a7b549de3 | |
|
|
9b90d44e34 | |
|
|
c66bdbcc74 | |
|
|
4b7021b45e | |
|
|
3d2266f4a1 | |
|
|
2fae492f2b | |
|
|
5ac798c295 | |
|
|
61143a39f7 | |
|
|
9d067ab748 | |
|
|
1860ac322e | |
|
|
50855a68ce | |
|
|
53d900ca22 | |
|
|
6280850e30 | |
|
|
96ede9b575 | |
|
|
a7e0598951 | |
|
|
487bf5e0c6 | |
|
|
93053025a2 | |
|
|
5409a3c225 | |
|
|
c39ed025b9 | |
|
|
af221e4486 | |
|
|
0f656cfed6 | |
|
|
6b62162e8d | |
|
|
2373b13fc6 | |
|
|
4cc420799e | |
|
|
48bad8a9a6 | |
|
|
adae1ab595 | |
|
|
7bcc08ff0e | |
|
|
6b9263ca1b | |
|
|
8abca803f9 | |
|
|
983780b73b | |
|
|
04b59e281a | |
|
|
9f11895847 | |
|
|
d1910e2497 | |
|
|
268c56e7ce | |
|
|
e251f3fccb | |
|
|
abcd3c1c6f | |
|
|
4b8cd8b9e8 | |
|
|
aff3016e4f | |
|
|
115394106a | |
|
|
ddcde9e1da | |
|
|
6f072b97c9 | |
|
|
ab03f70fd6 | |
|
|
c759c8bc55 | |
|
|
e298748d45 | |
|
|
e04b80ff25 | |
|
|
830fde4bc0 | |
|
|
7ff9552621 | |
|
|
6776fe3f0d | |
|
|
e2f9e0e393 | |
|
|
7d8ad420e4 | |
|
|
356fc1995e | |
|
|
d9da314968 | |
|
|
3951ef325d | |
|
|
a7f7a29d74 | |
|
|
50ee54d6ef | |
|
|
701d90779a | |
|
|
61b10eba26 | |
|
|
45f69d527e | |
|
|
a56d750f2d | |
|
|
aa2efbd42e | |
|
|
6bed84cf3e | |
|
|
330f2f7d54 | |
|
|
b96d006f9c | |
|
|
baa4b52553 | |
|
|
dd2a7d99b6 | |
|
|
e1643a6331 | |
|
|
5f6f014226 | |
|
|
b3e5568216 | |
|
|
cc29006013 | |
|
|
db343353cc | |
|
|
01cd03213e | |
|
|
31a8de5569 | |
|
|
ec3e34ad21 | |
|
|
0d14477705 | |
|
|
74ed165f71 | |
|
|
f2cbf8f9cb | |
|
|
5485a827be | |
|
|
0dfdeee19d | |
|
|
d1e23c58ef | |
|
|
7a6abccf2f | |
|
|
602d06aafc | |
|
|
59b045d457 | |
|
|
97bcc1ac0a | |
|
|
b1cbbec237 | |
|
|
4ce68abe84 | |
|
|
3102a2c0a0 | |
|
|
357410c3e7 | |
|
|
a4ce1ba45a | |
|
|
7db1b6ed37 | |
|
|
4238f345cb | |
|
|
caec1d4bd0 | |
|
|
695c77ca60 | |
|
|
f0294212b2 | |
|
|
f992b2b29e | |
|
|
5583bb0f11 | |
|
|
47879d029a | |
|
|
5ead2c13b2 | |
|
|
389deac133 | |
|
|
b21c54a901 | |
|
|
e767c6aa50 | |
|
|
8ea80a4dbe | |
|
|
fb3f3c721e | |
|
|
d3bbf2da62 | |
|
|
693899f45c | |
|
|
fe2791f815 | |
|
|
54e7d394bb | |
|
|
6ec773ce3d | |
|
|
0774e81fd0 | |
|
|
1111d285c5 | |
|
|
c257cdc5cd | |
|
|
1d85e92997 | |
|
|
e7ed169f94 | |
|
|
9be3b62aaf | |
|
|
6c3f344b92 | |
|
|
ed90f7ce87 | |
|
|
f79150abc9 | |
|
|
8d6ca0b64f | |
|
|
6613b3612c | |
|
|
af12674d7b | |
|
|
7c46ae5db4 | |
|
|
56d79e96b6 | |
|
|
995f7f8b03 | |
|
|
2cf2fdff08 | |
|
|
dc1f5f7302 | |
|
|
49cd27c221 | |
|
|
a4a93ee484 | |
|
|
c940880a9d | |
|
|
3e45254fc1 | |
|
|
a19909d1bf | |
|
|
2f51b56298 | |
|
|
4cc2d3beb1 | |
|
|
ed9fda7fe3 | |
|
|
bd830115d4 | |
|
|
a9142c0e1b | |
|
|
d3c4776bab | |
|
|
a1ea52a934 | |
|
|
05865dc2ef | |
|
|
30d551fe59 | |
|
|
333b5e1a8e | |
|
|
94348aebc4 | |
|
|
1d842d3bf2 | |
|
|
1bdc88cd7c | |
|
|
1a4f64865a |
|
|
@ -35,6 +35,8 @@ nbbuild/
|
||||||
dist/
|
dist/
|
||||||
nbdist/
|
nbdist/
|
||||||
.nb-gradle/
|
.nb-gradle/
|
||||||
|
.DS_Store
|
||||||
|
*.png
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Others
|
# Others
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
# 2026-09-08
|
||||||
|
|
||||||
|
## 代码 review(两轮)
|
||||||
|
|
||||||
|
用户要求只 review 不改代码。范围:工作区 53 个改动文件(Java 后端 + Vue 前端)。
|
||||||
|
|
||||||
|
### 改动主体
|
||||||
|
- Excel 注解新增 `decimalFormat` 字段(Excel.java + ExcelUtil.java),21 个 domain 的 BigDecimal 字段加 `@Excel(scale=2, decimalFormat="#,##0.00")`,仅影响导出显示,安全。
|
||||||
|
- 前端多处 `toFixed(2)` → 全局 `formatCurrency`(main.js 已注册 Vue.prototype),安全。
|
||||||
|
- ProductInfo/Mapper/前端新增 `pre_system_type`(预装系统类型)字段,前后端配套完整,配套 SQL:`sql/20290904update.sql`(文件名年份疑为笔误,写的是 2029)。
|
||||||
|
- `OmsInventoryInnerServiceImpl.insertOmsInventoryInner` 应付单生成逻辑重构(核心风险点)。
|
||||||
|
|
||||||
|
### 第一轮发现的问题 → 用户修复后第二轮复核结果
|
||||||
|
1. NPE(quantity/price 为 null 时直接 multiply)→ 已修:`usePurchaseOrder` 条件含 null 检查,null 时回退原公式 ✓
|
||||||
|
2. 重复 setTaxAmount(新旧两行并存)→ 已清理,只留正确行(含税-未税)✓
|
||||||
|
3. vendorInfo 为 null 时 getPayConfigDay() NPE → 已修,传 0 ✓
|
||||||
|
4. 遗留:`vendorInfo != null ? vendorInfo.getPayConfigDay() : 0` 三元混合 Integer/int,payConfigDay 为 null 时仍会拆箱 NPE(原代码同样会炸,非回归)
|
||||||
|
5. 遗留:ExcelUtil 每单元格 createCellStyle,POI 上限 64000 样式,大数据量导出会失败
|
||||||
|
6. 遗留:OrderList.java 文件末尾无换行;oms_web/.DS_Store 未跟踪垃圾文件
|
||||||
|
|
||||||
|
### 业务逻辑一致性结论(H3C 判定)
|
||||||
|
- 新华三(isH3C=true,startsWith("新华三")):条件退化为原条件,金额公式走原逻辑,与原 100% 一致
|
||||||
|
- 非新华三无采购单:innerComplete 默认 true,与原一致
|
||||||
|
### 第三轮复核(用户又修复了一批)
|
||||||
|
1. 三元拆箱 NPE → 已修:`vendorInfo != null && vendorInfo.getPayConfigDay() != null ? ... : 0` ✓
|
||||||
|
2. ExcelUtil CellStyle → 已修:新增 `moneyStyles` Map 缓存(按 decimalFormat 键),HashMap 已导入,ExcelUtil 每次 export 新建实例,缓存作用域正确 ✓
|
||||||
|
3. OrderList.java 末尾换行 → 已补 ✓
|
||||||
|
仍遗留(非代码 bug,提交时注意):isH3C 口径需业务确认(startsWith("新华三"),基于 ProductInfo.vendorName);sql/20290904update.sql 文件名年份笔误(2029);oms_web/.DS_Store 与 ruoyi-admin/.DS_Store 垃圾文件。
|
||||||
|
至此所有代码级问题均已修复,H3C/无采购单场景与原逻辑一致,非新华三+有采购单为有意业务变更。
|
||||||
|
|
||||||
|
### 金额格式统一专项检查(第四轮,只查不改)
|
||||||
|
**统一现状**:全局 formatCurrency(utils/index.js:17,main.js:46 注册,null→'0.00',toLocaleString en-US 2位小数);本次 diff 新增 72 处 formatCurrency(27 个文件);后端 21 个 domain 106 个 @Excel 字段加 decimalFormat。
|
||||||
|
|
||||||
|
**后端 Excel 遗漏**:OmsTicketBill.java:70 taxAmount(税额,活动注解,其余单据均已加)。比率类(taxRate×4、planRate×2、invoiceRate、receiptRate)未加属合理;折扣类(OrderList.discount、ProjectOrderConfigInfo.guidanceDiscount/discount)需确认语义。
|
||||||
|
|
||||||
|
**前端遗漏**:ApplyInvoice.vue:122/129/143 v-else 分支裸显示 unitPrice/amount/taxAmount;writeoff 两 index 的 $modal 提示文案 5 处无千分位(轻微);formatCurrency(x.toFixed(2)) 双重格式化 ~12 处(冗余无害)。
|
||||||
|
|
||||||
|
**体系性发现**:项目存在 7+ 处组件局部 formatAmount/formatAmountNumber 副本(均为存量,非本次新增):purchaseorder/index.vue:589、orderFinance.vue:377(filters)、base/quotation/index.vue:535、ProductConfig.vue:497、approve/all/index.vue:1295(用zh-CN)、projectTransfer.vue:293、project/info/index.vue:491 等。差异:局部版 null→'',全局版 null→'0.00'。后续统一建议收敛到全局 formatCurrency,先定 null 显示口径。
|
||||||
|
|
||||||
|
**formatCurrency 边界**:非数字字符串入参会显示 "NaN"(Number('abc').toLocaleString → "NaN"),建议加 Number.isNaN 保护。convertCurrency(大写金额)依赖 toFixed 字符串,InvoiceInfoView:115 / ApplyInvoice:278 / invoiceExcelUtils.js:147 的 toFixed(2) 必须保留,不是遗漏。
|
||||||
|
|
||||||
|
### 第五轮:全量改动集成测试(mvn 编译 + vue 解析)
|
||||||
|
**Maven 全量编译**(JAVA_HOME=Corretto 8):7 模块 SUCCESS,0 ERROR。Lombok、@Excel 注解、mapper SQL、Service 实现全部对齐。
|
||||||
|
**前端 31 文件 SFC/JS 解析**:用 @vue/compiler-sfc 批量 parse,0 失败(PaymentRefundDetail、ReceiptDetail、ReceivableInvoiceDetail、ApplyInvoice、InvoiceInfoView、EditForm×2、AddForm、receipt/index、writeoff 6 个文件、inventory/inner、ProductConfig、ProjectForm、project/info/index、OrderDetail、PurchaseOrderDetail、PurchaseOrderDetailView、PurchaseOrderSelectDialog、vendorConfirm、system/product、approve/purchaase×2、selectQuotation、projectTransfer、invoiceExcelUtils.js)。
|
||||||
|
|
||||||
|
**本轮新增发现的位置**(前几轮未深查):
|
||||||
|
- ruoyi-admin/logback.xml:把 jiliu 路径换成 kangwenjing 个人本地路径,纯个人配置切换,不影响代码(提交前建议恢复或加 .gitignore 排除)。
|
||||||
|
- dto/InventoryInfoExcelDto.java、dto/warehouse/WarehouseInnerExcelDto.java:innerPrice 加 decimalFormat,已正确 ✓
|
||||||
|
- approve/purchaase/{approve,approved}/index.vue:含税总金额列加 formatter ✓
|
||||||
|
- base/quotation/selectQuotation.vue:discountAmount 加 formatter,字段名语义为"报价金额"(QuotationInfo 表的折扣后金额),没问题 ✓
|
||||||
|
- system/product/index.vue:queryParams 已在 data() 初始声明 preSystemType: null ✓;resetForm 依赖表单模板自动重置 ✓;handleTypeChange 在切换非硬件时清空 preSystemType ✓
|
||||||
|
- OmsTicketBill.java:70 税额仍**漏** decimalFormat(连续两轮没修)。
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
# 2026-09-09 工作日志
|
||||||
|
|
||||||
|
## 全量改动复核 + 集成测试(不动代码)
|
||||||
|
|
||||||
|
**改动规模**:65 文件、+427 / -283 行
|
||||||
|
|
||||||
|
**集成测试结果**:
|
||||||
|
- 后端:`mvn compile -DskipTests`(JAVA_HOME=Corretto 1.8.0_442) → **BUILD SUCCESS**,7 模块 0 ERROR
|
||||||
|
- 前端:31 个 Vue/JS 文件用 `@vue/compiler-sfc` 解析 → **31 ok, 0 fail**
|
||||||
|
|
||||||
|
**本轮发现**:
|
||||||
|
1. `OmsTicketBill.java:70` 税额已加 `decimalFormat = "#,##0.00"`(用户修复)
|
||||||
|
2. SQL 文件名 `20290904` → `20260904`(已改),但文件内注释仍写 `2029-09-04`
|
||||||
|
3. `.gitignore` 已加 `.DS_Store`(用户修复)
|
||||||
|
4. `ruoyi-admin/.DS_Store` 仍被 `git ls-files` 跟踪,需 `git rm --cached` 才能彻底忽略
|
||||||
|
|
||||||
|
**确认正确的覆盖**:
|
||||||
|
- `inventory/inner/index.vue`、`vendorConfirm.vue` — 本次新加 formatter 没问题
|
||||||
|
- `InventoryInfoExcelDto.java`、`WarehouseInnerExcelDto.java` — 入库价已加 decimalFormat
|
||||||
|
- `ProductInfo.java` + `ProductInfoMapper.xml` — preSystemType 字段前后端完整配套
|
||||||
|
- `OmsInventoryInnerServiceImpl.java` — 三处修复(NPE、payConfigDay 拆箱、税金额)均正确
|
||||||
|
|
||||||
|
**仍未修的小遗留**:
|
||||||
|
- `ApplyInvoice.vue:122/129/143` v-else 裸显示(用户上一轮已知,影响小)
|
||||||
|
- 14 处 `formatCurrency(x.toFixed(2))` 双重格式化(冗余但正确)
|
||||||
|
- 7+ 处局部 `formatAmount/formatAmountNumber` 副本(存量代码,本次未改)
|
||||||
|
|
||||||
|
**体系性问题(与本次改动范围无关)**:
|
||||||
|
- 项目里 7+ 处组件局部的 `formatAmount` 副本,行为与全局 `formatCurrency` 略有差异(null 显示空 vs '0.00'),建议后续收敛
|
||||||
|
- `formatCurrency('abc')` 会显示 "NaN",建议加 `Number.isNaN` 保护
|
||||||
|
|
||||||
|
**提交前清单**:
|
||||||
|
- [ ] 修 `sql/20260904update.sql` 注释里的 `2029-09-04` → `2026-09-04`
|
||||||
|
- [ ] `git rm --cached ruoyi-admin/.DS_Store`(.gitignore 已加,但已跟踪文件需手动移除)
|
||||||
|
- [ ] 可选:`git checkout ruoyi-admin/src/main/resources/logback.xml` 恢复个人本地路径
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,70 @@
|
||||||
|
-- =====================================================================
|
||||||
|
-- MCP 只读数据工具 —— 索引脚本(对应方案第 6 章 / 16.4)
|
||||||
|
-- 适用:MySQL 8.0+
|
||||||
|
--
|
||||||
|
-- 【2026-09-23 实测更新】在 oms_test 上核对 information_schema 后发现:
|
||||||
|
-- • P0-1 oms_inventory_outer.idx_outer_code(outer_code) —— 已存在,无需执行
|
||||||
|
-- • P0-2 oms_inventory_outer_detail.idx_outer_code(outer_code) —— 已存在,无需执行
|
||||||
|
-- • P1-1 oms_inventory_inner.idx_order_code(order_code) —— 已存在,无需执行
|
||||||
|
-- • P1-2 oms_purchase_order.idx_vendor_id(vendor_id) —— 已存在,无需执行
|
||||||
|
-- • P1-3 oms_inventory_info 覆盖索引 —— 缺失,已在 oms_test 执行并实测收益
|
||||||
|
-- (建索引耗时 1.3s;按产品聚合 141.8ms → 66.6ms;EXPLAIN 显示 Using index,免回表)
|
||||||
|
--
|
||||||
|
-- 因此本脚本默认全部注释,仅作为「生产执行前的核对与补建」清单使用。
|
||||||
|
-- 执行前请先跑文末的核对语句,确认目标索引是否缺失。
|
||||||
|
-- =====================================================================
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- P0 必加(inventory_flow 出库链路依赖;若缺失将导致按出库单号全表扫描)
|
||||||
|
-- 实测:oms_test 已存在
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- ALTER TABLE oms_inventory_outer ADD INDEX idx_outer_code (outer_code), ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
-- ALTER TABLE oms_inventory_outer_detail ADD INDEX idx_outer_code (outer_code), ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- P1 建议
|
||||||
|
-- 实测:P1-1 / P1-2 在 oms_test 已存在
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- ALTER TABLE oms_inventory_inner ADD INDEX idx_order_code (order_code), ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
-- ALTER TABLE oms_purchase_order ADD INDEX idx_vendor_id (vendor_id), ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- P1-3 覆盖索引(库存聚合免回表)
|
||||||
|
-- 实测收益显著;但存在写入放大(每次 insert/update 需维护 4 列索引),
|
||||||
|
-- 生产库请由 DBA 结合写入量评估后再执行;回滚语句见文末。
|
||||||
|
-- oms_test 已于 2026-09-23 执行。
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- ALTER TABLE oms_inventory_info
|
||||||
|
-- ADD INDEX idx_pc_status_amt (product_code, inventory_status, inner_price, outer_price),
|
||||||
|
-- ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- P2 按表规模触发(当前数据量下无需执行;表增长到 10 万+ 或成为热点时再补)
|
||||||
|
-- 完整编号清单见方案 6.3 节(P2-1 ~ P2-26,其中 P2-3/P2-4/P2-19 已作废)
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- ALTER TABLE oms_purchase_order_map ADD INDEX idx_order_id (order_id), ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
-- ALTER TABLE oms_purchase_order_map ADD INDEX idx_purchase_id (purchase_id), ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
-- ALTER TABLE oms_inventory_info ADD INDEX idx_warehouse_id (warehouse_id), ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
-- ALTER TABLE oms_stock_info ADD INDEX idx_order_code (order_code), ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
-- ALTER TABLE oms_receivable_receipt_detail ADD INDEX idx_write_off_id (write_off_id), ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
-- ALTER TABLE oms_payable_payment_detail ADD INDEX idx_write_off_id (write_off_id), ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
-- ALTER TABLE bu_todo_completed ADD INDEX idx_approve_user_time (approve_user, approve_time), ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
|
||||||
|
-- =====================================================================
|
||||||
|
-- 执行前核对:列出目标表当前索引(若目标索引已出现则无需再建)
|
||||||
|
-- =====================================================================
|
||||||
|
-- SELECT table_name, index_name, GROUP_CONCAT(column_name ORDER BY seq_in_index) AS cols
|
||||||
|
-- FROM information_schema.statistics
|
||||||
|
-- WHERE table_schema = DATABASE()
|
||||||
|
-- AND table_name IN ('oms_inventory_outer','oms_inventory_outer_detail','oms_inventory_inner',
|
||||||
|
-- 'oms_purchase_order','oms_inventory_info')
|
||||||
|
-- GROUP BY table_name, index_name;
|
||||||
|
|
||||||
|
-- =====================================================================
|
||||||
|
-- 回滚(仅在需要撤销新增索引时执行)
|
||||||
|
-- =====================================================================
|
||||||
|
-- ALTER TABLE oms_inventory_outer DROP INDEX idx_outer_code, ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
-- ALTER TABLE oms_inventory_outer_detail DROP INDEX idx_outer_code, ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
-- ALTER TABLE oms_inventory_inner DROP INDEX idx_order_code, ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
-- ALTER TABLE oms_purchase_order DROP INDEX idx_vendor_id, ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
-- ALTER TABLE oms_inventory_info DROP INDEX idx_pc_status_amt, ALGORITHM=INPLACE, LOCK=NONE;
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
你是一个资深 Java 后端工程师,请在当前 Spring Boot(Java8)项目中实现一个完整的 MCP Server,并加入“AI 自动选 Tool(RAG + Tool Routing)”能力。
|
||||||
|
|
||||||
|
# 一、基础 MCP 能力(必须实现)
|
||||||
|
|
||||||
|
1. 提供统一入口:
|
||||||
|
POST /mcp
|
||||||
|
|
||||||
|
2. 实现 JSON-RPC 2.0:
|
||||||
|
|
||||||
|
* initialize
|
||||||
|
* tools/list
|
||||||
|
* tools/call
|
||||||
|
|
||||||
|
3. 返回格式:
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": "...",
|
||||||
|
"result": ...
|
||||||
|
}
|
||||||
|
|
||||||
|
# 二、流式能力(必须)
|
||||||
|
|
||||||
|
1. 禁止使用 SseEmitter
|
||||||
|
2. 使用 HttpServletResponse + OutputStream
|
||||||
|
3. 实现 chunked streaming(write + flush)
|
||||||
|
|
||||||
|
# 三、Tool 注册中心(必须)
|
||||||
|
|
||||||
|
实现:
|
||||||
|
|
||||||
|
* McpToolRegistry
|
||||||
|
* register / get / list
|
||||||
|
|
||||||
|
Tool结构:
|
||||||
|
|
||||||
|
* name
|
||||||
|
* description(非常重要,用于向量检索)
|
||||||
|
* inputSchema
|
||||||
|
* handler
|
||||||
|
|
||||||
|
# 四、RAG Tool 检索模块(新增重点)
|
||||||
|
|
||||||
|
实现一个 ToolRetriever 组件:
|
||||||
|
|
||||||
|
1. 构建向量索引(简化实现):
|
||||||
|
|
||||||
|
* 使用内存存储 List<ToolEmbedding>
|
||||||
|
* ToolEmbedding:
|
||||||
|
|
||||||
|
* toolName
|
||||||
|
* description
|
||||||
|
* embedding(double[])
|
||||||
|
|
||||||
|
2. 提供方法:
|
||||||
|
List<McpTool> retrieve(String query, int topK)
|
||||||
|
|
||||||
|
3. embedding 实现(简化):
|
||||||
|
|
||||||
|
* 可使用:
|
||||||
|
|
||||||
|
* 简单 TF-IDF
|
||||||
|
* 或 mock embedding(字符串相似度)
|
||||||
|
* 不依赖外部服务(保证可运行)
|
||||||
|
|
||||||
|
# 五、Tool Router(核心)
|
||||||
|
|
||||||
|
实现 ToolRouter:
|
||||||
|
|
||||||
|
流程:
|
||||||
|
|
||||||
|
1. 接收用户问题 query
|
||||||
|
2. 调用 ToolRetriever → 获取 topK tools
|
||||||
|
3. 构建 prompt(只包含候选 tools)
|
||||||
|
4. 返回候选 tools 给上层(或直接选)
|
||||||
|
|
||||||
|
要求:
|
||||||
|
|
||||||
|
* 不允许返回全部 tools
|
||||||
|
* 默认 topK = 3~5
|
||||||
|
|
||||||
|
# 六、tools/call 升级(关键)
|
||||||
|
|
||||||
|
当 method = tools/call 时:
|
||||||
|
|
||||||
|
1. 如果请求未指定 tool:
|
||||||
|
|
||||||
|
* 自动触发 ToolRouter
|
||||||
|
* 选出最优 tool
|
||||||
|
|
||||||
|
2. 如果指定 tool:
|
||||||
|
|
||||||
|
* 直接执行
|
||||||
|
|
||||||
|
# 七、默认内置工具
|
||||||
|
|
||||||
|
实现:
|
||||||
|
|
||||||
|
1. hello
|
||||||
|
输入:name
|
||||||
|
输出:hello xxx
|
||||||
|
|
||||||
|
2. meeting.summary
|
||||||
|
输入:text
|
||||||
|
输出:模拟总结
|
||||||
|
|
||||||
|
# 八、架构分层(必须)
|
||||||
|
|
||||||
|
* controller
|
||||||
|
* service
|
||||||
|
* registry
|
||||||
|
* retriever(新增)
|
||||||
|
* router(新增)
|
||||||
|
|
||||||
|
# 九、扩展能力(必须预留)
|
||||||
|
|
||||||
|
1. 支持替换为真实向量数据库(如 Milvus / ES)
|
||||||
|
2. 支持 embedding API(OpenAI / 本地模型)
|
||||||
|
3. 支持多 MCP Server(未来扩展)
|
||||||
|
|
||||||
|
# 十、输出要求
|
||||||
|
|
||||||
|
1. 输出完整代码:
|
||||||
|
|
||||||
|
* McpController
|
||||||
|
* McpService
|
||||||
|
* McpToolRegistry
|
||||||
|
* ToolRetriever
|
||||||
|
* ToolRouter
|
||||||
|
* ToolEmbedding
|
||||||
|
* ToolInitializer
|
||||||
|
* McpRequest
|
||||||
|
|
||||||
|
2. 代码必须:
|
||||||
|
|
||||||
|
* 可运行
|
||||||
|
* 无伪代码
|
||||||
|
* 包含必要注释
|
||||||
|
* 代码生成目录在ruoyi-sip/src/main/java/com/ruoyi/sip/llm
|
||||||
|
3. 不要解释,只输出代码
|
||||||
|
|
@ -6,3 +6,4 @@ ENV = 'production'
|
||||||
|
|
||||||
# 若依管理系统/生产环境
|
# 若依管理系统/生产环境
|
||||||
VUE_APP_BASE_API = '/prod-api'
|
VUE_APP_BASE_API = '/prod-api'
|
||||||
|
VUE_APP_SERVICE_HOST = 'wb.oms.unisspace.com'
|
||||||
|
|
|
||||||
|
|
@ -28,18 +28,20 @@
|
||||||
"axios": "0.28.1",
|
"axios": "0.28.1",
|
||||||
"clipboard": "2.0.8",
|
"clipboard": "2.0.8",
|
||||||
"core-js": "3.37.1",
|
"core-js": "3.37.1",
|
||||||
|
"decimal.js": "10.4.2",
|
||||||
"echarts": "5.4.0",
|
"echarts": "5.4.0",
|
||||||
"element-ui": "2.15.14",
|
"element-ui": "2.15.14",
|
||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
"fuse.js": "6.4.3",
|
"fuse.js": "6.4.3",
|
||||||
"highlight.js": "9.18.5",
|
"highlight.js": "9.18.5",
|
||||||
|
"html2canvas": "^1.4.1",
|
||||||
"js-beautify": "1.13.0",
|
"js-beautify": "1.13.0",
|
||||||
"js-cookie": "3.0.1",
|
"js-cookie": "3.0.1",
|
||||||
"jsencrypt": "3.0.0-rc.1",
|
"jsencrypt": "3.0.0-rc.1",
|
||||||
|
"jspdf": "^2.5.1",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"quill": "2.0.2",
|
"quill": "2.0.2",
|
||||||
"html2canvas": "^1.4.1",
|
|
||||||
"jspdf": "^2.5.1",
|
|
||||||
"screenfull": "5.0.2",
|
"screenfull": "5.0.2",
|
||||||
"sortablejs": "1.10.2",
|
"sortablejs": "1.10.2",
|
||||||
"splitpanes": "2.4.1",
|
"splitpanes": "2.4.1",
|
||||||
|
|
@ -48,8 +50,7 @@
|
||||||
"vue-cropper": "0.5.5",
|
"vue-cropper": "0.5.5",
|
||||||
"vue-router": "3.4.9",
|
"vue-router": "3.4.9",
|
||||||
"vuedraggable": "2.24.3",
|
"vuedraggable": "2.24.3",
|
||||||
"vuex": "3.6.0",
|
"vuex": "3.6.0"
|
||||||
"decimal.js": "10.4.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "4.4.6",
|
"@vue/cli-plugin-babel": "4.4.6",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询审批任务列表
|
||||||
|
export function listApprovalTask(query) {
|
||||||
|
const { pageNum, pageSize, orderByColumn, isAsc, ...body } = query
|
||||||
|
return request({
|
||||||
|
url: '/approve/task/vue/list',
|
||||||
|
method: 'post',
|
||||||
|
params: { pageNum, pageSize, orderByColumn, isAsc },
|
||||||
|
data: body
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取审批任务数量统计
|
||||||
|
export function getApprovalTaskTotal() {
|
||||||
|
return request({
|
||||||
|
url: '/approve/task/vue/total',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增流程确认记录
|
||||||
|
export function addTodoConfirm(data) {
|
||||||
|
return request({
|
||||||
|
url: '/approve/task/vue/confirm',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询报价单列表
|
||||||
|
export function listQuotation(query) {
|
||||||
|
return request({
|
||||||
|
url: '/quotation/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询报价单详细
|
||||||
|
export function getQuotation(id) {
|
||||||
|
return request({
|
||||||
|
url: '/quotation/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增报价单
|
||||||
|
export function addQuotation(data) {
|
||||||
|
return request({
|
||||||
|
url: '/quotation/insert',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改报价单
|
||||||
|
export function updateQuotation(data) {
|
||||||
|
return request({
|
||||||
|
url: '/quotation/update',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除报价单
|
||||||
|
export function delQuotation(id) {
|
||||||
|
return request({
|
||||||
|
url: '/quotation/remove/batch/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function exportSingleQuotation(id) {
|
||||||
|
return request({
|
||||||
|
url: '/quotation/export/single/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
import { tansParams } from '@/utils/ruoyi'
|
||||||
|
|
||||||
// 查询制造商信息列表
|
// 查询制造商信息列表
|
||||||
export function listVendor(query) {
|
export function listVendor(query) {
|
||||||
|
|
@ -48,7 +49,9 @@ export function exportVendor(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/vendor/export',
|
url: '/system/vendor/export',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: query
|
data: query,
|
||||||
|
transformRequest: [(params) => tansParams(params)],
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 按SN码查询库存数据
|
||||||
|
export function listInventoryInfoByProductSn(query) {
|
||||||
|
return request({
|
||||||
|
url: '/sip/dataProcess/inventoryInfoByProductSn',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询项目管理列表
|
||||||
|
export function listProject(query) {
|
||||||
|
return request({
|
||||||
|
url: '/sip/project/vue/list',
|
||||||
|
method: 'post',
|
||||||
|
data: query,
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 项目转移负责人
|
||||||
|
export function transferProject(data) {
|
||||||
|
return request({
|
||||||
|
url: '/sip/dataProcess/projectTransfer',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询财务计收列表
|
||||||
|
export function listCharge(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/charge/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询财务计收详细
|
||||||
|
export function getCharge(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/charge/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增财务计收
|
||||||
|
export function addCharge(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/charge',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改财务计收
|
||||||
|
export function updateCharge(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/charge',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function revokeCharge(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/charge/revoke',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除财务计收
|
||||||
|
export function delCharge(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/charge/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function applyChargeBiz(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/charge/applyBiz' ,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function returnApplyBiz(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/charge/returnApplyBiz' ,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function applyCharge(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/charge/apply' ,
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
import {tansParams} from "@/utils/ruoyi"
|
||||||
|
|
||||||
|
// 查询销售收票单列表
|
||||||
|
export function listInvoice(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询销售收票单详细
|
||||||
|
export function getInvoice(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询销售收票单附件
|
||||||
|
export function getInvoiceAttachments(id, params) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/invoice/attachment/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传销售收票单附件
|
||||||
|
export function uploadInvoiceAttachment(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/uploadReceipt',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
},
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请红冲
|
||||||
|
export function redRush(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/applyRefund/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请红冲 (提交表单)
|
||||||
|
export function applyRefund(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/applyRefund',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退回销售收票单
|
||||||
|
export function returnInvoice(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/return/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增销售收票单
|
||||||
|
export function addInvoice(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receivable/mergeAndInitiateInvoice',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询销售收票单产品明细
|
||||||
|
export function getInvoiceProducts(code) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/product/' + code,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请开票
|
||||||
|
export function applyInvoice(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/apply',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 撤销销售收票单
|
||||||
|
export function revokeInvoice(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/revoke/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询开票审批列表
|
||||||
|
export function listInvoiceApprove(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/approve/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询已审批开票列表
|
||||||
|
export function listInvoiceApproved(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/approved/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询开票单详细
|
||||||
|
export function getInvoiceDetail(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询收票待审批列表
|
||||||
|
export function listInvoiceReceiptApprove(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticket/approve/list',
|
||||||
|
method: 'post',
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询收票已审批列表
|
||||||
|
export function listInvoiceReceiptApproved(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticket/approved/list',
|
||||||
|
method: 'post',
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询收票详情
|
||||||
|
export function getInvoiceReceipt(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticket/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询收票附件
|
||||||
|
export function getInvoiceReceiptAttachments(id,params) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticket/attachment/' + id,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询核销记录列表
|
||||||
|
export function listInvoiceWriteOff(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/writeoff/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询核销详情
|
||||||
|
export function getInvoiceWriteoff(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/writeoff/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除核销记录
|
||||||
|
export function delInvoiceWriteoff(ids) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/invoice/writeoff/' + ids,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询采购应付单列表
|
||||||
|
export function listPayable(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payable/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询采购应付单详情
|
||||||
|
export function getPayable(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payable/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询付款计划列表
|
||||||
|
export function getPaymentPlan(payableBillId) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/payable/plan/${payableBillId}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新付款计划
|
||||||
|
export function updatePaymentPlan(payableBillId, data) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/payable/plan/${payableBillId}`,
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并并发起付款
|
||||||
|
export function mergeAndInitiatePayment(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payable/mergeAndInitiatePayment',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并并发起收票
|
||||||
|
export function mergeAndInitiateReceipt(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payable/mergeAndInitiateReceipt',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// [PLACEHOLDER] 查询收票计划列表 - Endpoint to be confirmed by user
|
||||||
|
export function getReceivingTicketPlan(payableBillId) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/ticket/plan/${payableBillId}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// [PLACEHOLDER] 更新收票计划 - Endpoint to be confirmed by user
|
||||||
|
export function updateReceivingTicketPlan(payableBillId, data) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/ticket/plan/${payableBillId}`,
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步付款计划至发票计划
|
||||||
|
export function syncToTicketPlan(payableBillId) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/payable/plan/sync/${payableBillId}`,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,210 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
import { tansParams } from "@/utils/ruoyi"
|
||||||
|
|
||||||
|
// 查询付款单列表
|
||||||
|
export function listPayment(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: tansParams(query)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function exportPayment(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/export',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: tansParams(query)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询付款单详细
|
||||||
|
export function getPayment(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询付款单附件
|
||||||
|
export function getPaymentAttachments(id, params) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/payment/attachment/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function deleteFile(id) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/payment/attachment/${id}`,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传付款单附件
|
||||||
|
export function uploadPaymentAttachment(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/uploadReceipt',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
},
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退回付款单
|
||||||
|
export function returnPayment(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/returnPayment/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增付款单
|
||||||
|
export function addPaymentFromPayable(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payable/mergeAndInitiatePayment',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function addPayment(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/add',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleRevoke(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/revoke',
|
||||||
|
method: 'post',
|
||||||
|
data: {id: id},
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请付款
|
||||||
|
export function applyPaymentApi(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/applyPayment',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请退款
|
||||||
|
export function applyRefund(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/applyRefund',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function deletePayment(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/remove',
|
||||||
|
method: 'post',
|
||||||
|
params:{ids:id},
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyRefundApprove(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/applyRefundApprove',
|
||||||
|
method: 'post',
|
||||||
|
data: {id: id},
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询应付单列表 (用于新增付款单-非预付)
|
||||||
|
export function listPayableBills(query) {
|
||||||
|
return request({
|
||||||
|
url: 'finance/payable/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询采购订单列表 (用于新增付款单-预付)
|
||||||
|
export function listOrders(query) {
|
||||||
|
return request({
|
||||||
|
url: '/project/order/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询付款审批列表
|
||||||
|
export function listPaymentApprove(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/approve/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: tansParams(query)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询已审批付款列表
|
||||||
|
export function listPaymentApproved(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/approved/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: tansParams(query)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询付款单列表 (核销专用)
|
||||||
|
export function listPaymentForWriteOff(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/write-off/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: tansParams(query)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手工匹配核销
|
||||||
|
export function manualWriteOff(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/writeoff',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增付款单
|
||||||
|
export function updatePaymentBill(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/updatePaymentBill',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
import {tansParams} from "@/utils/ruoyi";
|
||||||
|
|
||||||
|
// 查询付款退款待审批列表
|
||||||
|
export function listPaymentRefundApprove(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/approve/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询付款退款已审批列表
|
||||||
|
export function listPaymentRefundApproved(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payment/approved/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询付款退款详情
|
||||||
|
export function getPaymentRefund(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/paymentRefund/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
import {tansParams} from "@/utils/ruoyi"
|
||||||
|
|
||||||
|
// 查询收票单列表
|
||||||
|
export function listReceipt(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticket/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询收票单详细
|
||||||
|
export function getReceipt(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticket/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询收票单附件
|
||||||
|
export function getReceiptAttachments(id, params) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/ticket/attachment/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传收票单附件
|
||||||
|
export function uploadReceiptAttachment(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticket/uploadReceipt',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
},
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退回收票单
|
||||||
|
export function redRush(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticket/applyRefund/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function returnReceipt(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticket/return/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增收票单
|
||||||
|
export function addReceipt(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/payable/mergeAndInitiateReceipt',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 撤销收票单
|
||||||
|
export function revokeReceipt(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticket/revoke/' + id,
|
||||||
|
method: 'put'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请红冲(带附件)
|
||||||
|
export function applyRedRush(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticket/applyRefund',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
},
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 查询收款审批列表
|
||||||
|
export function listReceiptApprove(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/approve/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询已审批收款列表
|
||||||
|
export function listReceiptApproved(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/approved/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询收款单详细
|
||||||
|
export function getReceiptDetail(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询销售应收单列表
|
||||||
|
export function listReceivable(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receivable/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询销售应收单详情
|
||||||
|
export function getReceivable(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receivable/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询收款计划列表
|
||||||
|
export function getReceiptPlan(receivableBillId) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/receivable/plan/${receivableBillId}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新收款计划
|
||||||
|
export function updateReceiptPlan(receivableBillId, data) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/receivable/plan/${receivableBillId}`,
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并并发起收款
|
||||||
|
export function mergeAndInitiateReceipt(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receivable/mergeAndInitiateReceipt',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading:true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并并发起开票
|
||||||
|
export function mergeAndInitiateInvoice(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receivable/mergeAndInitiateInvoice',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading:true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询开票计划列表
|
||||||
|
export function getInvoicePlan(receivableBillId) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/receivable/invoice/plan/${receivableBillId}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新开票计划
|
||||||
|
export function updateInvoicePlan(receivableBillId, data) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/receivable/invoice/plan/${receivableBillId}`,
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步收款计划至开票计划
|
||||||
|
export function syncToInvoicePlan(receivableBillId) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/receivable/plan/sync/${receivableBillId}`,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询销售核销记录列表
|
||||||
|
export function listReceivableWriteOff(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receivable/writeoff/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询销售核销详情
|
||||||
|
export function getReceivableWriteOff(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receivable/writeoff/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除销售核销记录
|
||||||
|
export function delReceivableWriteOff(ids) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receivable/writeoff/' + ids,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
import {tansParams} from "@/utils/ruoyi"
|
||||||
|
|
||||||
|
// 查询收款单列表
|
||||||
|
export function listReceive(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询收款单详细
|
||||||
|
export function getReceive(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询收款单附件
|
||||||
|
export function getReceiveAttachments(id, params) {
|
||||||
|
return request({
|
||||||
|
url: `/finance/receipt/attachment/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传收款单附件
|
||||||
|
export function uploadReceiveAttachment(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/uploadReceipt',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
},
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请红冲
|
||||||
|
export function redRush(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/applyRefund/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退回收款单
|
||||||
|
export function returnReceive(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/returnReceivable/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增收款单 (Calls Receivable Merge Logic)
|
||||||
|
export function mergeReceivable(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receivable/mergeAndInitiateReceipt',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function addReceipt(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/insert',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function applyReceipt(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/applyReceipt',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请退款
|
||||||
|
export function submitRefund(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/applyRefund',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询收款单列表 (核销专用)
|
||||||
|
export function listReceiptForWriteOff(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/write-off/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: tansParams(query)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手工匹配核销
|
||||||
|
export function manualReceiptWriteOff(data) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receivable/writeoff',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 撤销收款单
|
||||||
|
export function revokeReceipt(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/receipt/revoke',
|
||||||
|
method: 'post',
|
||||||
|
data: {id: id},
|
||||||
|
needLoading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询项目报表列表
|
||||||
|
export function listReport(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/report/list',
|
||||||
|
method: 'post',
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询供货商维度报表列表
|
||||||
|
export function listSupplierReport(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/report/listSupplier',
|
||||||
|
method: 'post',
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
data: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询核销记录列表
|
||||||
|
export function listWriteOff(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/writeoff/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function listTicketWriteOff(query) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticketWriteoff/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询核销详情
|
||||||
|
export function getWriteOff(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/writeoff/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getTicketWriteoff(id) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticketWriteoff/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除核销记录
|
||||||
|
export function delWriteOff(ids) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/writeoff/' + ids,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function delTicketWriteoff(ids) {
|
||||||
|
return request({
|
||||||
|
url: '/finance/ticketWriteoff/' + ids,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,14 @@ export function listCompletedFlows(data) {
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 按业务单号查询待办记录(查看申请详情用)
|
||||||
|
export function listApplyFlows(data) {
|
||||||
|
return request({
|
||||||
|
url: '/flow/todo/apply-list',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
// 通用审批
|
// 通用审批
|
||||||
export function approveTask(data) {
|
export function approveTask(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,23 @@ export function recallDelivery(id) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 提交出库撤回审批(跨天发货记录)
|
||||||
|
export function recallApply(id, reason, amountChanged) {
|
||||||
|
return request({
|
||||||
|
url: '/inventory/delivery/vue/recall/apply',
|
||||||
|
method: 'post',
|
||||||
|
data: {id, reason, amountChanged}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按发货记录 id 检查关联财务单据的审批状态(撤回/作废按钮前置校验)
|
||||||
|
export function checkRecallByDelivery(deliveryId) {
|
||||||
|
return request({
|
||||||
|
url: '/inventory/delivery/vue/recall/check-by-delivery/' + deliveryId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 导出采购合同
|
// 导出采购合同
|
||||||
export function exportDelivery(query) {
|
export function exportDelivery(query) {
|
||||||
return request({
|
return request({
|
||||||
|
|
@ -80,6 +97,15 @@ export function listProductSn(query) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询仅维保订单发货可续用的SN(关联项目同产品编码的已发货SN)
|
||||||
|
export function maintenanceSn(query) {
|
||||||
|
return request({
|
||||||
|
url: '/inventory/delivery/vue/maintenanceSn',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function removeDelivery(id) {
|
export function removeDelivery(id) {
|
||||||
return request({
|
return request({
|
||||||
url: '/inventory/delivery/vue/' + id,
|
url: '/inventory/delivery/vue/' + id,
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,23 @@ export function recallExecution(id) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 提交撤单审批申请
|
||||||
|
export function recallExecutionApply(id, reason, amountChanged) {
|
||||||
|
return request({
|
||||||
|
url: '/inventory/execution/vue/recall/apply',
|
||||||
|
method: 'post',
|
||||||
|
data: { id, reason, amountChanged }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function exportExecution(data) {
|
||||||
|
return request({
|
||||||
|
url: `/inventory/execution/vue/export`,
|
||||||
|
method: 'post',
|
||||||
|
data: { data },
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 查询出库预览信息
|
// 查询出库预览信息
|
||||||
export function getCheckOutPreview(data) {
|
export function getCheckOutPreview(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|
|
||||||
|
|
@ -85,3 +85,30 @@ export function getOuterLog(query) {
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询在途库存
|
||||||
|
export function getPurchaseStock(query) {
|
||||||
|
return request({
|
||||||
|
url: '/inventory/info/vue/purchase-stock',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询锁单
|
||||||
|
export function getBindOrder(query) {
|
||||||
|
return request({
|
||||||
|
url: '/inventory/info/vue/bind-order',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询在库现存
|
||||||
|
export function getInventoryInner(query) {
|
||||||
|
return request({
|
||||||
|
url: '/inventory/info/vue/inventory-inner',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,15 @@ export function getInner(id) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询入库单产品信息列表
|
||||||
|
export function listInnerInventoryInfo(id, query) {
|
||||||
|
return request({
|
||||||
|
url: `${VUE_APP_API_URL}/${id}/inventory-info/list`,
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 新增入库单信息
|
// 新增入库单信息
|
||||||
export function addInner(data) {
|
export function addInner(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,14 @@ export function exportDownloadTemplate() {
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 下载带列表数据的发货导入模板
|
||||||
|
export function exportDownloadTemplateData(data) {
|
||||||
|
return request({
|
||||||
|
url: '/inventory/outer/importTemplateData',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
export function importSnData(formData) {
|
export function importSnData(formData) {
|
||||||
return request({
|
return request({
|
||||||
url: '/inventory/outer/vue/importData',
|
url: '/inventory/outer/vue/importData',
|
||||||
|
|
@ -89,3 +97,13 @@ export function importSnData(formData) {
|
||||||
data: formData
|
data: formData
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
export function assignDeliverySnData(formData) {
|
||||||
|
return request({
|
||||||
|
url: '/inventory/outer/vue/assignDeliveryData',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
},
|
||||||
|
data: formData
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
function createLoginFormData(data) {
|
||||||
|
const formData = new FormData()
|
||||||
|
Object.keys(data).forEach(key => formData.append(key, data[key]))
|
||||||
|
return formData
|
||||||
|
}
|
||||||
|
|
||||||
// 登录方法 - 基于 Session 认证,使用 FormData 格式
|
// 登录方法 - 基于 Session 认证,使用 FormData 格式
|
||||||
export function login(username, password, code, rememberMe) {
|
export function login(username, password, code, rememberMe) {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
|
|
@ -9,6 +15,40 @@ export function login(username, password, code, rememberMe) {
|
||||||
formData.append('rememberMe', rememberMe || false)
|
formData.append('rememberMe', rememberMe || false)
|
||||||
return request({
|
return request({
|
||||||
url: '/login',
|
url: '/login',
|
||||||
|
headers: {
|
||||||
|
isToken: false,
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
repeatSubmit: false,
|
||||||
|
allowWarning: true
|
||||||
|
},
|
||||||
|
method: 'post',
|
||||||
|
data: formData
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetLoginPwd(data) {
|
||||||
|
const formData = createLoginFormData({
|
||||||
|
username: data.username,
|
||||||
|
newPassword: data.newPassword,
|
||||||
|
confirmPassword: data.confirmPassword,
|
||||||
|
emailCode: data.emailCode
|
||||||
|
})
|
||||||
|
return request({
|
||||||
|
url: '/login/resetPwd',
|
||||||
|
headers: {
|
||||||
|
isToken: false,
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
repeatSubmit: false
|
||||||
|
},
|
||||||
|
method: 'post',
|
||||||
|
data: formData
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sendResetPwdEmailCode(username) {
|
||||||
|
const formData = createLoginFormData({ username })
|
||||||
|
return request({
|
||||||
|
url: '/login/sendResetPwdEmailCode',
|
||||||
headers: {
|
headers: {
|
||||||
isToken: false,
|
isToken: false,
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ export function delOrder(id) {
|
||||||
export function exportOrder(query) {
|
export function exportOrder(query) {
|
||||||
return request({
|
return request({
|
||||||
url: `${baseURL}/export`,
|
url: `${baseURL}/export`,
|
||||||
method: 'post',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,40 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询产品信息
|
function normalizeSerialNumbers(input) {
|
||||||
export function getProductInfo(serialNumber) {
|
const values = Array.isArray(input) ? input : [input]
|
||||||
|
return [...new Set(values
|
||||||
|
.map(item => (item || '').toString().trim())
|
||||||
|
.filter(Boolean))]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildQueryPayload(input) {
|
||||||
|
const serialNumbers = normalizeSerialNumbers(input)
|
||||||
|
return {
|
||||||
|
serialNumber: serialNumbers[0] || '',
|
||||||
|
serialNumbers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProductInfo(serialNumberOrNumbers) {
|
||||||
return request({
|
return request({
|
||||||
url: '/manage/service/product',
|
url: '/manage/service/product',
|
||||||
method: 'get',
|
method: 'post',
|
||||||
params: { serialNumber }
|
data: buildQueryPayload(serialNumberOrNumbers)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询相关合同信息
|
export function getOrderInfo(serialNumberOrNumbers) {
|
||||||
export function getOrderInfo(serialNumber) {
|
|
||||||
return request({
|
return request({
|
||||||
url: '/manage/service/order',
|
url: '/manage/service/order',
|
||||||
method: 'get',
|
method: 'post',
|
||||||
params: { serialNumber }
|
data: buildQueryPayload(serialNumberOrNumbers)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询标准保修信息
|
export function getWarrantyInfo(serialNumberOrNumbers) {
|
||||||
export function getWarrantyInfo(serialNumber) {
|
|
||||||
return request({
|
return request({
|
||||||
url: '/manage/service/query',
|
url: '/manage/service/query',
|
||||||
method: 'get',
|
method: 'post',
|
||||||
params: { serialNumber }
|
data: buildQueryPayload(serialNumberOrNumbers)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
import { tansParams } from '@/utils/ruoyi'
|
||||||
|
|
||||||
// 查询项目管理列表
|
// 查询项目管理列表
|
||||||
export function listProject(query) {
|
export function listProject(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/sip/project/vue/list',
|
url: '/sip/project/vue/list',
|
||||||
method: 'get',
|
method: 'post',
|
||||||
params: query
|
data: query,
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,6 +52,34 @@ export function exportProject(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/sip/project/vue/export',
|
url: '/sip/project/vue/export',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: query
|
data: query,
|
||||||
|
transformRequest: [(params) => tansParams(params)],
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 项目收藏
|
||||||
|
export function addCollect(data) {
|
||||||
|
return request({
|
||||||
|
url: '/project/collect/add',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
needLoading:true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function editJoinTrial(data) {
|
||||||
|
return request({
|
||||||
|
url: '/sip/project/vue/joinTrial',
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
needLoading:true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function exportSingle(id) {
|
||||||
|
return request({
|
||||||
|
url: `/sip/project/vue/joinTrial/export/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
needLoading:true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,3 +85,57 @@ export function updateFinanceStatus(id, status) {
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询仅维保订单可关联的项目订单(仅正常订单且审批完成)
|
||||||
|
export function relatedCandidateList(query) {
|
||||||
|
return request({
|
||||||
|
url: '/project/order/vue/relatedCandidateList',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询产品配货备货明细
|
||||||
|
export function productMatchList(orderCode) {
|
||||||
|
return request({
|
||||||
|
url: '/project/order/vue/productMatchList',
|
||||||
|
method: 'get',
|
||||||
|
params: { orderCode }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询产品配备货绑定信息
|
||||||
|
export function productMatchBindList(orderCode, productCode, params = {}) {
|
||||||
|
return request({
|
||||||
|
url: '/project/order/vue/productMatchBindList',
|
||||||
|
method: 'get',
|
||||||
|
params: { orderCode, productCode, ...params }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存订单关联采购单
|
||||||
|
export function savePurchaseOrderMap(data) {
|
||||||
|
return request({
|
||||||
|
url: '/project/order/vue/purchaseOrderMap',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询采购单SN码列表
|
||||||
|
export function purchaseSnList(purchaseNo, orderCode, productCode) {
|
||||||
|
return request({
|
||||||
|
url: '/project/order/vue/purchaseSnList',
|
||||||
|
method: 'get',
|
||||||
|
params: { purchaseNo, orderCode, productCode }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订单绑定SN码
|
||||||
|
export function bindOrderSnCodes(orderCode, productCode, purchaseNo, orderId, purchaseId, productSnList) {
|
||||||
|
return request({
|
||||||
|
url: '/project/order/vue/bindOrderSnCodes',
|
||||||
|
method: 'post',
|
||||||
|
data: { orderCode, productCode, purchaseNo, orderId, purchaseId, productSnList }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,16 @@ export function recallPurchaseorder(id) {
|
||||||
method: 'put'
|
method: 'put'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
export function exportPurchaseorder(data) {
|
||||||
|
return request({
|
||||||
|
url: '/sip/purchaseorder/export',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
// headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
needLoading: true
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 查询已审批采购单主表列表
|
// 查询已审批采购单主表列表
|
||||||
export function listApprovedPurchaseorder(query) {
|
export function listApprovedPurchaseorder(query) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
import { tansParams } from '@/utils/ruoyi'
|
||||||
|
|
||||||
// 查询办事处信息列表
|
// 查询办事处信息列表
|
||||||
export function listAgent(query) {
|
export function listAgent(query) {
|
||||||
|
|
@ -48,6 +49,8 @@ export function exportAgent(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/agent/vue/export',
|
url: '/system/agent/vue/export',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: query
|
data: query,
|
||||||
|
transformRequest: [(params) => tansParams(params)],
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询公司信息列表
|
||||||
|
export function listCompanyInfo(query) {
|
||||||
|
return request({
|
||||||
|
url: '/sip/companyInfo/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询公司信息详细
|
||||||
|
export function getCompanyInfo(id) {
|
||||||
|
return request({
|
||||||
|
url: '/sip/companyInfo/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增公司信息
|
||||||
|
export function addCompanyInfo(data) {
|
||||||
|
return request({
|
||||||
|
url: '/sip/companyInfo',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改公司信息
|
||||||
|
export function updateCompanyInfo(data) {
|
||||||
|
return request({
|
||||||
|
url: '/sip/companyInfo',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除公司信息
|
||||||
|
export function delCompanyInfo(id) {
|
||||||
|
return request({
|
||||||
|
url: '/sip/companyInfo/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
import { tansParams } from '@/utils/ruoyi'
|
||||||
|
|
||||||
// 查询客户信息列表
|
// 查询客户信息列表
|
||||||
export function listCustomer(query) {
|
export function listCustomer(query) {
|
||||||
|
|
@ -48,6 +49,8 @@ export function exportCustomer(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/customer/vue/export',
|
url: '/system/customer/vue/export',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: query
|
data: query,
|
||||||
|
transformRequest: [(params) => tansParams(params)],
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
import { tansParams } from '@/utils/ruoyi'
|
||||||
|
|
||||||
// 查询产品管理列表
|
// 查询产品管理列表
|
||||||
export function listProduct(query) {
|
export function listProduct(query) {
|
||||||
|
|
@ -48,7 +49,9 @@ export function exportProduct(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/product/export',
|
url: '/system/product/export',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: query
|
data: query,
|
||||||
|
transformRequest: [(params) => tansParams(params)],
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
import { parseStrEmpty } from "@/utils/ruoyi";
|
import { parseStrEmpty } from "@/utils/ruoyi";
|
||||||
|
|
||||||
// 查询用户列表
|
// 鏌ヨ鐢ㄦ埛鍒楄〃
|
||||||
export function listUser(query) {
|
export function listUser(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/list',
|
url: '/system/user/list',
|
||||||
|
|
@ -10,7 +10,7 @@ export function listUser(query) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询用户详细
|
// 鏌ヨ鐢ㄦ埛璇︾粏
|
||||||
export function getUser(userId) {
|
export function getUser(userId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/' + parseStrEmpty(userId),
|
url: '/system/user/' + parseStrEmpty(userId),
|
||||||
|
|
@ -18,7 +18,7 @@ export function getUser(userId) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增用户
|
// 鏂板鐢ㄦ埛
|
||||||
export function addUser(data) {
|
export function addUser(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/add',
|
url: '/system/user/add',
|
||||||
|
|
@ -30,7 +30,7 @@ export function addUser(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改用户
|
// 淇敼鐢ㄦ埛
|
||||||
export function updateUser(data) {
|
export function updateUser(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user',
|
url: '/system/user',
|
||||||
|
|
@ -39,7 +39,7 @@ export function updateUser(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除用户
|
// 鍒犻櫎鐢ㄦ埛
|
||||||
export function delUser(userId) {
|
export function delUser(userId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/' + userId,
|
url: '/system/user/' + userId,
|
||||||
|
|
@ -47,7 +47,7 @@ export function delUser(userId) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户密码重置
|
// 鐢ㄦ埛瀵嗙爜閲嶇疆
|
||||||
export function resetUserPwd(data) {
|
export function resetUserPwd(data) {
|
||||||
|
|
||||||
return request({
|
return request({
|
||||||
|
|
@ -60,7 +60,7 @@ export function resetUserPwd(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户状态修改
|
// 鐢ㄦ埛鐘舵€佷慨鏀?
|
||||||
export function changeUserStatus(userId, status) {
|
export function changeUserStatus(userId, status) {
|
||||||
const data = {
|
const data = {
|
||||||
userId,
|
userId,
|
||||||
|
|
@ -76,7 +76,7 @@ export function changeUserStatus(userId, status) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询用户个人信息
|
// 鏌ヨ鐢ㄦ埛涓汉淇℃伅
|
||||||
export function getUserProfile() {
|
export function getUserProfile() {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/profile/vue',
|
url: '/system/user/profile/vue',
|
||||||
|
|
@ -84,7 +84,7 @@ export function getUserProfile() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改用户个人信息
|
// 淇敼鐢ㄦ埛涓汉淇℃伅
|
||||||
export function updateUserProfile(data) {
|
export function updateUserProfile(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/profile',
|
url: '/system/user/profile',
|
||||||
|
|
@ -93,7 +93,7 @@ export function updateUserProfile(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户密码重置
|
// 鐢ㄦ埛瀵嗙爜閲嶇疆
|
||||||
export function updateUserPwd(data) {
|
export function updateUserPwd(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/profile/resetPwd',
|
url: '/system/user/profile/resetPwd',
|
||||||
|
|
@ -103,7 +103,7 @@ export function updateUserPwd(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户头像上传
|
// 鐢ㄦ埛澶村儚涓婁紶
|
||||||
export function uploadAvatar(data) {
|
export function uploadAvatar(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/profile/updateAvatar',
|
url: '/system/user/profile/updateAvatar',
|
||||||
|
|
@ -113,7 +113,20 @@ export function uploadAvatar(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询授权角色
|
export function getUserBotProfile() {
|
||||||
|
return request({
|
||||||
|
url: '/system/user/profile/bot',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateUserBotProfile() {
|
||||||
|
return request({
|
||||||
|
url: '/system/user/profile/bot/generate',
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 鏌ヨ鎺堟潈瑙掕壊
|
||||||
export function getAuthRole(userId) {
|
export function getAuthRole(userId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/vue/authRole/' + userId,
|
url: '/system/user/vue/authRole/' + userId,
|
||||||
|
|
@ -121,7 +134,7 @@ export function getAuthRole(userId) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存授权角色
|
// 淇濆瓨鎺堟潈瑙掕壊
|
||||||
export function updateAuthRole(data) {
|
export function updateAuthRole(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/authRole',
|
url: '/system/user/authRole',
|
||||||
|
|
@ -130,7 +143,7 @@ export function updateAuthRole(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询部门下拉树结构
|
// 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋?
|
||||||
export function deptTreeSelect() {
|
export function deptTreeSelect() {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/deptTree',
|
url: '/system/user/deptTree',
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
import { tansParams } from '@/utils/ruoyi'
|
||||||
|
|
||||||
// 查询仓库基础信息列表
|
// 查询仓库基础信息列表
|
||||||
export function listInfo(query) {
|
export function listInfo(query) {
|
||||||
|
|
@ -48,6 +49,8 @@ export function exportInfo(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/warehouse/info/export',
|
url: '/warehouse/info/export',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: query
|
data: query,
|
||||||
|
transformRequest: [(params) => tansParams(params)],
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 152 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -104,6 +104,18 @@
|
||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 固定列覆盖层:强制不透明背景 + 提升层级,防止横向滚动时内容穿透固定列
|
||||||
|
.el-table__fixed,
|
||||||
|
.el-table__fixed-right {
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__fixed .el-table__cell,
|
||||||
|
.el-table__fixed-right .el-table__cell {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 表单布局 **/
|
/** 表单布局 **/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||||
|
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||||
|
<img :src="currentImageUrl" style="width: 100%;max-height: 60vh" />
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "GlobalFilePreview",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pdfPreviewVisible: false,
|
||||||
|
currentPdfUrl: '',
|
||||||
|
imagePreviewVisible: false,
|
||||||
|
currentImageUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isPdf(filePath) {
|
||||||
|
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||||
|
},
|
||||||
|
getFileBaseName(filePath) {
|
||||||
|
if (!filePath) return '';
|
||||||
|
const parts = filePath.split('/');
|
||||||
|
return parts[parts.length - 1] || '';
|
||||||
|
},
|
||||||
|
// 在线查看地址:后端以 inline 方式返回文件(图片/PDF 可直接在浏览器渲染)
|
||||||
|
getViewUrl(filePath, fileName) {
|
||||||
|
const resource = encodeURIComponent(filePath || '');
|
||||||
|
const name = encodeURIComponent(fileName || this.getFileBaseName(filePath));
|
||||||
|
return `${process.env.VUE_APP_BASE_API}/project/order/file/view?filePath=${resource}&fileName=${name}`;
|
||||||
|
},
|
||||||
|
handlePreview(attachment) {
|
||||||
|
if (!attachment || !attachment.filePath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const filePath = attachment.filePath.toLowerCase();
|
||||||
|
const isImage = /\.(png|jpg|jpeg|gif)$/.test(filePath);
|
||||||
|
if (this.isPdf(filePath)) {
|
||||||
|
this.currentPdfUrl = this.getViewUrl(attachment.filePath, attachment.fileName);
|
||||||
|
this.pdfPreviewVisible = true;
|
||||||
|
} else if (isImage) {
|
||||||
|
this.currentImageUrl = this.getViewUrl(attachment.filePath, attachment.fileName);
|
||||||
|
this.imagePreviewVisible = true;
|
||||||
|
} else {
|
||||||
|
// 浏览器无法直接渲染的格式,改为新窗口打开(由浏览器决定预览或下载)
|
||||||
|
window.open(this.getViewUrl(attachment.filePath, attachment.fileName), '_blank');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
downloadFile(attachment){
|
||||||
|
if (attachment){
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" +attachment.filePath;
|
||||||
|
link.download = attachment.fileName || 'file';
|
||||||
|
link.style.display = 'none';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<el-button size="mini" circle icon="el-icon-menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
|
<el-button size="mini" circle icon="el-icon-menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
|
||||||
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
|
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
|
||||||
<el-button size="mini" circle icon="el-icon-menu" />
|
<el-button size="mini" circle icon="el-icon-menu" />
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown" >
|
||||||
<!-- 全选/反选 按钮 -->
|
<!-- 全选/反选 按钮 -->
|
||||||
<el-dropdown-item>
|
<el-dropdown-item>
|
||||||
<el-checkbox :indeterminate="isIndeterminate" v-model="isChecked" @change="toggleCheckAll"> 列展示 </el-checkbox>
|
<el-checkbox :indeterminate="isIndeterminate" v-model="isChecked" @change="toggleCheckAll"> 列展示 </el-checkbox>
|
||||||
|
|
@ -183,4 +183,8 @@ export default {
|
||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
margin: 3px auto;
|
margin: 3px auto;
|
||||||
}
|
}
|
||||||
|
.el-dropdown-menu{
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
<logo v-if="showLogo" :collapse="isCollapse" />
|
<logo v-if="showLogo" :collapse="isCollapse" />
|
||||||
<el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper">
|
<el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper">
|
||||||
<el-menu
|
<el-menu
|
||||||
|
ref="sideMenu"
|
||||||
:default-active="activeMenu"
|
:default-active="activeMenu"
|
||||||
:collapse="isCollapse"
|
:collapse="isCollapse"
|
||||||
:background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
|
:background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
|
||||||
|
|
@ -11,6 +12,8 @@
|
||||||
:active-text-color="settings.theme"
|
:active-text-color="settings.theme"
|
||||||
:collapse-transition="false"
|
:collapse-transition="false"
|
||||||
mode="vertical"
|
mode="vertical"
|
||||||
|
@open="handleMenuOpen"
|
||||||
|
@close="handleMenuClose"
|
||||||
>
|
>
|
||||||
<sidebar-item
|
<sidebar-item
|
||||||
v-for="(route, index) in sidebarRouters"
|
v-for="(route, index) in sidebarRouters"
|
||||||
|
|
@ -24,6 +27,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import pathLib from 'path'
|
||||||
import { mapGetters, mapState } from "vuex"
|
import { mapGetters, mapState } from "vuex"
|
||||||
import Logo from "./Logo"
|
import Logo from "./Logo"
|
||||||
import SidebarItem from "./SidebarItem"
|
import SidebarItem from "./SidebarItem"
|
||||||
|
|
@ -52,6 +56,55 @@ export default {
|
||||||
isCollapse() {
|
isCollapse() {
|
||||||
return !this.sidebar.opened
|
return !this.sidebar.opened
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 监听菜单折叠事件:当折叠"进销存管理"时同步折叠"出库管理"子菜单
|
||||||
|
handleMenuClose(index) {
|
||||||
|
// 在一级路由中找到当前折叠的菜单
|
||||||
|
const closedRoute = this.sidebarRouters.find(route => route.path === index)
|
||||||
|
if (!closedRoute || !closedRoute.children || !closedRoute.children.length) return
|
||||||
|
|
||||||
|
// 找到直属子级中名为"出库管理"的子菜单
|
||||||
|
const outerRoute = closedRoute.children.find(
|
||||||
|
child => !child.hidden && child.meta && child.meta.title === '出库管理'
|
||||||
|
)
|
||||||
|
if (!outerRoute) return
|
||||||
|
|
||||||
|
// 计算"出库管理"在 el-menu 中注册的 index(与展开逻辑保持一致)
|
||||||
|
const outerBasePath = pathLib.resolve(index, outerRoute.path)
|
||||||
|
const outerIndex = pathLib.resolve(outerBasePath, outerRoute.path)
|
||||||
|
|
||||||
|
if (this.$refs.sideMenu) {
|
||||||
|
this.$refs.sideMenu.close(outerIndex)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 监听菜单展开事件:当展开的菜单含有"出库管理"子菜单时同步将其展开
|
||||||
|
handleMenuOpen(index) {
|
||||||
|
// 在一级路由中找到当前展开的菜单
|
||||||
|
const openedRoute = this.sidebarRouters.find(route => route.path === index)
|
||||||
|
if (!openedRoute || !openedRoute.children || !openedRoute.children.length) return
|
||||||
|
|
||||||
|
// 找到直属子级中名为"出库管理"且自身含子项的子菜单
|
||||||
|
const outerRoute = openedRoute.children.find(
|
||||||
|
child => !child.hidden && child.meta && child.meta.title === '出库管理'
|
||||||
|
)
|
||||||
|
if (!outerRoute) return
|
||||||
|
|
||||||
|
// 计算"出库管理"对应的 el-menu index
|
||||||
|
// SidebarItem 渲染嵌套菜单时:
|
||||||
|
// 先将 basePath 设为 path.resolve(parentPath, child.path)
|
||||||
|
// 再以新 basePath 计算 el-submenu :index = path.resolve(basePath, child.path)
|
||||||
|
// 因此需要对 child.path 做两次 resolve 才能与注册 index 对齐
|
||||||
|
const outerBasePath = pathLib.resolve(index, outerRoute.path)
|
||||||
|
const outerIndex = pathLib.resolve(outerBasePath, outerRoute.path)
|
||||||
|
|
||||||
|
// 延迟 300ms 展开,确保 el-submenu 子组件已完成挂载并向 el-menu 注册
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.$refs.sideMenu) {
|
||||||
|
this.$refs.sideMenu.open(outerIndex)
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import './permission' // permission control
|
||||||
import { getDicts } from "@/api/system/dict/data"
|
import { getDicts } from "@/api/system/dict/data"
|
||||||
import { getConfigKey } from "@/api/system/config"
|
import { getConfigKey } from "@/api/system/config"
|
||||||
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"
|
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"
|
||||||
|
import { formatCurrency } from "@/utils"
|
||||||
// 分页组件
|
// 分页组件
|
||||||
import Pagination from "@/components/Pagination"
|
import Pagination from "@/components/Pagination"
|
||||||
// 自定义表格工具组件
|
// 自定义表格工具组件
|
||||||
|
|
@ -42,6 +43,7 @@ import DictData from '@/components/DictData'
|
||||||
Vue.prototype.getDicts = getDicts
|
Vue.prototype.getDicts = getDicts
|
||||||
Vue.prototype.getConfigKey = getConfigKey
|
Vue.prototype.getConfigKey = getConfigKey
|
||||||
Vue.prototype.parseTime = parseTime
|
Vue.prototype.parseTime = parseTime
|
||||||
|
Vue.prototype.formatCurrency = formatCurrency
|
||||||
Vue.prototype.resetForm = resetForm
|
Vue.prototype.resetForm = resetForm
|
||||||
Vue.prototype.addDateRange = addDateRange
|
Vue.prototype.addDateRange = addDateRange
|
||||||
Vue.prototype.selectDictLabel = selectDictLabel
|
Vue.prototype.selectDictLabel = selectDictLabel
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,16 @@ import NProgress from 'nprogress'
|
||||||
import 'nprogress/nprogress.css'
|
import 'nprogress/nprogress.css'
|
||||||
import { isPathMatch } from '@/utils/validate'
|
import { isPathMatch } from '@/utils/validate'
|
||||||
import { isRelogin } from '@/utils/request'
|
import { isRelogin } from '@/utils/request'
|
||||||
|
import { isServiceHost } from '@/utils/serviceHost'
|
||||||
|
|
||||||
NProgress.configure({ showSpinner: false })
|
NProgress.configure({ showSpinner: false })
|
||||||
|
|
||||||
const whiteList = ['/login', '/register', '/manage/service','/system/vendor/query','/system/partner/query']
|
const whiteList = ['/login', '/register', '/manage/service','/system/vendor/query','/system/partner/query']
|
||||||
|
|
||||||
const isWhiteList = (path) => {
|
const isWhiteList = (path) => {
|
||||||
|
if (path === '/' && isServiceHost()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return whiteList.some(pattern => isPathMatch(pattern, path))
|
return whiteList.some(pattern => isPathMatch(pattern, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,12 @@ const baseURL = process.env.VUE_APP_BASE_API
|
||||||
let downloadLoadingInstance
|
let downloadLoadingInstance
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name(name, isDelete = true) {
|
download(name, isDelete = true) {
|
||||||
var url = baseURL + "/common/download?fileName=" + encodeURIComponent(name) + "&delete=" + isDelete
|
var url = baseURL + "/common/download?fileName=" + encodeURIComponent(name) + "&delete=" + isDelete
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: url,
|
url: url,
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
headers: { 'Authorization': 'Bearer ' + getToken() }
|
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
const isBlob = blobValidate(res.data)
|
const isBlob = blobValidate(res.data)
|
||||||
if (isBlob) {
|
if (isBlob) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Router from 'vue-router'
|
import Router from 'vue-router'
|
||||||
|
import { isServiceHost } from '@/utils/serviceHost'
|
||||||
|
|
||||||
Vue.use(Router)
|
Vue.use(Router)
|
||||||
|
|
||||||
|
|
@ -62,10 +63,10 @@ export const constantRoutes = [
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: isServiceHost() ? '/admin-root' : '',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: 'index',
|
redirect: isServiceHost() ? undefined : 'index',
|
||||||
children: [
|
children: isServiceHost() ? [] : [
|
||||||
{
|
{
|
||||||
path: 'index',
|
path: 'index',
|
||||||
component: () => import('@/views/index'),
|
component: () => import('@/views/index'),
|
||||||
|
|
@ -94,6 +95,46 @@ export const constantRoutes = [
|
||||||
component: () => import('@/views/approve/approved_order/index'),
|
component: () => import('@/views/approve/approved_order/index'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'paymentLog',
|
||||||
|
component: () => import('@/views/approve/finance/payment/approved/index'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'paymentRedLog',
|
||||||
|
component: () => import('@/views/approve/finance/paymentRefund/approved/index'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'invoiceRedLog',
|
||||||
|
component: () => import('@/views/approve/finance/invoiceRed/approved/index'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'invoiceLog',
|
||||||
|
component: () => import('@/views/approve/finance/invoiceReceipt/approved/index'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'receiptLog',
|
||||||
|
component: () => import('@/views/approve/finance/receipt/approved/index.vue'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'receiptRefoundLog',
|
||||||
|
component: () => import('@/views/approve/finance/receiptRefound/approved/index.vue'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'receivableInvoiceLog',
|
||||||
|
component: () => import('@/views/approve/finance/receivableInvoice/approved/index.vue'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'receivableInvoiceRefundLog',
|
||||||
|
component: () => import('@/views/approve/finance/receivableInvoiceRefund/approved/index.vue'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -112,6 +153,11 @@ export const constantRoutes = [
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
path: isServiceHost() ? '/' : '/service-host-root',
|
||||||
|
component: () => import('@/views/manage/service/index'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/manage/service',
|
path: '/manage/service',
|
||||||
component: () => import('@/views/manage/service/index'),
|
component: () => import('@/views/manage/service/index'),
|
||||||
|
|
@ -164,6 +210,31 @@ export const dynamicRoutes = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/finance',
|
||||||
|
component: Layout,
|
||||||
|
redirect: 'noRedirect',
|
||||||
|
name: 'Finance',
|
||||||
|
meta: {
|
||||||
|
title: '财务管理',
|
||||||
|
icon: 'money'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'payment',
|
||||||
|
component: () => import('@/views/finance/payment/index'),
|
||||||
|
name: 'Payment',
|
||||||
|
meta: { title: '付款单', icon: 'form' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'charge',
|
||||||
|
component: () => import('@/views/finance/charge/index'),
|
||||||
|
name: 'Charge',
|
||||||
|
meta: { title: '财务计收', icon: 'money' },
|
||||||
|
permissions: ['finance:charge:list']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/inventory/execution',
|
path: '/inventory/execution',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ const user = {
|
||||||
const rememberMe = userInfo.rememberMe
|
const rememberMe = userInfo.rememberMe
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
login(username, password, code, rememberMe).then(res => {
|
login(username, password, code, rememberMe).then(res => {
|
||||||
resolve()
|
resolve(res)
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
|
|
@ -60,17 +60,16 @@ const user = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
GetInfo({ commit, state }) {
|
GetInfo({ commit }) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getInfo().then(res => {
|
getInfo().then(res => {
|
||||||
// 后端返回的数据在 res.data 中
|
|
||||||
const data = res.data || res
|
const data = res.data || res
|
||||||
const user = data.user
|
const user = data.user
|
||||||
let avatar = user.avatar? '/common/download/resource?resource='+encodeURIComponent(user.avatar) : ""
|
let avatar = user.avatar ? '/common/download/resource?resource=' + encodeURIComponent(user.avatar) : ""
|
||||||
if (!isHttp(avatar)) {
|
if (!isHttp(avatar)) {
|
||||||
avatar = (isEmpty(avatar)) ? defAva : process.env.VUE_APP_BASE_API + avatar
|
avatar = (isEmpty(avatar)) ? defAva : process.env.VUE_APP_BASE_API + avatar
|
||||||
}
|
}
|
||||||
if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
if (data.roles && data.roles.length > 0) {
|
||||||
commit('SET_ROLES', data.roles)
|
commit('SET_ROLES', data.roles)
|
||||||
commit('SET_PERMISSIONS', data.permissions)
|
commit('SET_PERMISSIONS', data.permissions)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -82,14 +81,12 @@ const user = {
|
||||||
commit('SET_NAME', user.loginName)
|
commit('SET_NAME', user.loginName)
|
||||||
commit('SET_NICK_NAME', user.userName)
|
commit('SET_NICK_NAME', user.userName)
|
||||||
commit('SET_AVATAR', avatar)
|
commit('SET_AVATAR', avatar)
|
||||||
/* 初始密码提示 */
|
if (data.isDefaultModifyPwd) {
|
||||||
if(data.isDefaultModifyPwd) {
|
|
||||||
MessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
|
MessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
|
||||||
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
|
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
/* 过期密码提示 */
|
if (!data.isDefaultModifyPwd && data.isPasswordExpired) {
|
||||||
if(!data.isDefaultModifyPwd && data.isPasswordExpired) {
|
|
||||||
MessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
|
MessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
|
||||||
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
|
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
|
|
@ -102,7 +99,7 @@ const user = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// 退出系统 - 基于 Session 认证
|
// 退出系统 - 基于 Session 认证
|
||||||
LogOut({ commit, state }) {
|
LogOut({ commit }) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
logout().then(() => {
|
logout().then(() => {
|
||||||
commit('SET_ROLES', [])
|
commit('SET_ROLES', [])
|
||||||
|
|
|
||||||
|
|
@ -19,17 +19,17 @@ export function toFixed(value, dp = DEFAULT_DP) {
|
||||||
|
|
||||||
// 加法
|
// 加法
|
||||||
export function add(a, b, dp = DEFAULT_DP) {
|
export function add(a, b, dp = DEFAULT_DP) {
|
||||||
return D(a).plus(b).toDecimalPlaces(dp).toNumber()
|
return D(a).plus(D(b)).toDecimalPlaces(dp).toNumber()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 减法
|
// 减法
|
||||||
export function sub(a, b, dp = DEFAULT_DP) {
|
export function sub(a, b, dp = DEFAULT_DP) {
|
||||||
return D(a).minus(b).toDecimalPlaces(dp).toNumber()
|
return D(a).minus(D(b)).toDecimalPlaces(dp).toNumber()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 乘法
|
// 乘法
|
||||||
export function mul(a, b, dp = DEFAULT_DP) {
|
export function mul(a, b, dp = DEFAULT_DP) {
|
||||||
return D(a).times(b).toDecimalPlaces(dp).toNumber()
|
return D(a).times(D(b)).toDecimalPlaces(dp).toNumber()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 除法
|
// 除法
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,10 @@ export function formatDate(cellValue) {
|
||||||
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
|
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
|
||||||
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
|
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
|
||||||
}
|
}
|
||||||
|
export function formatCurrency(value) {
|
||||||
|
if (value == null) return '0.00';
|
||||||
|
return Number(value).toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @param {number} time
|
* @param {number} time
|
||||||
* @param {string} option
|
* @param {string} option
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,8 @@ service.interceptors.response.use(res => {
|
||||||
} else if (code === 601) {
|
} else if (code === 601) {
|
||||||
Message({ message: msg, type: 'warning' })
|
Message({ message: msg, type: 'warning' })
|
||||||
return Promise.reject('error')
|
return Promise.reject('error')
|
||||||
|
} else if (code === 301 && res.config.headers && res.config.headers.allowWarning) {
|
||||||
|
return res.data
|
||||||
} else if (code !== 200) {
|
} else if (code !== 200) {
|
||||||
Notification.error({ title: msg })
|
Notification.error({ title: msg })
|
||||||
return Promise.reject('error')
|
return Promise.reject('error')
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
const serviceHosts = (process.env.VUE_APP_SERVICE_HOST || '')
|
||||||
|
.split(',')
|
||||||
|
.map(host => host.trim().toLowerCase())
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
export function isServiceHost(hostname) {
|
||||||
|
const currentHost = (hostname || (typeof window !== 'undefined' ? window.location.hostname : '') || '')
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
|
||||||
|
return currentHost !== '' && serviceHosts.includes(currentHost)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,359 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="订单撤回审批"
|
||||||
|
:visible="dialogVisible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
width="80%"
|
||||||
|
top="5vh"
|
||||||
|
append-to-body
|
||||||
|
@update:visible="handleVisibleChange"
|
||||||
|
>
|
||||||
|
<div v-loading="loading" style="max-height: 70vh; overflow-y: auto;">
|
||||||
|
<div class="approve-container">
|
||||||
|
<ApproveLayout ref="approveLayout" title="紫光汇智信息技术有限公司">
|
||||||
|
<template #default>
|
||||||
|
<div style="display: flex;align-items: center;justify-content: center;">
|
||||||
|
<span style="margin-left: 10px;color: black;font-size: 30px">
|
||||||
|
紫光汇智信息技术有限公司订单撤回审批
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<el-form ref="orderForm" :model="order" label-width="120px" class="mb20">
|
||||||
|
<h3 class="section-title">订单信息</h3>
|
||||||
|
<order-info-display
|
||||||
|
:order-data.sync="order"
|
||||||
|
/>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<h3 class="section-title">配置信息</h3>
|
||||||
|
<config-info
|
||||||
|
:order-data="order"
|
||||||
|
class="mb20"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<p>{{ order.projectCode }}-{{ order.orderCode }}-Rev.{{ order.versionCode }}</p>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
|
||||||
|
<!-- 撤单信息 -->
|
||||||
|
<el-form label-width="120px" class="mb20">
|
||||||
|
<h3 class="section-title">撤单信息</h3>
|
||||||
|
<el-form-item label="撤单原因:">
|
||||||
|
<el-input :value="reason" readonly type="textarea" :rows="2" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="金额是否变化:">
|
||||||
|
<el-input :value="amountChanged || '—'" readonly />
|
||||||
|
</el-form-item>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="出库状态:">
|
||||||
|
<dict-tag :options="dict.type.execution_outer_status" :value="order.outerStatus"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="发货状态:">
|
||||||
|
<dict-tag :options="dict.type.execution_delivery_status" :value="order.deliveryStatus"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="签收状态:">
|
||||||
|
<dict-tag :options="dict.type.execution_sign_status" :value="order.signStatus"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 流转过程 -->
|
||||||
|
<el-tabs v-model="activeTab" class="approve-tabs">
|
||||||
|
<el-tab-pane label="流转过程" name="process">
|
||||||
|
<div class="process-container">
|
||||||
|
<el-tabs :value="activeVersionTab" @input="handleVersionTabChange" type="card" v-if="uniqueVersions.length > 0">
|
||||||
|
<el-tab-pane
|
||||||
|
v-for="version in uniqueVersions"
|
||||||
|
:key="version"
|
||||||
|
:label="'版本号Rev.' + version"
|
||||||
|
:name="version">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item
|
||||||
|
v-for="log in groupedApproveLogs[version]"
|
||||||
|
:key="log.id"
|
||||||
|
:timestamp="log.approveTime"
|
||||||
|
placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }}</p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="getStatusTagType(log.approveStatus)" size="small">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
<div v-else>暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer" v-if="showApprove && taskId">
|
||||||
|
<el-button @click="handleVisibleChange(false)">取 消</el-button>
|
||||||
|
<el-button type="danger" @click="openOpinionDialog('reject')">驳 回</el-button>
|
||||||
|
<el-button type="primary" @click="openOpinionDialog('approve')">同 意</el-button>
|
||||||
|
</span>
|
||||||
|
<span slot="footer" class="dialog-footer" v-else>
|
||||||
|
<el-button @click="handleVisibleChange(false)">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 审批意见对话框 -->
|
||||||
|
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="500px" append-to-body :close-on-click-modal="false" :close-on-press-escape="false">
|
||||||
|
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||||
|
<el-form-item label="审批意见" prop="approveOpinion">
|
||||||
|
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见" :disabled="submitLoading"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="opinionDialogVisible = false" :disabled="submitLoading">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="showConfirmDialog" :loading="submitLoading">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getOrder } from '@/api/approve/order/index'
|
||||||
|
import { listCompletedFlows, listApplyFlows, approveTask } from '@/api/flow'
|
||||||
|
import ConfigInfo from '@/views/approve/order/ConfigInfo.vue'
|
||||||
|
import ApproveLayout from '@/views/approve/ApproveLayout.vue'
|
||||||
|
import OrderInfoDisplay from '@/views/project/order/components/OrderInfoDisplay.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'OrderRebackDetail',
|
||||||
|
dicts: ['execution_outer_status', 'execution_delivery_status', 'execution_sign_status'],
|
||||||
|
components: {
|
||||||
|
ApproveLayout,
|
||||||
|
OrderInfoDisplay,
|
||||||
|
ConfigInfo
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 订单id,用于获取订单详情
|
||||||
|
orderId: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
// 合同编号(流程businessKey),用于查询审批流转记录
|
||||||
|
orderCode: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
processKey: {
|
||||||
|
type: String,
|
||||||
|
default: 'order_reback'
|
||||||
|
},
|
||||||
|
// 当前审批任务id,为空表示仅查看
|
||||||
|
taskId: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
// 是否显示审批按钮(由父组件控制,如当前用户是否为审批人)
|
||||||
|
showApprove: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
loading: false,
|
||||||
|
order: {},
|
||||||
|
logs: [],
|
||||||
|
reason: '',
|
||||||
|
amountChanged: '',
|
||||||
|
activeTab: 'process',
|
||||||
|
activeVersionTab: null,
|
||||||
|
confirmDialogTitle: '',
|
||||||
|
opinionDialogVisible: false,
|
||||||
|
currentApproveType: null,
|
||||||
|
submitLoading: false,
|
||||||
|
opinionForm: { approveOpinion: '' },
|
||||||
|
opinionRules: {
|
||||||
|
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 获取所有唯一的版本号
|
||||||
|
uniqueVersions() {
|
||||||
|
if (!this.logs || this.logs.length === 0) return []
|
||||||
|
const versions = [...new Set(this.logs.map(log => log.extendField1))].filter(v => v !== null && v !== undefined && v !== '')
|
||||||
|
return versions.sort((a, b) => b - a) // 降序排列
|
||||||
|
},
|
||||||
|
// 按版本号分组的审批记录
|
||||||
|
groupedApproveLogs() {
|
||||||
|
if (!this.logs || this.logs.length === 0) return {}
|
||||||
|
return this.logs.reduce((acc, log) => {
|
||||||
|
const version = log.extendField1
|
||||||
|
if (!acc[version]) acc[version] = []
|
||||||
|
acc[version].push(log)
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
visible(val) {
|
||||||
|
this.dialogVisible = val
|
||||||
|
if (val) {
|
||||||
|
this.loadDetail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 同步弹窗显隐到父组件,避免直接修改 prop
|
||||||
|
handleVisibleChange(val) {
|
||||||
|
this.dialogVisible = val
|
||||||
|
this.$emit('update:visible', val)
|
||||||
|
},
|
||||||
|
// 加载订单详情、撤单原因、流转意见
|
||||||
|
loadDetail() {
|
||||||
|
this.loading = true
|
||||||
|
this.order = {}
|
||||||
|
this.logs = []
|
||||||
|
this.reason = ''
|
||||||
|
this.amountChanged = ''
|
||||||
|
this.activeVersionTab = null
|
||||||
|
if (!this.orderId) {
|
||||||
|
this.loading = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getOrder(this.orderId).then(response => {
|
||||||
|
const data = response.data || {}
|
||||||
|
this.order = data.projectOrderInfo || {}
|
||||||
|
const businessKey = this.order.orderCode || this.orderCode
|
||||||
|
this.loadFlows(businessKey)
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('获取订单详情失败: ', error)
|
||||||
|
this.$modal.msgError('获取订单详情失败!')
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
loadFlows(businessKey) {
|
||||||
|
const processKeyList = this.processKey ? [this.processKey] : ['order_reback']
|
||||||
|
listCompletedFlows({ businessKey: businessKey, processKeyList }).then(res => {
|
||||||
|
this.logs = res.data || []
|
||||||
|
// 撤单原因存储于流程记录的扩展字段 extendField2,金额是否变化存储于 extendField3(extendField1 为订单版本号)
|
||||||
|
const applyLog = this.logs.find(log => log.extendField2)
|
||||||
|
if (applyLog) {
|
||||||
|
this.reason = applyLog.extendField2
|
||||||
|
this.amountChanged = applyLog.extendField3 || ''
|
||||||
|
} else {
|
||||||
|
// 审批中首节点已办表可能无记录,从待办表补充查询
|
||||||
|
listApplyFlows({ businessKey: businessKey, processKey: this.processKey }).then(todoRes => {
|
||||||
|
const todoList = todoRes.data || []
|
||||||
|
const apply = todoList.find(item => item.extendField2)
|
||||||
|
this.reason = apply ? apply.extendField2 : ''
|
||||||
|
this.amountChanged = apply ? (apply.extendField3 || '') : ''
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
if (this.uniqueVersions.length > 0) {
|
||||||
|
this.activeVersionTab = String(this.uniqueVersions[0])
|
||||||
|
}
|
||||||
|
this.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
this.logs = []
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openOpinionDialog(type) {
|
||||||
|
this.currentApproveType = type
|
||||||
|
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批'
|
||||||
|
this.opinionForm.approveOpinion = ''
|
||||||
|
this.opinionDialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.opinionForm && this.$refs.opinionForm.clearValidate()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
showConfirmDialog() {
|
||||||
|
this.$refs.opinionForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.opinionDialogVisible = false
|
||||||
|
this.submitApproval()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitApproval() {
|
||||||
|
this.submitLoading = true
|
||||||
|
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0
|
||||||
|
const params = {
|
||||||
|
businessKey: this.order.orderCode || this.orderCode,
|
||||||
|
processKey: this.processKey,
|
||||||
|
taskId: this.taskId,
|
||||||
|
variables: {
|
||||||
|
comment: this.opinionForm.approveOpinion,
|
||||||
|
approveBtn: approveBtn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
approveTask(params).then(() => {
|
||||||
|
this.submitLoading = false
|
||||||
|
this.$modal.msgSuccess(this.confirmDialogTitle + '成功')
|
||||||
|
this.handleVisibleChange(false)
|
||||||
|
this.$emit('success')
|
||||||
|
}).catch(error => {
|
||||||
|
this.submitLoading = false
|
||||||
|
console.error('审批失败: ', error)
|
||||||
|
this.$modal.msgError('审批失败!' + (error.msg || error.message))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getStatusTagType(status) {
|
||||||
|
const typeMap = {
|
||||||
|
'1': 'info', // 提交审批
|
||||||
|
'2': 'danger', // 驳回
|
||||||
|
'3': 'success' // 批准
|
||||||
|
}
|
||||||
|
return typeMap[String(status)] || 'info'
|
||||||
|
},
|
||||||
|
// 内层版本Tab切换:仅接受日志中存在的版本号,避免空值/非法值回写造成渲染循环
|
||||||
|
handleVersionTabChange(val) {
|
||||||
|
if (val !== null && val !== undefined && val !== '' && this.uniqueVersions.includes(val)) {
|
||||||
|
this.activeVersionTab = val
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
const textMap = { '1': '提交审批', '2': '驳回', '3': '批准' }
|
||||||
|
return textMap[String(status)] || '提交审批'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb20 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab样式 */
|
||||||
|
.approve-tabs {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 流转过程容器 */
|
||||||
|
.process-container {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 审批意见弹窗样式 */
|
||||||
|
::v-deep .el-dialog__body {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,312 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="出库撤回审批"
|
||||||
|
:visible="dialogVisible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
width="80%"
|
||||||
|
top="5vh"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
@update:visible="handleVisibleChange"
|
||||||
|
>
|
||||||
|
<div v-loading="loading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<el-form label-width="110px">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="出库单号:">
|
||||||
|
<el-input :value="form.outerCode" readonly />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="物流单号:">
|
||||||
|
<el-input :value="form.logisticsCode" readonly />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="物流公司:">
|
||||||
|
<el-input :value="form.logisticsCompany" readonly />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="仓库:">
|
||||||
|
<el-input :value="form.warehouseName" readonly />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="发货时间:">
|
||||||
|
<el-input :value="form.deliveryTime" readonly />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="发货数量:">
|
||||||
|
<el-input :value="form.quantity" readonly />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="收货地址:">
|
||||||
|
<el-input :value="form.notifierAddress" readonly />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="撤回原因:">
|
||||||
|
<el-input :value="reason" readonly type="textarea" :rows="2" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="金额是否变化:">
|
||||||
|
<el-input :value="amountChanged || '—'" readonly />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-divider content-position="left">SN码列表</el-divider>
|
||||||
|
<el-table v-loading="snLoading" :data="snList" border size="mini">
|
||||||
|
<el-table-column type="index" label="序号" align="center" width="60" />
|
||||||
|
<el-table-column label="SN码" align="center" prop="productSn"/>
|
||||||
|
<el-table-column label="授权码" align="center" prop="licenseKey"/>
|
||||||
|
<el-table-column label="产品编码" align="center" prop="productCode"/>
|
||||||
|
<el-table-column label="产品型号" align="center" prop="model"/>
|
||||||
|
<el-table-column label="描述" align="center" prop="productDesc"/>
|
||||||
|
<el-table-column label="仓库" align="center" prop="warehouseName"/>
|
||||||
|
</el-table>
|
||||||
|
<pagination v-show="snTotal>0" :total="snTotal" :page.sync="snQueryParams.pageNum" :limit.sync="snQueryParams.pageSize" @pagination="getSnList"/>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="log in logs" :key="log.id" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }}</p>
|
||||||
|
<p><b>审批状态:</b>
|
||||||
|
<el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag>
|
||||||
|
</p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!logs || logs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer" v-if="showApprove && taskId">
|
||||||
|
<el-button @click="handleVisibleChange(false)">取 消</el-button>
|
||||||
|
<el-button type="danger" @click="openOpinionDialog('reject')">驳 回</el-button>
|
||||||
|
<el-button type="primary" @click="openOpinionDialog('approve')">同 意</el-button>
|
||||||
|
</span>
|
||||||
|
<span slot="footer" class="dialog-footer" v-else>
|
||||||
|
<el-button @click="handleVisibleChange(false)">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 审批意见对话框 -->
|
||||||
|
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||||
|
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||||
|
<el-form-item label="审批意见" prop="approveOpinion">
|
||||||
|
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getDelivery, listProductSn } from '@/api/inventory/delivery'
|
||||||
|
import { listCompletedFlows, listApplyFlows, approveTask } from '@/api/flow'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'OuterRebackDetail',
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 发货记录id,用于获取发货详情与SN码列表
|
||||||
|
deliveryId: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
// 出库单号,用于查询审批流转记录
|
||||||
|
outerCode: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
processKey: {
|
||||||
|
type: String,
|
||||||
|
default: 'outer_reback'
|
||||||
|
},
|
||||||
|
// 当前审批任务id,为空表示仅查看
|
||||||
|
taskId: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
// 是否显示审批按钮(由父组件控制,如当前用户是否为审批人)
|
||||||
|
showApprove: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
loading: false,
|
||||||
|
form: {},
|
||||||
|
logs: [],
|
||||||
|
reason: '',
|
||||||
|
amountChanged: '',
|
||||||
|
// SN码列表
|
||||||
|
snLoading: false,
|
||||||
|
snList: [],
|
||||||
|
snTotal: 0,
|
||||||
|
snQueryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
deliveryId: null,
|
||||||
|
// 不按状态/出库单过滤:撤回后的SN已还原为入库状态且outer_code被清空
|
||||||
|
inventoryStatus: null,
|
||||||
|
outerCode: null,
|
||||||
|
warehouseId: null,
|
||||||
|
productSnList: []
|
||||||
|
},
|
||||||
|
confirmDialogTitle: '',
|
||||||
|
opinionDialogVisible: false,
|
||||||
|
currentApproveType: null,
|
||||||
|
opinionForm: { approveOpinion: '' },
|
||||||
|
opinionRules: {
|
||||||
|
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
visible(val) {
|
||||||
|
this.dialogVisible = val
|
||||||
|
if (val) {
|
||||||
|
this.loadDetail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 同步弹窗显隐到父组件,避免直接修改 prop
|
||||||
|
handleVisibleChange(val) {
|
||||||
|
this.dialogVisible = val
|
||||||
|
this.$emit('update:visible', val)
|
||||||
|
},
|
||||||
|
// 加载发货详情、撤回原因、流转意见、SN码列表
|
||||||
|
loadDetail() {
|
||||||
|
this.loading = true
|
||||||
|
this.form = {}
|
||||||
|
this.logs = []
|
||||||
|
this.reason = ''
|
||||||
|
this.amountChanged = ''
|
||||||
|
this.snList = []
|
||||||
|
this.snTotal = 0
|
||||||
|
if (!this.deliveryId) {
|
||||||
|
this.loading = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getDelivery(this.deliveryId).then(response => {
|
||||||
|
this.form = response.data || {}
|
||||||
|
// SN码列表查询参数(与发货单详情一致:不传 outer_code)
|
||||||
|
this.snQueryParams.deliveryId = this.deliveryId
|
||||||
|
this.snQueryParams.warehouseId = this.form.warehouseId
|
||||||
|
this.snQueryParams.productSnList = this.form.productSnList
|
||||||
|
this.getSnList()
|
||||||
|
const businessKey = this.form.outerCode || this.outerCode
|
||||||
|
this.loadFlows(businessKey)
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('获取发货记录详情失败: ', error)
|
||||||
|
this.$modal.msgError('获取发货记录详情失败!')
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 查询关联SN码列表
|
||||||
|
getSnList() {
|
||||||
|
this.snLoading = true
|
||||||
|
listProductSn(this.snQueryParams).then(res => {
|
||||||
|
this.snList = res.rows
|
||||||
|
this.snTotal = res.total
|
||||||
|
this.snLoading = false
|
||||||
|
}).catch(() => {
|
||||||
|
this.snLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
loadFlows(businessKey) {
|
||||||
|
const processKeyList = this.processKey ? [this.processKey] : ['outer_reback']
|
||||||
|
listCompletedFlows({ businessKey: businessKey, processKeyList }).then(res => {
|
||||||
|
this.logs = res.data || []
|
||||||
|
// 申请说明存储于流程发起记录的扩展字段 extend_field1,金额是否变化存储于 extend_field3
|
||||||
|
const applyLog = this.logs.find(log => log.extendField1)
|
||||||
|
if (applyLog) {
|
||||||
|
this.reason = applyLog.extendField1
|
||||||
|
this.amountChanged = applyLog.extendField3 || ''
|
||||||
|
} else {
|
||||||
|
// 审批中首节点已办表可能无记录,从待办表补充查询
|
||||||
|
listApplyFlows({ businessKey: businessKey, processKey: this.processKey }).then(todoRes => {
|
||||||
|
const todoList = todoRes.data || []
|
||||||
|
const apply = todoList.find(item => item.extendField1)
|
||||||
|
this.reason = apply ? apply.extendField1 : ''
|
||||||
|
this.amountChanged = apply ? (apply.extendField3 || '') : ''
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
this.loading = false
|
||||||
|
}).catch(() => {
|
||||||
|
this.logs = []
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openOpinionDialog(type) {
|
||||||
|
this.currentApproveType = type
|
||||||
|
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批'
|
||||||
|
this.opinionForm.approveOpinion = ''
|
||||||
|
this.opinionDialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.opinionForm && this.$refs.opinionForm.clearValidate()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
showConfirmDialog() {
|
||||||
|
this.$refs.opinionForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.opinionDialogVisible = false
|
||||||
|
this.submitApproval()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitApproval() {
|
||||||
|
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0
|
||||||
|
const params = {
|
||||||
|
businessKey: this.form.outerCode || this.outerCode,
|
||||||
|
processKey: this.processKey,
|
||||||
|
taskId: this.taskId,
|
||||||
|
variables: {
|
||||||
|
comment: this.opinionForm.approveOpinion,
|
||||||
|
approveBtn: approveBtn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
approveTask(params).then(() => {
|
||||||
|
this.$modal.msgSuccess(this.confirmDialogTitle + '成功')
|
||||||
|
this.handleVisibleChange(false)
|
||||||
|
this.$emit('success')
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('审批失败: ', error)
|
||||||
|
this.$modal.msgError('审批失败!' + (error.msg || error.message))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
const textMap = { '1': '提交审批', '2': '驳回', '3': '批准' }
|
||||||
|
return textMap[String(status)] || '提交审批'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -49,6 +49,22 @@
|
||||||
@keyup.enter.native="handleQuery"
|
@keyup.enter.native="handleQuery"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="产品编码" prop="productCode">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.productCode"
|
||||||
|
placeholder="请输入产品编码"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品型号" prop="productModel">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.productModel"
|
||||||
|
placeholder="请输入产品型号"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
|
@ -72,13 +88,18 @@
|
||||||
<el-table-column label="项目名称" align="center" prop="projectName" />
|
<el-table-column label="项目名称" align="center" prop="projectName" />
|
||||||
<el-table-column label="项目编号" align="center" prop="projectCode" />
|
<el-table-column label="项目编号" align="center" prop="projectCode" />
|
||||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||||
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" />
|
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" >
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ formatCurrency(scope.row.actualPurchaseAmount || scope.row.shipmentAmount) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="汇智负责人" align="center" prop="dutyName" />
|
<el-table-column label="汇智负责人" align="center" prop="dutyName" />
|
||||||
<el-table-column label="审批节点" align="center" prop="approveNode">
|
<el-table-column label="审批节点" align="center" prop="approveNode">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{ scope.row.approveNode || '-' }}</span>
|
<span>{{ scope.row.approveNode || '-' }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="提交时间" align="center" prop="approveTime" />
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
|
|
@ -127,6 +148,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { listOrder } from "@/api/approve/order/orderLog";
|
import { listOrder } from "@/api/approve/order/orderLog";
|
||||||
import ApproveDialog from '../order/Approve.vue';
|
import ApproveDialog from '../order/Approve.vue';
|
||||||
|
import {formatCurrency} from "../../../utils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ApprovedOrder",
|
name: "ApprovedOrder",
|
||||||
|
|
@ -153,10 +175,12 @@ export default {
|
||||||
orderCode: null,
|
orderCode: null,
|
||||||
projectName: null,
|
projectName: null,
|
||||||
projectCode: null,
|
projectCode: null,
|
||||||
|
productCode: null,
|
||||||
|
productModel: null,
|
||||||
customerName: null,
|
customerName: null,
|
||||||
dutyName: null,
|
dutyName: null,
|
||||||
approveNode: null,
|
approveNode: null,
|
||||||
orderByColumn:'t7.todo_approve_time',
|
orderByColumn:'t1.approveTime desc, t1.id',
|
||||||
isAsc: 'desc'
|
isAsc: 'desc'
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -165,6 +189,7 @@ export default {
|
||||||
this.getList();
|
this.getList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
formatCurrency,
|
||||||
/** 查询订单列表 */
|
/** 查询订单列表 */
|
||||||
getList() {
|
getList() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,259 @@
|
||||||
|
function escapeHtml(value) {
|
||||||
|
return String(value == null ? "" : value)
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCurrency(value) {
|
||||||
|
const num = Number(value);
|
||||||
|
if (Number.isNaN(num)) {
|
||||||
|
return "0.00";
|
||||||
|
}
|
||||||
|
return num.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProductTypeLabel(value) {
|
||||||
|
const map = {
|
||||||
|
"1": "软件",
|
||||||
|
"2": "电子计算机",
|
||||||
|
"3": "信息系统服务"
|
||||||
|
};
|
||||||
|
return map[String(value)] || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertCurrency(money) {
|
||||||
|
const cnNums = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
|
||||||
|
const cnIntRadice = ["", "拾", "佰", "仟"];
|
||||||
|
const cnIntUnits = ["", "万", "亿", "兆"];
|
||||||
|
const cnDecUnits = ["角", "分", "毫", "厘"];
|
||||||
|
const cnInteger = "整";
|
||||||
|
const cnIntLast = "元";
|
||||||
|
|
||||||
|
let integerNum;
|
||||||
|
let decimalNum;
|
||||||
|
let chineseStr = "";
|
||||||
|
let parts;
|
||||||
|
|
||||||
|
if (money === "") return "";
|
||||||
|
money = parseFloat(money);
|
||||||
|
if (money >= 999999999999) return "";
|
||||||
|
if (money === 0) return cnNums[0] + cnIntLast + cnInteger;
|
||||||
|
|
||||||
|
let prefix = "";
|
||||||
|
if (money < 0) {
|
||||||
|
prefix = "(负数)";
|
||||||
|
money = Math.abs(money);
|
||||||
|
}
|
||||||
|
|
||||||
|
money = money.toString();
|
||||||
|
if (money.indexOf(".") === -1) {
|
||||||
|
integerNum = money;
|
||||||
|
decimalNum = "";
|
||||||
|
} else {
|
||||||
|
parts = money.split(".");
|
||||||
|
integerNum = parts[0];
|
||||||
|
decimalNum = parts[1].substr(0, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parseInt(integerNum, 10) > 0) {
|
||||||
|
let zeroCount = 0;
|
||||||
|
const intLen = integerNum.length;
|
||||||
|
for (let i = 0; i < intLen; i++) {
|
||||||
|
const n = integerNum.substr(i, 1);
|
||||||
|
const p = intLen - i - 1;
|
||||||
|
const q = Math.floor(p / 4);
|
||||||
|
const m = p % 4;
|
||||||
|
if (n === "0") {
|
||||||
|
zeroCount++;
|
||||||
|
} else {
|
||||||
|
if (zeroCount > 0) chineseStr += cnNums[0];
|
||||||
|
zeroCount = 0;
|
||||||
|
chineseStr += cnNums[parseInt(n, 10)] + cnIntRadice[m];
|
||||||
|
}
|
||||||
|
if (m === 0 && zeroCount < 4) chineseStr += cnIntUnits[q];
|
||||||
|
}
|
||||||
|
chineseStr += cnIntLast;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decimalNum !== "") {
|
||||||
|
const decLen = decimalNum.length;
|
||||||
|
for (let i = 0; i < decLen; i++) {
|
||||||
|
const n = decimalNum.substr(i, 1);
|
||||||
|
if (n !== "0") chineseStr += cnNums[Number(n)] + cnDecUnits[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chineseStr === "") chineseStr += cnNums[0] + cnIntLast + cnInteger;
|
||||||
|
else if (decimalNum === "") chineseStr += cnInteger;
|
||||||
|
return prefix + chineseStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInvoiceTypeLabel(invoiceType) {
|
||||||
|
const map = {
|
||||||
|
"1": "增值税专用发票",
|
||||||
|
"2": "增值税普通发票",
|
||||||
|
"3": "电子发票"
|
||||||
|
};
|
||||||
|
return map[String(invoiceType)] || (invoiceType || "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildItemRows(items) {
|
||||||
|
if (!items || items.length === 0) {
|
||||||
|
return `
|
||||||
|
<tr>
|
||||||
|
<td class="cell center" colspan="12" style="height: 28px;">暂无明细</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.map((item) => `
|
||||||
|
<tr>
|
||||||
|
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">${escapeHtml(getProductTypeLabel(item.productType))}</td>
|
||||||
|
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">${escapeHtml(item.productName)}</td>
|
||||||
|
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;">${escapeHtml(item.productModel)}</td>
|
||||||
|
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;">${escapeHtml(item.unit)}</td>
|
||||||
|
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;">${escapeHtml(item.quantity)}</td>
|
||||||
|
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;">${formatCurrency(item.price)}</td>
|
||||||
|
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">${formatCurrency(item.allPrice)}</td>
|
||||||
|
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;">${escapeHtml(item.taxRate)}</td>
|
||||||
|
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;">${formatCurrency(item.taxAmount)}</td>
|
||||||
|
</tr>
|
||||||
|
`).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSummaryRow(items) {
|
||||||
|
let allPriceTotal = 0;
|
||||||
|
let taxTotal = 0;
|
||||||
|
(items || []).forEach((item) => {
|
||||||
|
allPriceTotal += Number(item.allPrice) || 0;
|
||||||
|
taxTotal += Number(item.taxAmount) || 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return `
|
||||||
|
<tr>
|
||||||
|
<td class="cell center bold" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="8">合计</td>
|
||||||
|
<td class="cell center bold" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">¥${formatCurrency(allPriceTotal)}</td>
|
||||||
|
<td class="cell center bold" align="center" style="text-align: center; mso-horizontal-align: center;">-</td>
|
||||||
|
<td class="cell center bold" align="center" style="text-align: center; mso-horizontal-align: center;">¥${formatCurrency(taxTotal)}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildInvoiceExcelHtml(invoice) {
|
||||||
|
const detailItems = invoice.detailItemList || [];
|
||||||
|
const totalAmountNumber = detailItems.reduce((sum, item) => sum + (Number(item.allPrice) || 0), 0).toFixed(2);
|
||||||
|
const totalAmountChinese = convertCurrency(totalAmountNumber);
|
||||||
|
const invoiceTypeLabel = getInvoiceTypeLabel(invoice.invoiceType);
|
||||||
|
|
||||||
|
return `<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="content-type" content="application/vnd.ms-excel; charset=UTF-8" />
|
||||||
|
<!--[if gte mso 9]><xml>
|
||||||
|
<x:ExcelWorkbook>
|
||||||
|
<x:ExcelWorksheets>
|
||||||
|
<x:ExcelWorksheet>
|
||||||
|
<x:Name>开票信息</x:Name>
|
||||||
|
<x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions>
|
||||||
|
</x:ExcelWorksheet>
|
||||||
|
</x:ExcelWorksheets>
|
||||||
|
</x:ExcelWorkbook>
|
||||||
|
</xml><![endif]-->
|
||||||
|
<style>
|
||||||
|
table { border-collapse: collapse; width: 1200px; font-family: "Microsoft YaHei", Arial, sans-serif; color: #8B4513; }
|
||||||
|
td { border: 1px solid #8B4513; padding: 6px; font-size: 12px; vertical-align: middle; mso-vertical-align: middle; mso-wrap-style: none; }
|
||||||
|
.no-border { border: none !important; }
|
||||||
|
.title { font-size: 26px; font-weight: bold; text-align: center; border: none !important; padding: 12px 0; }
|
||||||
|
.sub-title { font-size: 18px; font-weight: bold; text-align: center; border: none !important; padding-bottom: 8px; }
|
||||||
|
.label { width: 120px; font-weight: bold; text-align: center; }
|
||||||
|
.center { text-align: center; mso-horizontal-align: center; }
|
||||||
|
.right { text-align: right; mso-horizontal-align: right; }
|
||||||
|
.bold { font-weight: bold; }
|
||||||
|
.section-head { font-weight: bold; text-align: center; background: #f8f5f0; }
|
||||||
|
.cell { height: 26px; }
|
||||||
|
.total-main { font-weight: bold; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col style="width:100px;" />
|
||||||
|
<col style="width:100px;" />
|
||||||
|
<col style="width:100px;" />
|
||||||
|
<col style="width:100px;" />
|
||||||
|
<col style="width:80px;" />
|
||||||
|
<col style="width:80px;" />
|
||||||
|
<col style="width:80px;" />
|
||||||
|
<col style="width:100px;" />
|
||||||
|
<col style="width:100px;" />
|
||||||
|
<col style="width:100px;" />
|
||||||
|
<col style="width:80px;" />
|
||||||
|
<col style="width:100px;" />
|
||||||
|
</colgroup>
|
||||||
|
<tr>
|
||||||
|
<td class="no-border" colspan="2"></td>
|
||||||
|
<td class="title" colspan="8">电子发票(${escapeHtml(invoiceTypeLabel)})</td>
|
||||||
|
<td class="no-border" colspan="2" style="text-align: left; vertical-align: middle;">
|
||||||
|
<div><span class="bold">发票号码:</span>----------</div>
|
||||||
|
<div><span class="bold">开票日期:</span>----------</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="section-head" colspan="2" rowspan="2">购买方信息</td>
|
||||||
|
<td class="label" colspan="4">名称:${escapeHtml(invoice.buyerName)}</td>
|
||||||
|
<td class="section-head" colspan="2" rowspan="2">销售方信息</td>
|
||||||
|
<td class="label" colspan="4">名称:${escapeHtml(invoice.sellerName)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label" colspan="4">纳税人识别号:${escapeHtml(invoice.buyerCreditCode)}</td>
|
||||||
|
<td class="label" colspan="4">纳税人识别号:${escapeHtml(invoice.sellerCreditCode)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="section-head" colspan="2">税收大类</td>
|
||||||
|
<td class="section-head" colspan="2">项目名称</td>
|
||||||
|
<td class="section-head">规格型号</td>
|
||||||
|
<td class="section-head">单位</td>
|
||||||
|
<td class="section-head">数量</td>
|
||||||
|
<td class="section-head">单价</td>
|
||||||
|
<td class="section-head" colspan="2">金额</td>
|
||||||
|
<td class="section-head">税率%</td>
|
||||||
|
<td class="section-head">税额</td>
|
||||||
|
</tr>
|
||||||
|
${buildItemRows(detailItems)}
|
||||||
|
${buildSummaryRow(detailItems)}
|
||||||
|
<tr>
|
||||||
|
<td class="label total-main" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">价税合计(大写)</td>
|
||||||
|
<td class="total-main center" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="6">⊗ ${escapeHtml(totalAmountChinese)}</td>
|
||||||
|
<td class="label total-main" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">(小写)</td>
|
||||||
|
<td class="total-main center" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">¥${formatCurrency(totalAmountNumber)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label" colspan="2">备注</td>
|
||||||
|
<td colspan="10">${escapeHtml(invoice.remark)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label" colspan="2">信息说明</td>
|
||||||
|
<td colspan="10">${escapeHtml(invoice.informationNote)}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exportInvoiceInfoToExcel(invoiceData, fileName) {
|
||||||
|
const content = buildInvoiceExcelHtml(invoiceData || {});
|
||||||
|
const blob = new Blob(["\ufeff", content], {
|
||||||
|
type: "application/vnd.ms-excel;charset=utf-8;"
|
||||||
|
});
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = url;
|
||||||
|
link.download = fileName || "开票单.xls";
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="收票编号" prop="receiptNo">
|
||||||
|
<el-input v-model="queryParams.receiptNo" placeholder="请输入收票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="供应商" prop="vendorName">
|
||||||
|
<el-input v-model="queryParams.vendorName" placeholder="请输入供应商" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="invoiceReceiptList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="收票编号" align="center" prop="ticketBillCode" />
|
||||||
|
<el-table-column label="供应商" align="center" prop="vendorName" />
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 详情对话框 -->
|
||||||
|
<el-dialog title="收票单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="收票单详情">
|
||||||
|
<invoice-receipt-detail :data="form"></invoice-receipt-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span> {{ form.ticketBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listInvoiceReceiptApproved, getInvoiceReceipt } from "@/api/finance/invoiceReceipt";
|
||||||
|
import { listCompletedFlows } from "@/api/flow";
|
||||||
|
import InvoiceReceiptDetail from "../components/InvoiceReceiptDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "InvoiceReceiptApproved",
|
||||||
|
components: { InvoiceReceiptDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
invoiceReceiptList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
receiptNo: null,
|
||||||
|
vendorName: null,
|
||||||
|
processKey: 'fianance_ticket',
|
||||||
|
projectName: null,
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
currentInvoiceReceiptId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listInvoiceReceiptApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.invoiceReceiptList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
handleView(row) {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.currentInvoiceReceiptId = row.id;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getInvoiceReceipt(this.currentInvoiceReceiptId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.ticketBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `收票单-${this.form.receiptBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
<template>
|
||||||
|
<div class="invoice-receipt-detail">
|
||||||
|
<div style="text-align: center;font-weight:bold;font-size: 25px;">收票申请单</div>
|
||||||
|
<el-descriptions title="收票单信息" :column="3" border>
|
||||||
|
<el-descriptions-item label="采购-收票单编号">{{ data.ticketBillCode }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="制造商名称">{{ data.vendorName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="票据类型">
|
||||||
|
<dict-tag :options="dict.type.finance_invoice_type" :value="data.ticketType"/>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="含税总价(元)">{{ formatCurrency(data.totalPriceWithTax) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="未税总价(元)">{{ formatCurrency(data.totalPriceWithoutTax) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="税额(元)">{{ formatCurrency(data.taxAmount) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="预计收票时间">{{ data.ticketTime }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="制造商开票时间">{{ data.vendorTicketTime }}</el-descriptions-item>
|
||||||
|
<!-- <el-descriptions-item label="备注" :span="3">{{ data.remark }}</el-descriptions-item>-->
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-show="data.detailList && data.detailList.length>0">
|
||||||
|
<div class="el-descriptions__title">发票明细列表</div>
|
||||||
|
<el-table :data="data.detailList" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="payableBillCode" label="采购-应付单编号" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="productType" label="产品类型" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||||
|
<el-table-column prop="paymentAmount" label="本次收票金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
|
||||||
|
<div class="el-descriptions__title">附件信息</div>
|
||||||
|
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createByName" label="上传人" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
|
||||||
|
<el-table-column label="操作" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||||
|
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||||
|
<img :src="currentImageUrl" style="width: 100%;" />
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getInvoiceReceiptAttachments } from "@/api/finance/invoiceReceipt";
|
||||||
|
import request from '@/utils/request';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "InvoiceReceiptDetail",
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dicts: ['finance_invoice_type', 'product_type'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
attachments: [],
|
||||||
|
pdfPreviewVisible: false,
|
||||||
|
currentPdfUrl: '',
|
||||||
|
imagePreviewVisible: false,
|
||||||
|
currentImageUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'data.id': {
|
||||||
|
handler(val) {
|
||||||
|
if (val) {
|
||||||
|
this.fetchAttachments(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchAttachments(id) {
|
||||||
|
getInvoiceReceiptAttachments(id,{ type: 'ticket' }).then(response => {
|
||||||
|
this.attachments = (response.data || []).filter(item => item.delFlag !== '2');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isPdf(filePath) {
|
||||||
|
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||||
|
},
|
||||||
|
getImageUrl(resource) {
|
||||||
|
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||||
|
},
|
||||||
|
handlePreview(row) {
|
||||||
|
if (this.isPdf(row.filePath)) {
|
||||||
|
request({
|
||||||
|
url: '/common/download/resource',
|
||||||
|
method: 'get',
|
||||||
|
params: { resource: row.filePath },
|
||||||
|
responseType: 'blob'
|
||||||
|
}).then(res => {
|
||||||
|
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||||
|
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||||
|
this.pdfPreviewVisible = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||||
|
this.imagePreviewVisible = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDownload(row) {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = this.getImageUrl(row.filePath);
|
||||||
|
link.download = row.fileName || 'attachment';
|
||||||
|
link.style.display = 'none';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.invoice-receipt-detail {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,302 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="收票编号" prop="receiptNo">
|
||||||
|
<el-input v-model="queryParams.receiptNo" placeholder="请输入收票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="供应商" prop="vendorName">
|
||||||
|
<el-input v-model="queryParams.vendorName" placeholder="请输入供应商" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
@click="toApproved()"
|
||||||
|
>审批历史</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="invoiceReceiptList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="收票编号" align="center" prop="ticketBillCode" />
|
||||||
|
<el-table-column label="制造商" align="center" prop="vendorName" />
|
||||||
|
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||||
|
<!-- <el-table-column label="登记人" align="center" prop="createUserName" />-->
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 审批详情主对话框 -->
|
||||||
|
<el-dialog title="收票单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="收票单详情">
|
||||||
|
<invoice-receipt-detail :data="form"></invoice-receipt-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span>{{ form.ticketBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b><el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||||
|
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||||
|
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 审批意见对话框 -->
|
||||||
|
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||||
|
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||||
|
<el-form-item label="审批意见" prop="approveOpinion">
|
||||||
|
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listInvoiceReceiptApprove, getInvoiceReceipt } from "@/api/finance/invoiceReceipt";
|
||||||
|
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||||
|
import InvoiceReceiptDetail from "./components/InvoiceReceiptDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "InvoiceReceiptApprove",
|
||||||
|
components: { InvoiceReceiptDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
invoiceReceiptList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
receiptNo: null,
|
||||||
|
vendorName: null,
|
||||||
|
processKey: 'fianance_ticket',
|
||||||
|
projectName: null,
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
opinionDialogVisible: false,
|
||||||
|
confirmDialogTitle: '',
|
||||||
|
currentApproveType: '',
|
||||||
|
opinionForm: {
|
||||||
|
approveOpinion: ''
|
||||||
|
},
|
||||||
|
opinionRules: {
|
||||||
|
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
|
},
|
||||||
|
processKey: 'fianance_ticket',
|
||||||
|
taskId: null,
|
||||||
|
currentInvoiceReceiptId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listInvoiceReceiptApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.invoiceReceiptList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
toApproved() {
|
||||||
|
this.$router.push( '/approve/invoiceLog' )
|
||||||
|
},
|
||||||
|
handleApprove(row) {
|
||||||
|
this.resetDetailForm();
|
||||||
|
this.currentInvoiceReceiptId = row.id;
|
||||||
|
this.taskId = row.taskId;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getInvoiceReceipt(this.currentInvoiceReceiptId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.ticketBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
resetDetailForm() {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
let keys = [];
|
||||||
|
if(this.processKey) keys.push(this.processKey);
|
||||||
|
|
||||||
|
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openOpinionDialog(type) {
|
||||||
|
this.currentApproveType = type;
|
||||||
|
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||||
|
this.opinionDialogVisible = true;
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showConfirmDialog() {
|
||||||
|
this.$refs.opinionForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.opinionDialogVisible = false;
|
||||||
|
this.submitApproval();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
submitApproval() {
|
||||||
|
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||||
|
const params = {
|
||||||
|
businessKey: this.form.ticketBillCode,
|
||||||
|
processKey: this.processKey,
|
||||||
|
taskId: this.taskId,
|
||||||
|
variables: {
|
||||||
|
comment: this.opinionForm.approveOpinion,
|
||||||
|
approveBtn: approveBtn
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
approveTask(params).then(() => {
|
||||||
|
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||||
|
this.detailDialogVisible = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
if (!status) {
|
||||||
|
return '提交审批'
|
||||||
|
}
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `收票单-${this.form.ticketBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,214 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="收票编号" prop="receiptNo">
|
||||||
|
<el-input v-model="queryParams.receiptNo" placeholder="请输入收票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="供应商" prop="vendorName">
|
||||||
|
<el-input v-model="queryParams.vendorName" placeholder="请输入供应商" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="invoiceReceiptList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="收票编号" align="center" prop="ticketBillCode" />
|
||||||
|
<el-table-column label="供应商" align="center" prop="vendorName" />
|
||||||
|
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 详情对话框 -->
|
||||||
|
<el-dialog title="红冲发票详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="红冲发票详情">
|
||||||
|
<invoice-red-detail :data="form"></invoice-red-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span> {{ form.ticketBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listInvoiceReceiptApproved, getInvoiceReceipt } from "@/api/finance/invoiceReceipt";
|
||||||
|
import { listCompletedFlows } from "@/api/flow";
|
||||||
|
import InvoiceRedDetail from "../components/InvoiceRedDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "InvoiceRedApproved",
|
||||||
|
components: { InvoiceRedDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
invoiceReceiptList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
receiptNo: null,
|
||||||
|
vendorName: null,
|
||||||
|
processKey: 'finance_ticket_refound',
|
||||||
|
projectName: null,
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
currentInvoiceReceiptId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listInvoiceReceiptApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.invoiceReceiptList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
handleView(row) {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.currentInvoiceReceiptId = row.id;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getInvoiceReceipt(this.currentInvoiceReceiptId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.ticketBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `红冲发票-${this.form.receiptBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
<template>
|
||||||
|
<div class="invoice-red-detail">
|
||||||
|
<div style="text-align: center;font-weight:bold;font-size: 25px;">红冲收票申请单</div>
|
||||||
|
<el-descriptions title="红冲发票信息" :column="3" border>
|
||||||
|
<el-descriptions-item label="采购-收票单编号">{{ data.ticketBillCode }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="制造商名称">{{ data.vendorName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="票据类型">
|
||||||
|
<dict-tag :options="dict.type.finance_invoice_type" :value="data.ticketType"/>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="含税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithTax) }} </span></el-descriptions-item>
|
||||||
|
<el-descriptions-item label="未税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithTax) }}</span></el-descriptions-item>
|
||||||
|
<el-descriptions-item label="税额(元)"><span style="color: red">{{ formatCurrency(data.taxAmount) }} </span></el-descriptions-item>
|
||||||
|
<el-descriptions-item label="收票时间">{{ data.ticketTime }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="制造商开票时间">{{ data.vendorTicketTime }}</el-descriptions-item>
|
||||||
|
<!-- <el-descriptions-item label="备注" :span="3">{{ data.remark }}</el-descriptions-item>-->
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-show="data.detailList && data.detailList.length>0">
|
||||||
|
<div class="el-descriptions__title">发票明细列表</div>
|
||||||
|
<el-table :data="data.detailList" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="payableBillCode" label="采购-应付单编号" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="productType" label="产品类型" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||||
|
<el-table-column prop="paymentAmount" label="本次红冲金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
|
||||||
|
<div class="el-descriptions__title">附件信息</div>
|
||||||
|
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createByName" label="上传人" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
|
||||||
|
<el-table-column label="操作" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||||
|
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||||
|
<img :src="currentImageUrl" style="width: 100%;" />
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getInvoiceReceiptAttachments } from "@/api/finance/invoiceReceipt";
|
||||||
|
import request from '@/utils/request';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "InvoiceRedDetail",
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dicts: ['finance_invoice_type', 'product_type'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
attachments: [],
|
||||||
|
pdfPreviewVisible: false,
|
||||||
|
currentPdfUrl: '',
|
||||||
|
imagePreviewVisible: false,
|
||||||
|
currentImageUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'data.id': {
|
||||||
|
handler(val) {
|
||||||
|
if (val) {
|
||||||
|
this.fetchAttachments(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchAttachments(id) {
|
||||||
|
// 保持调用 invoiceReceipt 的附件接口,因为接口一致
|
||||||
|
getInvoiceReceiptAttachments(id,{ type: 'ticket' }).then(response => {
|
||||||
|
this.attachments = (response.data || []).filter(item => item.delFlag !== '2');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isPdf(filePath) {
|
||||||
|
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||||
|
},
|
||||||
|
getImageUrl(resource) {
|
||||||
|
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||||
|
},
|
||||||
|
handlePreview(row) {
|
||||||
|
if (this.isPdf(row.filePath)) {
|
||||||
|
request({
|
||||||
|
url: '/common/download/resource',
|
||||||
|
method: 'get',
|
||||||
|
params: { resource: row.filePath },
|
||||||
|
responseType: 'blob'
|
||||||
|
}).then(res => {
|
||||||
|
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||||
|
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||||
|
this.pdfPreviewVisible = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||||
|
this.imagePreviewVisible = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDownload(row) {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = this.getImageUrl(row.filePath);
|
||||||
|
link.download = row.fileName || 'attachment';
|
||||||
|
link.style.display = 'none';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.invoice-red-detail {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,304 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="收票编号" prop="receiptNo">
|
||||||
|
<el-input v-model="queryParams.receiptNo" placeholder="请输入收票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="供应商" prop="vendorName">
|
||||||
|
<el-input v-model="queryParams.vendorName" placeholder="请输入供应商" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
@click="toApproved()"
|
||||||
|
>审批历史</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="invoiceReceiptList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="收票编号" align="center" prop="ticketBillCode" />
|
||||||
|
<el-table-column label="供应商" align="center" prop="vendorName" />
|
||||||
|
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)">
|
||||||
|
|
||||||
|
</el-table-column>
|
||||||
|
<!-- <el-table-column label="登记人" align="center" prop="createUserName" />-->
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 审批详情主对话框 -->
|
||||||
|
<el-dialog title="红冲发票审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="红冲发票详情">
|
||||||
|
<invoice-red-detail :data="form"></invoice-red-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span> {{ form.ticketBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||||
|
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||||
|
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 审批意见对话框 -->
|
||||||
|
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||||
|
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||||
|
<el-form-item label="审批意见" prop="approveOpinion">
|
||||||
|
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listInvoiceReceiptApprove, getInvoiceReceipt } from "@/api/finance/invoiceReceipt";
|
||||||
|
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||||
|
import InvoiceRedDetail from "./components/InvoiceRedDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "InvoiceRedApprove",
|
||||||
|
components: { InvoiceRedDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
invoiceReceiptList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
receiptNo: null,
|
||||||
|
vendorName: null,
|
||||||
|
processKey: 'finance_ticket_refound',
|
||||||
|
projectName: null,
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
opinionDialogVisible: false,
|
||||||
|
confirmDialogTitle: '',
|
||||||
|
currentApproveType: '',
|
||||||
|
opinionForm: {
|
||||||
|
approveOpinion: ''
|
||||||
|
},
|
||||||
|
opinionRules: {
|
||||||
|
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
|
},
|
||||||
|
processKey: 'finance_ticket_refound',
|
||||||
|
taskId: null,
|
||||||
|
currentInvoiceReceiptId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listInvoiceReceiptApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.invoiceReceiptList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
toApproved() {
|
||||||
|
this.$router.push( '/approve/invoiceRedLog' )
|
||||||
|
},
|
||||||
|
handleApprove(row) {
|
||||||
|
this.resetDetailForm();
|
||||||
|
this.currentInvoiceReceiptId = row.id;
|
||||||
|
this.taskId = row.taskId;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getInvoiceReceipt(this.currentInvoiceReceiptId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.ticketBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
resetDetailForm() {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
let keys = [];
|
||||||
|
if(this.processKey) keys.push(this.processKey);
|
||||||
|
|
||||||
|
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openOpinionDialog(type) {
|
||||||
|
this.currentApproveType = type;
|
||||||
|
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||||
|
this.opinionDialogVisible = true;
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showConfirmDialog() {
|
||||||
|
this.$refs.opinionForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.opinionDialogVisible = false;
|
||||||
|
this.submitApproval();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
submitApproval() {
|
||||||
|
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||||
|
const params = {
|
||||||
|
businessKey: this.form.ticketBillCode,
|
||||||
|
processKey: this.processKey,
|
||||||
|
taskId: this.taskId,
|
||||||
|
variables: {
|
||||||
|
comment: this.opinionForm.approveOpinion,
|
||||||
|
approveBtn: approveBtn
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
approveTask(params).then(() => {
|
||||||
|
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||||
|
this.detailDialogVisible = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
if (!status) {
|
||||||
|
return '提交审批'
|
||||||
|
}
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `红冲发票-${this.form.ticketBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,215 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="付款编号" prop="paymentNo">
|
||||||
|
<el-input v-model="queryParams.paymentNo" placeholder="请输入付款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="制造商" prop="manufacturer">
|
||||||
|
<el-input v-model="queryParams.manufacturer" placeholder="请输入制造商" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="paymentList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="付款编号" align="center" prop="paymentBillCode" />
|
||||||
|
<el-table-column label="制造商" align="center" prop="vendorName" />
|
||||||
|
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||||
|
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 详情对话框 -->
|
||||||
|
<el-dialog title="付款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="付款单详情">
|
||||||
|
<payment-detail :data="form"></payment-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span> {{ form.paymentBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b><el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listPaymentApproved, getPayment } from "@/api/finance/payment";
|
||||||
|
import { listCompletedFlows } from "@/api/flow";
|
||||||
|
import PaymentDetail from "../components/PaymentDetail"; // Relative path adjustment
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "PaymentApproved",
|
||||||
|
components: { PaymentDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
paymentList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
paymentNo: null,
|
||||||
|
manufacturer: null,
|
||||||
|
projectName: null,
|
||||||
|
processKey: 'finance_payment',
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
currentPaymentId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listPaymentApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.paymentList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
handleView(row) {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.currentPaymentId = row.id;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getPayment(this.currentPaymentId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.paymentBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,284 @@
|
||||||
|
<template>
|
||||||
|
<div class="payment-detail">
|
||||||
|
<div style="text-align: center;font-weight:bold;font-size: 25px;">付款申请单</div>
|
||||||
|
<el-descriptions title="付款单信息" :column="3" border>
|
||||||
|
<el-descriptions-item label="采购-付款单编号">{{ data.paymentBillCode }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="制造商名称">{{ data.vendorName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="付款条件">
|
||||||
|
{{data.payType==='0'?'入库付款':'出库付款'}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="付款周期">
|
||||||
|
{{data.payConfigDay}}天
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="含税总价(元)">{{ formatCurrency(data.totalPriceWithTax) }}</el-descriptions-item>
|
||||||
|
<!-- <el-descriptions-item label="未税总价(元)">{{ data.totalPriceWithoutTax }}</el-descriptions-item>-->
|
||||||
|
<!-- <el-descriptions-item label="税额(元)">{{ data.taxAmount }}</el-descriptions-item>-->
|
||||||
|
<el-descriptions-item label="支付方式">
|
||||||
|
<dict-tag :options="dict.type.payment_method" :value="data.paymentMethod"/>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="银行账号">{{ data.payBankNumber }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="账户名称">{{ data.payName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="银行开户行">{{ data.payBankOpenAddress }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item span="2"></el-descriptions-item>
|
||||||
|
<el-descriptions-item label="其它特别说明" :span="3">{{ data.remark }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" >
|
||||||
|
<div class="el-descriptions__title">应付单信息</div>
|
||||||
|
<el-table :data="data.payableDetails" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||||
|
<el-table-column label="采购单号" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div class="purchase-no-cell">
|
||||||
|
<el-link
|
||||||
|
v-for="(purchaseItem, index) in getPurchaseNoList(scope.row.purchaseNo)"
|
||||||
|
:key="`${scope.$index}-${purchaseItem.purchaseNo}-${purchaseItem.purchaseId || ''}-${index}`"
|
||||||
|
type="primary"
|
||||||
|
:underline="false"
|
||||||
|
class="purchase-no-link"
|
||||||
|
@click="handleViewPruchaseDetail(scope.row, purchaseItem)"
|
||||||
|
>
|
||||||
|
{{ purchaseItem.purchaseNo }}
|
||||||
|
</el-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="采购-应付单编号" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="handleViewPayable(scope.row)">{{ scope.row.payableBillCode }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="项目名称" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<a @click="handleViewProject(scope.row)" class="link-type">{{ scope.row.projectName }}</a>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="productCode" label="产品编码" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="model" label="产品型号" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="productName" label="产品名称" align="center"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<div class="el-descriptions__title">附件信息</div>
|
||||||
|
<el-button type="success" size="mini" icon="el-icon-download" :loading="zipDownloading" @click="handleDownloadAllAttachments">一键下载附件</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="data.fileList" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
|
||||||
|
<el-table-column label="操作" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||||
|
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||||
|
<img :src="currentImageUrl" style="width: 100%;" />
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<edit-form :visible.sync="payableVisible" :data="selectedPayableRow" :z-index="2000" @close="payableVisible = false" />
|
||||||
|
<project-detail-drawer :visible.sync="projectDrawerVisible" :project-id="currentProjectId" />
|
||||||
|
|
||||||
|
<el-drawer
|
||||||
|
title="采购单详情"
|
||||||
|
:visible.sync="showPruchaseDetailDrawer"
|
||||||
|
direction="rtl"
|
||||||
|
size="80%"
|
||||||
|
append-to-body
|
||||||
|
:modal-append-to-body="true"
|
||||||
|
:z-index="pruchaseDrawerZIndex"
|
||||||
|
>
|
||||||
|
<ApproveLayout title="采购单详情" v-if="showPruchaseDetailDrawer">
|
||||||
|
<purchase-order-detail-view
|
||||||
|
ref="pruchaseDetailView"
|
||||||
|
:order-data="pruchaseDetailOrderData"
|
||||||
|
@close="showPruchaseDetailDrawer = false"
|
||||||
|
>
|
||||||
|
</purchase-order-detail-view>
|
||||||
|
<template #footer>
|
||||||
|
<span>采购单编号: {{ pruchaseDetailOrderData ? pruchaseDetailOrderData.purchaseNo : '' }}</span>
|
||||||
|
<span v-if="pruchaseDetailOrderData"> | 版本号: {{ pruchaseDetailOrderData.version }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import request from '@/utils/request';
|
||||||
|
import JSZip from 'jszip';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import { getPurchaseorder } from "@/api/sip/purchaseorder";
|
||||||
|
import {formatCurrency} from "@/utils";
|
||||||
|
import EditForm from "@/views/finance/payable/components/EditForm.vue";
|
||||||
|
import ProjectDetailDrawer from "@/views/project/info/ProjectDetailDrawer.vue";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout.vue";
|
||||||
|
import PurchaseOrderDetailView from "@/views/purchaseorder/components/PurchaseOrderDetailView.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "PaymentDetail",
|
||||||
|
components: { EditForm, ProjectDetailDrawer, ApproveLayout, PurchaseOrderDetailView },
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dicts: ['payment_bill_type', 'product_type','payment_method'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
attachments: [],
|
||||||
|
zipDownloading: false,
|
||||||
|
pdfPreviewVisible: false,
|
||||||
|
currentPdfUrl: '',
|
||||||
|
imagePreviewVisible: false,
|
||||||
|
currentImageUrl: '',
|
||||||
|
payableVisible: false,
|
||||||
|
selectedPayableRow: {},
|
||||||
|
projectDrawerVisible: false,
|
||||||
|
currentProjectId: null,
|
||||||
|
showPruchaseDetailDrawer: false,
|
||||||
|
pruchaseDetailOrderData: null,
|
||||||
|
pruchaseDrawerZIndex: 5000
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatCurrency,
|
||||||
|
|
||||||
|
isPdf(filePath) {
|
||||||
|
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||||
|
},
|
||||||
|
getImageUrl(resource) {
|
||||||
|
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||||
|
},
|
||||||
|
handlePreview(row) {
|
||||||
|
if (this.isPdf(row.filePath)) {
|
||||||
|
// PDF Preview logic
|
||||||
|
request({
|
||||||
|
url: '/common/download/resource',
|
||||||
|
method: 'get',
|
||||||
|
params: { resource: row.filePath },
|
||||||
|
responseType: 'blob'
|
||||||
|
}).then(res => {
|
||||||
|
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||||
|
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||||
|
this.pdfPreviewVisible = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Image Preview
|
||||||
|
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||||
|
this.imagePreviewVisible = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDownload(row) {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = this.getImageUrl(row.filePath);
|
||||||
|
link.download = row.fileName || 'attachment';
|
||||||
|
link.style.display = 'none';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
},
|
||||||
|
async handleDownloadAllAttachments() {
|
||||||
|
const files = Array.isArray(this.data.fileList) ? this.data.fileList : [];
|
||||||
|
if (files.length === 0) {
|
||||||
|
this.$modal.msgWarning('暂无可下载的附件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.zipDownloading = true;
|
||||||
|
this.$modal.loading('正在打包附件,请稍候...');
|
||||||
|
try {
|
||||||
|
const zip = new JSZip();
|
||||||
|
for (const row of files) {
|
||||||
|
const blob = await request({
|
||||||
|
url: '/common/download/resource',
|
||||||
|
method: 'get',
|
||||||
|
params: { resource: row.filePath },
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 0
|
||||||
|
}).then(res => res.data);
|
||||||
|
zip.file(row.fileName || 'file', blob);
|
||||||
|
}
|
||||||
|
const content = await zip.generateAsync({ type: 'blob' });
|
||||||
|
saveAs(content, (this.data.paymentBillCode || '付款申请单附件') + '.zip');
|
||||||
|
this.$modal.msgSuccess('下载成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('附件打包下载失败', error);
|
||||||
|
this.$modal.msgError('附件下载失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.zipDownloading = false;
|
||||||
|
this.$modal.closeLoading();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleViewPayable(row) {
|
||||||
|
row.id = row.payableBillId;
|
||||||
|
this.selectedPayableRow = row;
|
||||||
|
this.payableVisible = true;
|
||||||
|
},
|
||||||
|
handleViewProject(row) {
|
||||||
|
this.currentProjectId = row.projectId;
|
||||||
|
this.projectDrawerVisible = true;
|
||||||
|
},
|
||||||
|
getPurchaseNoList(purchaseNo) {
|
||||||
|
return (purchaseNo || '')
|
||||||
|
.split(',')
|
||||||
|
.map(item => item.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(item => {
|
||||||
|
const splitIndex = item.lastIndexOf('__');
|
||||||
|
if (splitIndex === -1) {
|
||||||
|
return { purchaseNo: item, purchaseId: null };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
purchaseNo: item.substring(0, splitIndex),
|
||||||
|
purchaseId: item.substring(splitIndex + 2)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleViewPruchaseDetail(row, purchaseItem) {
|
||||||
|
this.pruchaseDrawerZIndex += 2;
|
||||||
|
const purchaseId = purchaseItem && purchaseItem.purchaseId ? purchaseItem.purchaseId : (row.purchaseId || row.purchaseOrderId);
|
||||||
|
if (purchaseId) {
|
||||||
|
getPurchaseorder(purchaseId).then(response => {
|
||||||
|
this.pruchaseDetailOrderData = response.data;
|
||||||
|
this.showPruchaseDetailDrawer = true;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.pruchaseDetailOrderData = {
|
||||||
|
purchaseNo: (purchaseItem && purchaseItem.purchaseNo) || row.purchaseNo || '',
|
||||||
|
version: row.version || ''
|
||||||
|
};
|
||||||
|
this.showPruchaseDetailDrawer = true;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.payment-detail {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.purchase-no-cell {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.purchase-no-link {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,314 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="付款编号" prop="paymentNo">
|
||||||
|
<el-input v-model="queryParams.paymentNo" placeholder="请输入付款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="制造商" prop="manufacturer">
|
||||||
|
<el-input v-model="queryParams.manufacturer" placeholder="请输入制造商" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
@click="toApproved()"
|
||||||
|
>审批历史</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="paymentList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="付款编号" align="center" prop="paymentBillCode" />
|
||||||
|
<el-table-column label="制造商" align="center" prop="vendorName" />
|
||||||
|
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
|
||||||
|
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 审批详情主对话框 -->
|
||||||
|
<el-dialog title="付款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="付款单详情">
|
||||||
|
<payment-detail :data="form"></payment-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span>付款编号: {{ form.paymentBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b>
|
||||||
|
|
||||||
|
<el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||||
|
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||||
|
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 审批意见对话框 -->
|
||||||
|
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||||
|
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||||
|
<el-form-item label="审批意见" prop="approveOpinion">
|
||||||
|
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listPaymentApprove, getPayment } from "@/api/finance/payment";
|
||||||
|
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||||
|
import PaymentDetail from "./components/PaymentDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "PaymentApprove",
|
||||||
|
components: { PaymentDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
paymentList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
paymentNo: null,
|
||||||
|
manufacturer: null,
|
||||||
|
projectName: null,
|
||||||
|
processKey: 'finance_payment',
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
opinionDialogVisible: false,
|
||||||
|
confirmDialogTitle: '',
|
||||||
|
currentApproveType: '',
|
||||||
|
opinionForm: {
|
||||||
|
approveOpinion: ''
|
||||||
|
},
|
||||||
|
opinionRules: {
|
||||||
|
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
|
},
|
||||||
|
processKey: 'finance_payment',
|
||||||
|
taskId: null,
|
||||||
|
currentPaymentId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listPaymentApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.paymentList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
toApproved() {
|
||||||
|
this.$router.push( '/approve/paymentLog' )
|
||||||
|
},
|
||||||
|
handleApprove(row) {
|
||||||
|
this.resetDetailForm();
|
||||||
|
this.currentPaymentId = row.id;
|
||||||
|
this.taskId = row.taskId;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getPayment(this.currentPaymentId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.paymentBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
resetDetailForm() {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
// Assuming processKeyList might be generic or specific, using generic fetch for now
|
||||||
|
// Usually need to know the specific process key. For payment, it might be 'payment_approval' etc.
|
||||||
|
// However, listCompletedFlows logic in reference used specific keys.
|
||||||
|
// If I don't know the key, maybe I can omit it or guess.
|
||||||
|
// The reference used `processKeyList: ['purchase_order_online']`.
|
||||||
|
// I will use a generic one or try to find it.
|
||||||
|
// If row.processKey is available I can use that.
|
||||||
|
let keys = [];
|
||||||
|
console.log(this.processKey)
|
||||||
|
if(this.processKey) keys.push(this.processKey);
|
||||||
|
|
||||||
|
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openOpinionDialog(type) {
|
||||||
|
this.currentApproveType = type;
|
||||||
|
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||||
|
this.opinionDialogVisible = true;
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showConfirmDialog() {
|
||||||
|
this.$refs.opinionForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.opinionDialogVisible = false;
|
||||||
|
this.submitApproval();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
submitApproval() {
|
||||||
|
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||||
|
const params = {
|
||||||
|
businessKey: this.form.paymentBillCode,
|
||||||
|
processKey: this.processKey,
|
||||||
|
taskId: this.taskId,
|
||||||
|
variables: {
|
||||||
|
comment: this.opinionForm.approveOpinion,
|
||||||
|
approveBtn: approveBtn
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
approveTask(params).then(() => {
|
||||||
|
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||||
|
this.detailDialogVisible = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
if (!status) {
|
||||||
|
return '提交审批'
|
||||||
|
}
|
||||||
|
// Map status codes to text
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,219 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="付款编号" prop="paymentNo">
|
||||||
|
<el-input v-model="queryParams.paymentNo" placeholder="请输入付款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="制造商" prop="manufacturer">
|
||||||
|
<el-input v-model="queryParams.manufacturer" placeholder="请输入制造商" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="paymentList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="付款编号" align="center" prop="paymentBillCode" />
|
||||||
|
<el-table-column label="制造商" align="center" prop="vendorName" />
|
||||||
|
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" >
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="color: red">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 详情对话框 -->
|
||||||
|
<el-dialog title="付款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="付款单详情">
|
||||||
|
<payment-detail :data="form"></payment-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span> {{ form.paymentBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listPaymentApproved, getPayment } from "@/api/finance/payment";
|
||||||
|
import { listCompletedFlows } from "@/api/flow";
|
||||||
|
import PaymentDetail from "../components/PaymentRefundDetail.vue"; // Relative path adjustment
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "PaymentApproved",
|
||||||
|
components: { PaymentDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
paymentList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
paymentNo: null,
|
||||||
|
manufacturer: null,
|
||||||
|
projectName: null,
|
||||||
|
processKey: 'finance_refund',
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
currentPaymentId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listPaymentApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.paymentList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
handleView(row) {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.currentPaymentId = row.id;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getPayment(this.currentPaymentId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.paymentBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
<template>
|
||||||
|
<div class="payment-refund-detail">
|
||||||
|
<div style="text-align: center;font-weight:bold;font-size: 25px;">退款申请单</div>
|
||||||
|
<el-descriptions title="付款单信息" :column="3" border>
|
||||||
|
<el-descriptions-item label="采购付款单编号">{{ data.paymentBillCode }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="制造商名称">{{ data.vendorName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="付款条件">
|
||||||
|
{{data.payType==='0'?'入库付款':'出库付款'}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="付款周期">
|
||||||
|
{{data.payConfigDay}}天
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="含税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithTax) }}</span></el-descriptions-item>
|
||||||
|
<!-- <el-descriptions-item label="未税总价(元)"><span style="color: red">{{ data.totalPriceWithoutTax }}</span></el-descriptions-item>-->
|
||||||
|
<!-- <el-descriptions-item label="税额(元)"><span style="color: red">{{ data.taxAmount }}</span></el-descriptions-item>-->
|
||||||
|
<el-descriptions-item label="支付方式">
|
||||||
|
<dict-tag :options="dict.type.payment_method" :value="data.paymentMethod"/>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="银行账号">{{ data.payBankNumber }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="账户名称">{{ data.payName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="银行开户行">{{ data.payBankOpenAddress }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="银行行号" span="1">{{ data.bankNumber }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item span="2"></el-descriptions-item>
|
||||||
|
<el-descriptions-item label="其它特别说明" span="3">{{ data.remark }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" >
|
||||||
|
<div class="el-descriptions__title">应付单信息</div>
|
||||||
|
<el-table :data="data.payableDetails" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="payableBillCode" label="采购应付单编号" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="productType" label="产品类型" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- Note: Product Type is requested but not present in reference DetailDrawer.vue. Omitting for safety or need to ask. -->
|
||||||
|
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||||
|
<el-table-column prop="paymentAmount" label="本次付款金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {formatCurrency} from "@/utils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "PaymentRefundDetail",
|
||||||
|
methods: {formatCurrency},
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dicts: ['payment_bill_type', 'payment_method','product_type']
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.payment-refund-detail {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,315 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="付款编号" prop="paymentNo">
|
||||||
|
<el-input v-model="queryParams.paymentNo" placeholder="请输入付款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="制造商" prop="manufacturer">
|
||||||
|
<el-input v-model="queryParams.manufacturer" placeholder="请输入制造商" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
@click="toApproved()"
|
||||||
|
>审批历史</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="paymentList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="付款编号" align="center" prop="paymentBillCode" />
|
||||||
|
<el-table-column label="制造商" align="center" prop="vendorName" />
|
||||||
|
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" >
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="color: red">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 审批详情主对话框 -->
|
||||||
|
<el-dialog title="付款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="付款单详情">
|
||||||
|
<payment-detail :data="form"></payment-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span>付款编号: {{ form.paymentBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||||
|
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||||
|
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 审批意见对话框 -->
|
||||||
|
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||||
|
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||||
|
<el-form-item label="审批意见" prop="approveOpinion">
|
||||||
|
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listPaymentApprove, getPayment } from "@/api/finance/payment";
|
||||||
|
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||||
|
import PaymentDetail from "./components/PaymentRefundDetail.vue";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "PaymentApprove",
|
||||||
|
components: { PaymentDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
paymentList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
paymentNo: null,
|
||||||
|
manufacturer: null,
|
||||||
|
projectName: null,
|
||||||
|
processKey: 'finance_refund',
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
opinionDialogVisible: false,
|
||||||
|
confirmDialogTitle: '',
|
||||||
|
currentApproveType: '',
|
||||||
|
opinionForm: {
|
||||||
|
approveOpinion: ''
|
||||||
|
},
|
||||||
|
opinionRules: {
|
||||||
|
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
|
},
|
||||||
|
processKey: 'finance_refund',
|
||||||
|
taskId: null,
|
||||||
|
currentPaymentId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listPaymentApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.paymentList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
toApproved() {
|
||||||
|
this.$router.push( '/approve/paymentRedLog' )
|
||||||
|
},
|
||||||
|
handleApprove(row) {
|
||||||
|
this.resetDetailForm();
|
||||||
|
this.currentPaymentId = row.id;
|
||||||
|
this.taskId = row.taskId;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getPayment(this.currentPaymentId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.paymentBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
resetDetailForm() {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
// Assuming processKeyList might be generic or specific, using generic fetch for now
|
||||||
|
// Usually need to know the specific process key. For payment, it might be 'payment_approval' etc.
|
||||||
|
// However, listCompletedFlows logic in reference used specific keys.
|
||||||
|
// If I don't know the key, maybe I can omit it or guess.
|
||||||
|
// The reference used `processKeyList: ['purchase_order_online']`.
|
||||||
|
// I will use a generic one or try to find it.
|
||||||
|
// If row.processKey is available I can use that.
|
||||||
|
let keys = [];
|
||||||
|
console.log(this.processKey)
|
||||||
|
if(this.processKey) keys.push(this.processKey);
|
||||||
|
|
||||||
|
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openOpinionDialog(type) {
|
||||||
|
this.currentApproveType = type;
|
||||||
|
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||||
|
this.opinionDialogVisible = true;
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showConfirmDialog() {
|
||||||
|
this.$refs.opinionForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.opinionDialogVisible = false;
|
||||||
|
this.submitApproval();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
submitApproval() {
|
||||||
|
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||||
|
const params = {
|
||||||
|
businessKey: this.form.paymentBillCode,
|
||||||
|
processKey: this.processKey,
|
||||||
|
taskId: this.taskId,
|
||||||
|
variables: {
|
||||||
|
comment: this.opinionForm.approveOpinion,
|
||||||
|
approveBtn: approveBtn
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
approveTask(params).then(() => {
|
||||||
|
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||||
|
this.detailDialogVisible = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
if (!status) {
|
||||||
|
return '提交审批'
|
||||||
|
}
|
||||||
|
// Map status codes to text
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
import html2canvas from 'html2canvas';
|
||||||
|
import jsPDF from 'jspdf';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出指定DOM元素为PDF
|
||||||
|
* @param {HTMLElement} element - 要导出的DOM元素
|
||||||
|
* @param {string} fileName - 导出的文件名
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function exportElementToPDF(element, fileName) {
|
||||||
|
const disabledElements = [];
|
||||||
|
try {
|
||||||
|
// 移除所有输入框的 disabled 属性,以便在PDF中正确显示
|
||||||
|
element.querySelectorAll('input:disabled, textarea:disabled').forEach(el => {
|
||||||
|
disabledElements.push(el);
|
||||||
|
el.disabled = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 使用html2canvas捕获内容
|
||||||
|
const canvas = await html2canvas(element, {
|
||||||
|
scale: 2, // 提高清晰度
|
||||||
|
useCORS: true, // 允许跨域图片
|
||||||
|
logging: false, // 关闭日志
|
||||||
|
backgroundColor: '#F8F5F0' // 设置背景色
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算PDF页面尺寸
|
||||||
|
const imgWidth = 210; // A4纸宽度(mm)
|
||||||
|
const pageHeight = 297; // A4纸高度(mm)
|
||||||
|
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
||||||
|
let heightLeft = imgHeight;
|
||||||
|
|
||||||
|
// 创建PDF
|
||||||
|
const pdf = new jsPDF('p', 'mm', 'a4');
|
||||||
|
let position = 0;
|
||||||
|
|
||||||
|
// 将canvas转换为图片
|
||||||
|
const imgData = canvas.toDataURL('image/jpeg');
|
||||||
|
|
||||||
|
// 添加第一页
|
||||||
|
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
|
||||||
|
heightLeft -= pageHeight;
|
||||||
|
|
||||||
|
// 如果内容超过一页,添加更多页
|
||||||
|
while (heightLeft > 0) {
|
||||||
|
position = heightLeft - imgHeight;
|
||||||
|
pdf.addPage();
|
||||||
|
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
|
||||||
|
heightLeft -= pageHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存PDF
|
||||||
|
pdf.save(fileName);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
// 恢复之前移除的 disabled 属性
|
||||||
|
disabledElements.forEach(el => {
|
||||||
|
el.disabled = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,215 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="收款编号" prop="receiptBillCode">
|
||||||
|
<el-input v-model="queryParams.receiptBillCode" placeholder="请输入收款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="进货商" prop="partnerName">
|
||||||
|
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="receiptList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="收款编号" align="center" prop="receiptBillCode" />
|
||||||
|
<el-table-column label="客户" align="center" prop="partnerName" />
|
||||||
|
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||||
|
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 详情对话框 -->
|
||||||
|
<el-dialog title="收款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="收款单详情">
|
||||||
|
<receipt-detail :data="form"></receipt-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span>{{ form.receiptBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listReceiptApproved, getReceiptDetail } from "@/api/finance/receipt";
|
||||||
|
import { listCompletedFlows } from "@/api/flow";
|
||||||
|
import ReceiptDetail from "../components/ReceiptDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ReceiptApproved",
|
||||||
|
components: { ReceiptDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
receiptList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
receiptBillCode: null,
|
||||||
|
partnerName: null,
|
||||||
|
projectName: null,
|
||||||
|
processKey: 'finance_receipt_approve',
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
currentReceiptId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listReceiptApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.receiptList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
handleView(row) {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.currentReceiptId = row.id;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getReceiptDetail(this.currentReceiptId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.receiptBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `收款单-${this.form.receiptBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
<template>
|
||||||
|
<div class="receipt-detail">
|
||||||
|
<div style="text-align: center;font-weight:bold;font-size: 25px;">收款申请单</div>
|
||||||
|
<el-descriptions title="收款单信息" :column="3" border>
|
||||||
|
<el-descriptions-item label="销售-收款单编号">{{ data.receiptBillCode }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :span="2" label="进货商名称">{{ data.partnerName }}</el-descriptions-item>
|
||||||
|
<!-- <el-descriptions-item label="收款单类型">-->
|
||||||
|
<!-- <dict-tag :options="dict.type.receipt_bill_type" :value="data.receiptBillType"/>-->
|
||||||
|
<!-- </el-descriptions-item>-->
|
||||||
|
<el-descriptions-item label="含税总价(元)">{{ formatCurrency(data.totalPriceWithTax) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="未税总价(元)">{{ formatCurrency(data.totalPriceWithoutTax) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="税额(元)">{{ formatCurrency(data.taxAmount) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="支付方式">
|
||||||
|
<dict-tag :options="dict.type.payment_method" :value="data.receiptMethod"/>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="银行账号">{{ data.receiptBankNumber }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="账户名称">{{ data.receiptAccountName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="银行开户行">{{ data.receiptBankOpenAddress }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item>
|
||||||
|
<!-- <el-descriptions-item label="备注" :span="3">{{ data.remark }}</el-descriptions-item>-->
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-show="data.detailDTOList && data.detailDTOList.length>0">
|
||||||
|
<div class="el-descriptions__title">应收单列表</div>
|
||||||
|
<el-table :data="data.detailDTOList" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="productType" label="产品类型" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||||
|
<el-table-column prop="receiptAmount" label="本次收款金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
|
||||||
|
<div class="el-descriptions__title">附件信息</div>
|
||||||
|
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createByName" label="上传人" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
|
||||||
|
<el-table-column label="操作" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||||
|
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||||
|
<img :src="currentImageUrl" style="width: 100%;" />
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import request from '@/utils/request';
|
||||||
|
import {getInvoiceAttachments} from "@/api/finance/invoice";
|
||||||
|
import {getReceiptAttachments} from "@/api/finance/receipt";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ReceiptDetail",
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dicts: ['receipt_bill_type', 'product_type','payment_method'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
attachments: [],
|
||||||
|
pdfPreviewVisible: false,
|
||||||
|
currentPdfUrl: '',
|
||||||
|
imagePreviewVisible: false,
|
||||||
|
currentImageUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'data.id': {
|
||||||
|
handler(val) {
|
||||||
|
if (val) {
|
||||||
|
this.fetchAttachments(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchAttachments(id) {
|
||||||
|
getReceiptAttachments(id,{type: "receipt"}).then(response => {
|
||||||
|
this.attachments = (response.data || []).filter(item => item.delFlag !== '2');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isPdf(filePath) {
|
||||||
|
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||||
|
},
|
||||||
|
getImageUrl(resource) {
|
||||||
|
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||||
|
},
|
||||||
|
handlePreview(row) {
|
||||||
|
if (this.isPdf(row.filePath)) {
|
||||||
|
// PDF Preview logic
|
||||||
|
request({
|
||||||
|
url: '/common/download/resource',
|
||||||
|
method: 'get',
|
||||||
|
params: { resource: row.filePath },
|
||||||
|
responseType: 'blob'
|
||||||
|
}).then(res => {
|
||||||
|
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||||
|
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||||
|
this.pdfPreviewVisible = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Image Preview
|
||||||
|
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||||
|
this.imagePreviewVisible = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDownload(row) {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = this.getImageUrl(row.filePath);
|
||||||
|
link.download = row.fileName || 'attachment';
|
||||||
|
link.style.display = 'none';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.receipt-detail {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,302 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="收款编号" prop="receiptBillCode">
|
||||||
|
<el-input v-model="queryParams.receiptBillCode" placeholder="请输入收款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="进货商" prop="partnerName">
|
||||||
|
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
@click="toApproved()"
|
||||||
|
>审批历史</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="receiptList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="收款编号" align="center" prop="receiptBillCode" />
|
||||||
|
<el-table-column label="进货商" align="center" prop="partnerName" />
|
||||||
|
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||||
|
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 审批详情主对话框 -->
|
||||||
|
<el-dialog title="收款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="收款单详情">
|
||||||
|
<receipt-detail :data="form"></receipt-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span>{{ form.receiptBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||||
|
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||||
|
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 审批意见对话框 -->
|
||||||
|
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||||
|
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||||
|
<el-form-item label="审批意见" prop="approveOpinion">
|
||||||
|
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listReceiptApprove, getReceiptDetail } from "@/api/finance/receipt";
|
||||||
|
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||||
|
import ReceiptDetail from "./components/ReceiptDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ReceiptApprove",
|
||||||
|
components: { ReceiptDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
receiptList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
receiptBillCode: null,
|
||||||
|
partnerName: null,
|
||||||
|
projectName: null,
|
||||||
|
processKey: 'finance_receipt_approve',
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
opinionDialogVisible: false,
|
||||||
|
confirmDialogTitle: '',
|
||||||
|
currentApproveType: '',
|
||||||
|
opinionForm: {
|
||||||
|
approveOpinion: ''
|
||||||
|
},
|
||||||
|
opinionRules: {
|
||||||
|
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
|
},
|
||||||
|
processKey: 'finance_receipt_approve',
|
||||||
|
taskId: null,
|
||||||
|
currentReceiptId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listReceiptApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.receiptList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
toApproved() {
|
||||||
|
this.$router.push( '/approve/receiptLog' )
|
||||||
|
},
|
||||||
|
handleApprove(row) {
|
||||||
|
this.resetDetailForm();
|
||||||
|
this.currentReceiptId = row.id;
|
||||||
|
this.taskId = row.taskId;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getReceiptDetail(this.currentReceiptId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.receiptBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
resetDetailForm() {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
let keys = [];
|
||||||
|
if(this.processKey) keys.push(this.processKey);
|
||||||
|
|
||||||
|
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openOpinionDialog(type) {
|
||||||
|
this.currentApproveType = type;
|
||||||
|
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||||
|
this.opinionDialogVisible = true;
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showConfirmDialog() {
|
||||||
|
this.$refs.opinionForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.opinionDialogVisible = false;
|
||||||
|
this.submitApproval();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
submitApproval() {
|
||||||
|
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||||
|
const params = {
|
||||||
|
businessKey: this.form.receiptBillCode,
|
||||||
|
processKey: this.processKey,
|
||||||
|
taskId: this.taskId,
|
||||||
|
variables: {
|
||||||
|
comment: this.opinionForm.approveOpinion,
|
||||||
|
approveBtn: approveBtn
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
approveTask(params).then(() => {
|
||||||
|
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||||
|
this.detailDialogVisible = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
if (!status) {
|
||||||
|
return '提交审批'
|
||||||
|
}
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `收款单-${this.form.receiptBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,215 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="收款编号" prop="receiptBillCode">
|
||||||
|
<el-input v-model="queryParams.receiptBillCode" placeholder="请输入收款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="进货商" prop="partnerName">
|
||||||
|
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="receiptList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="收款编号" align="center" prop="receiptBillCode" />
|
||||||
|
<el-table-column label="客户" align="center" prop="partnerName" />
|
||||||
|
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
|
||||||
|
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 详情对话框 -->
|
||||||
|
<el-dialog title="收款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="收款单详情">
|
||||||
|
<receipt-detail :data="form"></receipt-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span> {{ form.receiptBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listReceiptApproved, getReceiptDetail } from "@/api/finance/receipt";
|
||||||
|
import { listCompletedFlows } from "@/api/flow";
|
||||||
|
import ReceiptDetail from "../components/ReceiptDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ReceiptRefoundApproved",
|
||||||
|
components: { ReceiptDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
receiptList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
receiptBillCode: null,
|
||||||
|
partnerName: null,
|
||||||
|
projectName: null,
|
||||||
|
processKey: 'finance_receipt_refound',
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
currentReceiptId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listReceiptApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.receiptList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
handleView(row) {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.currentReceiptId = row.id;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getReceiptDetail(this.currentReceiptId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.receiptBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `收款单-${this.form.receiptBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
<template>
|
||||||
|
<div class="receipt-detail">
|
||||||
|
<div style="text-align: center;font-weight:bold;font-size: 25px;">退款申请单</div>
|
||||||
|
<el-descriptions title="收款单信息" :column="3" border>
|
||||||
|
<el-descriptions-item label="销售-收款单编号">{{ data.receiptBillCode }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :span="2" label="进货商名称">{{ data.partnerName }}</el-descriptions-item>
|
||||||
|
<!-- <el-descriptions-item label="收款单类型">-->
|
||||||
|
<!-- <dict-tag :options="dict.type.receipt_bill_type" :value="data.receiptBillType"/>-->
|
||||||
|
<!-- </el-descriptions-item>-->
|
||||||
|
<el-descriptions-item label="含税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithTax) }}</span></el-descriptions-item>
|
||||||
|
<el-descriptions-item label="未税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithoutTax) }}</span></el-descriptions-item>
|
||||||
|
<el-descriptions-item label="税额(元)"><span style="color: red">{{ formatCurrency(data.taxAmount) }}</span></el-descriptions-item>
|
||||||
|
<el-descriptions-item label="支付方式">
|
||||||
|
<dict-tag :options="dict.type.payment_method" :value="data.receiptMethod"/>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="银行账号">{{ data.receiptBankNumber }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="账户名称">{{ data.receiptAccountName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="银行开户行">{{ data.receiptBankOpenAddress }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item>
|
||||||
|
<!-- <el-descriptions-item label="备注" :span="3">{{ data.remark }}</el-descriptions-item>-->
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-show="data.detailDTOList && data.detailDTOList.length>0">
|
||||||
|
<div class="el-descriptions__title">应收单列表</div>
|
||||||
|
<el-table :data="data.detailDTOList" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="productType" label="产品类型" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||||
|
<el-table-column prop="receiptAmount" label="本次收款金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
|
||||||
|
<div class="el-descriptions__title">附件信息</div>
|
||||||
|
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createByName" label="上传人" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
|
||||||
|
<el-table-column label="操作" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||||
|
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||||
|
<img :src="currentImageUrl" style="width: 100%;" />
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import request from '@/utils/request';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ReceiptRefoundDetail",
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dicts: ['receipt_bill_type', 'product_type','payment_method'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
attachments: [],
|
||||||
|
pdfPreviewVisible: false,
|
||||||
|
currentPdfUrl: '',
|
||||||
|
imagePreviewVisible: false,
|
||||||
|
currentImageUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
isPdf(filePath) {
|
||||||
|
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||||
|
},
|
||||||
|
getImageUrl(resource) {
|
||||||
|
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||||
|
},
|
||||||
|
handlePreview(row) {
|
||||||
|
if (this.isPdf(row.filePath)) {
|
||||||
|
// PDF Preview logic
|
||||||
|
request({
|
||||||
|
url: '/common/download/resource',
|
||||||
|
method: 'get',
|
||||||
|
params: { resource: row.filePath },
|
||||||
|
responseType: 'blob'
|
||||||
|
}).then(res => {
|
||||||
|
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||||
|
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||||
|
this.pdfPreviewVisible = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Image Preview
|
||||||
|
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||||
|
this.imagePreviewVisible = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDownload(row) {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = this.getImageUrl(row.filePath);
|
||||||
|
link.download = row.fileName || 'attachment';
|
||||||
|
link.style.display = 'none';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.receipt-detail {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,306 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="收款编号" prop="receiptBillCode">
|
||||||
|
<el-input v-model="queryParams.receiptBillCode" placeholder="请输入收款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="进货商" prop="partnerName">
|
||||||
|
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
@click="toApproved()"
|
||||||
|
>审批历史</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="receiptList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="收款编号" align="center" prop="receiptBillCode" />
|
||||||
|
<el-table-column label="进货商" align="center" prop="partnerName" />
|
||||||
|
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" >
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="color: red">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 审批详情主对话框 -->
|
||||||
|
<el-dialog title="收款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="收款单详情">
|
||||||
|
<receipt-detail :data="form"></receipt-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span>{{ form.receiptBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b><el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||||
|
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||||
|
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 审批意见对话框 -->
|
||||||
|
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||||
|
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||||
|
<el-form-item label="审批意见" prop="approveOpinion">
|
||||||
|
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listReceiptApprove, getReceiptDetail } from "@/api/finance/receipt";
|
||||||
|
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||||
|
import ReceiptDetail from "./components/ReceiptDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ReceiptRefoundApprove",
|
||||||
|
components: { ReceiptDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
receiptList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
receiptBillCode: null,
|
||||||
|
partnerName: null,
|
||||||
|
projectName: null,
|
||||||
|
processKey: 'finance_receipt_refound',
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
opinionDialogVisible: false,
|
||||||
|
confirmDialogTitle: '',
|
||||||
|
currentApproveType: '',
|
||||||
|
opinionForm: {
|
||||||
|
approveOpinion: ''
|
||||||
|
},
|
||||||
|
opinionRules: {
|
||||||
|
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
|
},
|
||||||
|
processKey: 'finance_receipt_refound',
|
||||||
|
taskId: null,
|
||||||
|
currentReceiptId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listReceiptApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.receiptList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
toApproved() {
|
||||||
|
this.$router.push( '/approve/receiptRefoundLog' )
|
||||||
|
},
|
||||||
|
handleApprove(row) {
|
||||||
|
this.resetDetailForm();
|
||||||
|
this.currentReceiptId = row.id;
|
||||||
|
this.taskId = row.taskId;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getReceiptDetail(this.currentReceiptId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.receiptBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
resetDetailForm() {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
let keys = [];
|
||||||
|
if(this.processKey) keys.push(this.processKey);
|
||||||
|
|
||||||
|
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openOpinionDialog(type) {
|
||||||
|
this.currentApproveType = type;
|
||||||
|
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||||
|
this.opinionDialogVisible = true;
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showConfirmDialog() {
|
||||||
|
this.$refs.opinionForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.opinionDialogVisible = false;
|
||||||
|
this.submitApproval();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
submitApproval() {
|
||||||
|
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||||
|
const params = {
|
||||||
|
businessKey: this.form.receiptBillCode,
|
||||||
|
processKey: this.processKey,
|
||||||
|
taskId: this.taskId,
|
||||||
|
variables: {
|
||||||
|
comment: this.opinionForm.approveOpinion,
|
||||||
|
approveBtn: approveBtn
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
approveTask(params).then(() => {
|
||||||
|
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||||
|
this.detailDialogVisible = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
if (!status) {
|
||||||
|
return '提交审批'
|
||||||
|
}
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `收款单-${this.form.receiptBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,240 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="开票编号" prop="invoiceBillCode">
|
||||||
|
<el-input v-model="queryParams.invoiceBillCode" placeholder="请输入开票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="客户" prop="partnerName">
|
||||||
|
<el-input v-model="queryParams.partnerName" placeholder="请输入客户" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="invoiceList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="开票编号" align="center" prop="invoiceBillCode" />
|
||||||
|
<el-table-column label="客户" align="center" prop="partnerName" />
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 详情对话框 -->
|
||||||
|
<el-dialog title="开票单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-document"
|
||||||
|
style="margin-right: 10px;"
|
||||||
|
@click="exportExcel"
|
||||||
|
:loading="excelExporting"
|
||||||
|
>导出Excel</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="开票单详情">
|
||||||
|
<receivable-invoice-detail :data="form"></receivable-invoice-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span>{{ form.invoiceBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listInvoiceApproved, getInvoiceDetail } from "@/api/finance/invoice";
|
||||||
|
import { listCompletedFlows } from "@/api/flow";
|
||||||
|
import ReceivableInvoiceDetail from "../components/ReceivableInvoiceDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
import { exportInvoiceInfoToExcel } from "@/views/approve/finance/invoiceExcelUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ReceivableInvoiceApproved",
|
||||||
|
components: { ReceivableInvoiceDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
invoiceList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
invoiceBillCode: null,
|
||||||
|
partnerName: null,
|
||||||
|
projectName: null,
|
||||||
|
processKey: 'finance_invoice_approve',
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
currentInvoiceId: null,
|
||||||
|
pdfExporting: false,
|
||||||
|
excelExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listInvoiceApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.invoiceList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
handleView(row) {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.currentInvoiceId = row.id;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getInvoiceDetail(this.currentInvoiceId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.invoiceBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `开票单-${this.form.invoiceBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
exportExcel() {
|
||||||
|
if (!this.form || !this.form.invoiceBillCode) {
|
||||||
|
this.$modal.msgWarning("暂无可导出的开票信息");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.excelExporting = true;
|
||||||
|
try {
|
||||||
|
const fileName = `开票单-${this.form.invoiceBillCode || ""}.xls`;
|
||||||
|
exportInvoiceInfoToExcel(this.form, fileName);
|
||||||
|
this.$modal.msgSuccess("Excel导出成功");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Excel导出失败:", error);
|
||||||
|
this.$modal.msgError("Excel导出失败,请稍后重试");
|
||||||
|
} finally {
|
||||||
|
this.excelExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
<template>
|
||||||
|
<div class="invoice-detail">
|
||||||
|
<div style="text-align: center;font-weight:bold;font-size: 25px;">开票申请单</div>
|
||||||
|
<el-descriptions title="开票单信息" :column="3" border>
|
||||||
|
<el-descriptions-item label="销售-开票单编号">{{ data.invoiceBillCode }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :span="2" label="进货商">{{ data.partnerName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="含税总价(元)">{{ formatCurrency(data.totalPriceWithTax) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="未税总价(元)">{{ formatCurrency(data.totalPriceWithoutTax) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="税额(元)">{{ formatCurrency(data.taxAmount) }}</el-descriptions-item>
|
||||||
|
<!-- <el-descriptions-item label="银行账号">{{ data.buyerBankAccount }}</el-descriptions-item>-->
|
||||||
|
<!-- <el-descriptions-item label="账户名称">{{ data.buyerName }}</el-descriptions-item>-->
|
||||||
|
<!-- <el-descriptions-item label="银行开户行">{{ data.buyerBank }}</el-descriptions-item>-->
|
||||||
|
<!-- <el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item> -->
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;">
|
||||||
|
<div class="el-descriptions__title">发票信息</div>
|
||||||
|
<invoice-info-view :data="data" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-show="data.detailDTOList && data.detailDTOList.length>0">
|
||||||
|
<div class="el-descriptions__title">应收单列表</div>
|
||||||
|
<el-table :data="data.detailDTOList" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="productType" label="产品类型" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||||
|
<el-table-column prop="receiptAmount" label="本次开票金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-if="excelList && excelList.length > 0">
|
||||||
|
<div class="el-descriptions__title">附件信息</div>
|
||||||
|
<el-table :data="excelList" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createUserName" label="上传信息" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="生成时间" align="center"></el-table-column>
|
||||||
|
<el-table-column label="操作" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">-->
|
||||||
|
<!-- <div class="el-descriptions__title">附件信息</div>-->
|
||||||
|
<!-- <el-table :data="attachments" border style="width: 100%; margin-top: 10px;">-->
|
||||||
|
<!-- <el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>-->
|
||||||
|
<!-- <el-table-column prop="createUserName" label="上传人" align="center"></el-table-column>-->
|
||||||
|
<!-- <el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>-->
|
||||||
|
<!-- <el-table-column label="操作" align="center">-->
|
||||||
|
<!-- <template slot-scope="scope">-->
|
||||||
|
<!-- <el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>-->
|
||||||
|
<!-- <el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </el-table-column>-->
|
||||||
|
<!-- </el-table>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<!-- <el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">-->
|
||||||
|
<!-- <iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>-->
|
||||||
|
<!-- </el-dialog>-->
|
||||||
|
|
||||||
|
<!-- <el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">-->
|
||||||
|
<!-- <img :src="currentImageUrl" style="width: 100%;" />-->
|
||||||
|
<!-- </el-dialog>-->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getInvoiceAttachments } from "@/api/finance/invoice";
|
||||||
|
import request from '@/utils/request';
|
||||||
|
import InvoiceInfoView from '@/views/finance/invoice/components/InvoiceInfoView';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ReceivableInvoiceDetail",
|
||||||
|
components: { InvoiceInfoView },
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dicts: ['product_type'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
attachments: [],
|
||||||
|
excelList: [],
|
||||||
|
pdfPreviewVisible: false,
|
||||||
|
currentPdfUrl: '',
|
||||||
|
imagePreviewVisible: false,
|
||||||
|
currentImageUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
data: {
|
||||||
|
handler(val) {
|
||||||
|
if (val && val.updateTime) {
|
||||||
|
this.excelList = [{
|
||||||
|
fileName: '电子发票--购买方公司信息.xlsx',
|
||||||
|
createUserName: '系统生成',
|
||||||
|
createTime: val.updateTime,
|
||||||
|
isSystemGenerated: true
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
isPdf(filePath) {
|
||||||
|
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||||
|
},
|
||||||
|
getImageUrl(resource) {
|
||||||
|
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||||
|
},
|
||||||
|
handlePreview(row) {
|
||||||
|
if (this.isPdf(row.filePath)) {
|
||||||
|
request({
|
||||||
|
url: '/common/download/resource',
|
||||||
|
method: 'get',
|
||||||
|
params: { resource: row.filePath },
|
||||||
|
responseType: 'blob'
|
||||||
|
}).then(res => {
|
||||||
|
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||||
|
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||||
|
this.pdfPreviewVisible = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||||
|
this.imagePreviewVisible = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDownload(row) {
|
||||||
|
if (row.isSystemGenerated) {
|
||||||
|
request({
|
||||||
|
url: '/finance/invoice/export/' + this.data.invoiceBillCode,
|
||||||
|
method: 'get'
|
||||||
|
}).then(res => {
|
||||||
|
window.location.href = process.env.VUE_APP_BASE_API + "/common/download?fileName=" + encodeURIComponent(res.msg) + "&delete=true";
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = this.getImageUrl(row.filePath);
|
||||||
|
link.download = row.fileName || 'attachment';
|
||||||
|
link.style.display = 'none';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.invoice-detail {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,327 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="开票编号" prop="invoiceBillCode">
|
||||||
|
<el-input v-model="queryParams.invoiceBillCode" placeholder="请输入开票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="进货商" prop="partnerName">
|
||||||
|
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
@click="toApproved()"
|
||||||
|
>审批历史</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="invoiceList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="开票编号" align="center" prop="invoiceBillCode" />
|
||||||
|
<el-table-column label="进货商" align="center" prop="partnerName" />
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 审批详情主对话框 -->
|
||||||
|
<el-dialog title="开票单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-document"
|
||||||
|
style="margin-right: 10px;"
|
||||||
|
@click="exportExcel"
|
||||||
|
:loading="excelExporting"
|
||||||
|
>导出Excel</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="开票单详情">
|
||||||
|
<receivable-invoice-detail :data="form"></receivable-invoice-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span>{{ form.invoiceBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||||
|
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||||
|
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 审批意见对话框 -->
|
||||||
|
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||||
|
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||||
|
<el-form-item label="审批意见" prop="approveOpinion">
|
||||||
|
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listInvoiceApprove, getInvoiceDetail } from "@/api/finance/invoice";
|
||||||
|
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||||
|
import ReceivableInvoiceDetail from "./components/ReceivableInvoiceDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
import { exportInvoiceInfoToExcel } from "@/views/approve/finance/invoiceExcelUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ReceivableInvoiceApprove",
|
||||||
|
components: { ReceivableInvoiceDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
invoiceList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
invoiceBillCode: null,
|
||||||
|
partnerName: null,
|
||||||
|
projectName: null,
|
||||||
|
processKey: 'finance_invoice_approve',
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
opinionDialogVisible: false,
|
||||||
|
confirmDialogTitle: '',
|
||||||
|
currentApproveType: '',
|
||||||
|
opinionForm: {
|
||||||
|
approveOpinion: ''
|
||||||
|
},
|
||||||
|
opinionRules: {
|
||||||
|
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
|
},
|
||||||
|
processKey: 'finance_invoice_approve',
|
||||||
|
taskId: null,
|
||||||
|
currentInvoiceId: null,
|
||||||
|
pdfExporting: false,
|
||||||
|
excelExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listInvoiceApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.invoiceList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
toApproved() {
|
||||||
|
this.$router.push('/approve/receivableInvoiceLog')
|
||||||
|
},
|
||||||
|
handleApprove(row) {
|
||||||
|
this.resetDetailForm();
|
||||||
|
this.currentInvoiceId = row.id;
|
||||||
|
this.taskId = row.taskId;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getInvoiceDetail(this.currentInvoiceId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.invoiceBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
resetDetailForm() {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
let keys = [];
|
||||||
|
if(this.processKey) keys.push(this.processKey);
|
||||||
|
|
||||||
|
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openOpinionDialog(type) {
|
||||||
|
this.currentApproveType = type;
|
||||||
|
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||||
|
this.opinionDialogVisible = true;
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showConfirmDialog() {
|
||||||
|
this.$refs.opinionForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.opinionDialogVisible = false;
|
||||||
|
this.submitApproval();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
submitApproval() {
|
||||||
|
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||||
|
const params = {
|
||||||
|
businessKey: this.form.invoiceBillCode,
|
||||||
|
processKey: this.processKey,
|
||||||
|
taskId: this.taskId,
|
||||||
|
variables: {
|
||||||
|
comment: this.opinionForm.approveOpinion,
|
||||||
|
approveBtn: approveBtn
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
approveTask(params).then(() => {
|
||||||
|
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||||
|
this.detailDialogVisible = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
if (!status) {
|
||||||
|
return '提交审批'
|
||||||
|
}
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `开票单-${this.form.invoiceBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
exportExcel() {
|
||||||
|
if (!this.form || !this.form.invoiceBillCode) {
|
||||||
|
this.$modal.msgWarning("暂无可导出的开票信息");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.excelExporting = true;
|
||||||
|
try {
|
||||||
|
const fileName = `开票单-${this.form.invoiceBillCode || ""}.xls`;
|
||||||
|
exportInvoiceInfoToExcel(this.form, fileName);
|
||||||
|
this.$modal.msgSuccess("Excel导出成功");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Excel导出失败:", error);
|
||||||
|
this.$modal.msgError("Excel导出失败,请稍后重试");
|
||||||
|
} finally {
|
||||||
|
this.excelExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="开票编号" prop="invoiceBillCode">
|
||||||
|
<el-input v-model="queryParams.invoiceBillCode" placeholder="请输入开票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="客户" prop="partnerName">
|
||||||
|
<el-input v-model="queryParams.partnerName" placeholder="请输入客户" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="invoiceList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="开票编号" align="center" prop="invoiceBillCode" />
|
||||||
|
<el-table-column label="客户" align="center" prop="partnerName" />
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span :style="scope.row.totalPriceWithTax < 0 ? 'color: red' : ''">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 详情对话框 -->
|
||||||
|
<el-dialog title="开票单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="开票单详情">
|
||||||
|
<receivable-invoice-detail :data="form"></receivable-invoice-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span>{{ form.invoiceBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listInvoiceApproved, getInvoiceDetail } from "@/api/finance/invoice";
|
||||||
|
import { listCompletedFlows } from "@/api/flow";
|
||||||
|
import ReceivableInvoiceDetail from "../components/ReceivableInvoiceDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ReceivableInvoiceRefundApproved",
|
||||||
|
components: { ReceivableInvoiceDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
invoiceList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
invoiceBillCode: null,
|
||||||
|
partnerName: null,
|
||||||
|
projectName: null,
|
||||||
|
processKey: 'finance_invoice_refound',
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
currentInvoiceId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listInvoiceApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.invoiceList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
handleView(row) {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.currentInvoiceId = row.id;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getInvoiceDetail(this.currentInvoiceId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.invoiceBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `开票单-${this.form.invoiceBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,183 @@
|
||||||
|
<template>
|
||||||
|
<div class="invoice-detail">
|
||||||
|
<div style="text-align: center;font-weight:bold;font-size: 25px;">红冲开票申请单</div>
|
||||||
|
<el-descriptions title="开票单信息" :column="3" border>
|
||||||
|
<el-descriptions-item label="销售-开票单编号">{{ data.invoiceBillCode }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :span="2" label="进货商">{{ data.partnerName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="含税总价(元)">
|
||||||
|
<span :style="data.totalPriceWithTax < 0 ? 'color: red' : ''">{{ formatCurrency(data.totalPriceWithTax) }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="未税总价(元)">
|
||||||
|
<span :style="data.totalPriceWithoutTax < 0 ? 'color: red' : ''">{{ formatCurrency(data.totalPriceWithoutTax) }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="税额(元)">
|
||||||
|
<span :style="data.taxAmount < 0 ? 'color: red' : ''">{{ formatCurrency(data.taxAmount) }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<!-- <el-descriptions-item label="银行账号">{{ data.buyerBankAccount }}</el-descriptions-item>-->
|
||||||
|
<!-- <el-descriptions-item label="账户名称">{{ data.buyerName }}</el-descriptions-item>-->
|
||||||
|
<!-- <el-descriptions-item label="银行开户行">{{ data.buyerBank }}</el-descriptions-item>-->
|
||||||
|
<!-- <el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item> -->
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;">
|
||||||
|
<div class="el-descriptions__title">发票信息</div>
|
||||||
|
<invoice-info-view :data="data" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-show="data.detailDTOList && data.detailDTOList.length>0">
|
||||||
|
<div class="el-descriptions__title">应收单列表</div>
|
||||||
|
<el-table :data="data.detailDTOList" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="productType" label="产品类型" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span :style="scope.row.totalPriceWithTax < 0 ? 'color: red' : ''">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="receiptAmount" label="本次开票金额" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span :style="scope.row.receiptAmount < 0 ? 'color: red' : ''">{{ formatCurrency(scope.row.receiptAmount) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-if="excelList && excelList.length > 0">
|
||||||
|
<div class="el-descriptions__title">附件信息</div>
|
||||||
|
<el-table :data="excelList" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createUserName" label="上传信息" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="生成时间" align="center"></el-table-column>
|
||||||
|
<el-table-column label="操作" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
|
||||||
|
<div class="el-descriptions__title">附件信息</div>
|
||||||
|
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
|
||||||
|
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createUserName" label="上传人" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
|
||||||
|
<el-table-column label="操作" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||||
|
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||||
|
<img :src="currentImageUrl" style="width: 100%;" />
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getInvoiceAttachments } from "@/api/finance/invoice";
|
||||||
|
import request from '@/utils/request';
|
||||||
|
import InvoiceInfoView from '@/views/finance/invoice/components/InvoiceInfoView';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ReceivableInvoiceDetail",
|
||||||
|
components: { InvoiceInfoView },
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dicts: ['product_type'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
attachments: [],
|
||||||
|
excelList: [],
|
||||||
|
pdfPreviewVisible: false,
|
||||||
|
currentPdfUrl: '',
|
||||||
|
imagePreviewVisible: false,
|
||||||
|
currentImageUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
data: {
|
||||||
|
handler(val) {
|
||||||
|
if (val && val.updateTime) {
|
||||||
|
this.excelList = [{
|
||||||
|
fileName: '电子发票--购买方公司信息.xlsx',
|
||||||
|
createUserName: '系统生成',
|
||||||
|
createTime: val.updateTime,
|
||||||
|
isSystemGenerated: true
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
isPdf(filePath) {
|
||||||
|
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||||
|
},
|
||||||
|
getImageUrl(resource) {
|
||||||
|
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||||
|
},
|
||||||
|
handlePreview(row) {
|
||||||
|
if (this.isPdf(row.filePath)) {
|
||||||
|
request({
|
||||||
|
url: '/common/download/resource',
|
||||||
|
method: 'get',
|
||||||
|
params: { resource: row.filePath },
|
||||||
|
responseType: 'blob'
|
||||||
|
}).then(res => {
|
||||||
|
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||||
|
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||||
|
this.pdfPreviewVisible = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||||
|
this.imagePreviewVisible = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDownload(row) {
|
||||||
|
if (row.isSystemGenerated) {
|
||||||
|
request({
|
||||||
|
url: '/finance/invoice/export/' + this.data.invoiceBillCode,
|
||||||
|
method: 'get'
|
||||||
|
}).then(res => {
|
||||||
|
window.location.href = process.env.VUE_APP_BASE_API + "/common/download?fileName=" + encodeURIComponent(res.msg) + "&delete=true";
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = this.getImageUrl(row.filePath);
|
||||||
|
link.download = row.fileName || 'attachment';
|
||||||
|
link.style.display = 'none';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.invoice-detail {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,304 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||||
|
<el-form-item label="开票编号" prop="invoiceBillCode">
|
||||||
|
<el-input v-model="queryParams.invoiceBillCode" placeholder="请输入开票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="进货商" prop="partnerName">
|
||||||
|
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="projectName">
|
||||||
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提交日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
style="width: 240px"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
@click="toApproved()"
|
||||||
|
>审批历史</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="invoiceList">
|
||||||
|
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||||
|
<el-table-column label="开票编号" align="center" prop="invoiceBillCode" />
|
||||||
|
<el-table-column label="进货商" align="center" prop="partnerName" />
|
||||||
|
<el-table-column label="金额" align="center" prop="totalPriceWithTax">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span :style="scope.row.totalPriceWithTax < 0 ? 'color: red' : ''">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||||
|
|
||||||
|
<!-- 审批详情主对话框 -->
|
||||||
|
<el-dialog title="开票单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||||
|
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||||
|
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
@click="exportPDF"
|
||||||
|
:loading="pdfExporting"
|
||||||
|
>导出PDF</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||||
|
<ApproveLayout ref="approveLayout" title="开票单详情">
|
||||||
|
<receivable-invoice-detail :data="form"></receivable-invoice-detail>
|
||||||
|
<template #footer>
|
||||||
|
<span>{{ form.invoiceBillCode }}</span>
|
||||||
|
</template>
|
||||||
|
</ApproveLayout>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider content-position="left">流转意见</el-divider>
|
||||||
|
<div class="process-container">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||||
|
<el-card>
|
||||||
|
<h4>{{ log.approveOpinion }}</h4>
|
||||||
|
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||||
|
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||||
|
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||||
|
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 审批意见对话框 -->
|
||||||
|
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||||
|
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||||
|
<el-form-item label="审批意见" prop="approveOpinion">
|
||||||
|
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listInvoiceApprove, getInvoiceDetail } from "@/api/finance/invoice";
|
||||||
|
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||||
|
import ReceivableInvoiceDetail from "./components/ReceivableInvoiceDetail";
|
||||||
|
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||||
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ReceivableInvoiceRefundApprove",
|
||||||
|
components: { ReceivableInvoiceDetail, ApproveLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
invoiceList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
invoiceBillCode: null,
|
||||||
|
partnerName: null,
|
||||||
|
projectName: null,
|
||||||
|
processKey: 'finance_invoice_refound',
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
},
|
||||||
|
dateRange: [],
|
||||||
|
detailDialogVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
form: {},
|
||||||
|
approveLogs: [],
|
||||||
|
opinionDialogVisible: false,
|
||||||
|
confirmDialogTitle: '',
|
||||||
|
currentApproveType: '',
|
||||||
|
opinionForm: {
|
||||||
|
approveOpinion: ''
|
||||||
|
},
|
||||||
|
opinionRules: {
|
||||||
|
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
|
},
|
||||||
|
processKey: 'finance_invoice_refound',
|
||||||
|
taskId: null,
|
||||||
|
currentInvoiceId: null,
|
||||||
|
pdfExporting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listInvoiceApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||||
|
this.invoiceList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
toApproved() {
|
||||||
|
this.$router.push('/approve/receivableInvoiceRefundLog')
|
||||||
|
},
|
||||||
|
handleApprove(row) {
|
||||||
|
this.resetDetailForm();
|
||||||
|
this.currentInvoiceId = row.id;
|
||||||
|
this.taskId = row.taskId;
|
||||||
|
this.detailLoading = true;
|
||||||
|
this.detailDialogVisible = true;
|
||||||
|
|
||||||
|
getInvoiceDetail(this.currentInvoiceId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.loadApproveHistory(this.form.invoiceBillCode);
|
||||||
|
this.detailLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.detailLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
resetDetailForm() {
|
||||||
|
this.form = {};
|
||||||
|
this.approveLogs = [];
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
},
|
||||||
|
loadApproveHistory(businessKey) {
|
||||||
|
if (businessKey) {
|
||||||
|
let keys = [];
|
||||||
|
if(this.processKey) keys.push(this.processKey);
|
||||||
|
|
||||||
|
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||||
|
this.approveLogs = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openOpinionDialog(type) {
|
||||||
|
this.currentApproveType = type;
|
||||||
|
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||||
|
this.opinionDialogVisible = true;
|
||||||
|
this.opinionForm.approveOpinion = '';
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showConfirmDialog() {
|
||||||
|
this.$refs.opinionForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.opinionDialogVisible = false;
|
||||||
|
this.submitApproval();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
submitApproval() {
|
||||||
|
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||||
|
const params = {
|
||||||
|
businessKey: this.form.invoiceBillCode,
|
||||||
|
processKey: this.processKey,
|
||||||
|
taskId: this.taskId,
|
||||||
|
variables: {
|
||||||
|
comment: this.opinionForm.approveOpinion,
|
||||||
|
approveBtn: approveBtn
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
approveTask(params).then(() => {
|
||||||
|
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||||
|
this.detailDialogVisible = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getStatusText(status) {
|
||||||
|
if (!status) {
|
||||||
|
return '提交审批'
|
||||||
|
}
|
||||||
|
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||||
|
return map[status] || '提交审批';
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
this.pdfExporting = true;
|
||||||
|
try {
|
||||||
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
const fileName = `开票单-${this.form.invoiceBillCode || ''}.pdf`;
|
||||||
|
await exportElementToPDF(element, fileName);
|
||||||
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
this.pdfExporting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.process-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导出PDF时的特殊样式 */
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
resize: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -135,8 +135,7 @@
|
||||||
import { approveOrder,getOrder } from "@/api/approve/order";
|
import { approveOrder,getOrder } from "@/api/approve/order";
|
||||||
import ConfigInfo from './ConfigInfo.vue';
|
import ConfigInfo from './ConfigInfo.vue';
|
||||||
import ApproveLayout from '@/views/approve/ApproveLayout.vue';
|
import ApproveLayout from '@/views/approve/ApproveLayout.vue';
|
||||||
import html2canvas from 'html2canvas';
|
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||||
import jsPDF from 'jspdf';
|
|
||||||
|
|
||||||
import OrderInfoDisplay from '@/views/project/order/components/OrderInfoDisplay.vue';
|
import OrderInfoDisplay from '@/views/project/order/components/OrderInfoDisplay.vue';
|
||||||
|
|
||||||
|
|
@ -468,65 +467,21 @@ export default {
|
||||||
// 导出PDF
|
// 导出PDF
|
||||||
async exportPDF() {
|
async exportPDF() {
|
||||||
this.pdfExporting = true;
|
this.pdfExporting = true;
|
||||||
const disabledElements = [];
|
|
||||||
try {
|
try {
|
||||||
// 获取ApproveLayout组件的DOM元素
|
// 获取ApproveLayout组件的DOM元素
|
||||||
const element = this.$refs.approveLayout.$el;
|
const element = this.$refs.approveLayout.$el;
|
||||||
|
|
||||||
// 移除所有输入框的 disabled 属性,以便在PDF中正确显示
|
|
||||||
element.querySelectorAll('input:disabled, textarea:disabled').forEach(el => {
|
|
||||||
disabledElements.push(el);
|
|
||||||
el.disabled = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 使用html2canvas捕获内容
|
|
||||||
const canvas = await html2canvas(element, {
|
|
||||||
scale: 2, // 提高清晰度
|
|
||||||
useCORS: true, // 允许跨域图片
|
|
||||||
logging: false, // 关闭日志
|
|
||||||
backgroundColor: '#F8F5F0' // 设置背景色
|
|
||||||
});
|
|
||||||
|
|
||||||
// 计算PDF页面尺寸
|
|
||||||
const imgWidth = 210; // A4纸宽度(mm)
|
|
||||||
const pageHeight = 297; // A4纸高度(mm)
|
|
||||||
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
|
||||||
let heightLeft = imgHeight;
|
|
||||||
|
|
||||||
// 创建PDF
|
|
||||||
const pdf = new jsPDF('p', 'mm', 'a4');
|
|
||||||
let position = 0;
|
|
||||||
|
|
||||||
// 将canvas转换为图片
|
|
||||||
const imgData = canvas.toDataURL('image/jpeg');
|
|
||||||
|
|
||||||
// 添加第一页
|
|
||||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
|
|
||||||
heightLeft -= pageHeight;
|
|
||||||
|
|
||||||
// 如果内容超过一页,添加更多页
|
|
||||||
while (heightLeft > 0) {
|
|
||||||
position = heightLeft - imgHeight;
|
|
||||||
pdf.addPage();
|
|
||||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
|
|
||||||
heightLeft -= pageHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成文件名
|
// 生成文件名
|
||||||
const fileName = `${this.order.projectCode || '订单'}-${this.order.orderCode || ''}-Rev.${this.order.versionCode || '1'}.pdf`;
|
const fileName = `${this.order.projectCode || '订单'}-${this.order.orderCode || ''}-Rev.${this.order.versionCode || '1'}.pdf`;
|
||||||
|
|
||||||
// 保存PDF
|
// 调用通用方法导出PDF
|
||||||
pdf.save(fileName);
|
await exportElementToPDF(element, fileName);
|
||||||
|
|
||||||
this.$modal.msgSuccess('PDF导出成功');
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('PDF导出失败:', error);
|
console.error('PDF导出失败:', error);
|
||||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||||
} finally {
|
} finally {
|
||||||
// 恢复之前移除的 disabled 属性
|
|
||||||
disabledElements.forEach(el => {
|
|
||||||
el.disabled = true;
|
|
||||||
});
|
|
||||||
this.pdfExporting = false;
|
this.pdfExporting = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
|
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
|
||||||
<td>{{ formatCurrency(product.price) }}</td>
|
<td>{{ formatCurrency(product.price) }}</td>
|
||||||
<td>{{ selectedDiscountLabel }}</td>
|
<td>{{ selectedDiscountLabel }}</td>
|
||||||
<td>{{ formatCurrency(product.allPrice) }}</td>
|
<td>{{ formatCurrency(getDisplayAllPrice(product, selectedDiscount)) }}</td>
|
||||||
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
|
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
|
||||||
<td>
|
<td>
|
||||||
<el-input
|
<el-input
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
|
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
|
||||||
<td>{{ formatCurrency(product.price) }}</td>
|
<td>{{ formatCurrency(product.price) }}</td>
|
||||||
<td>{{ selectedDiscountLabel }}</td>
|
<td>{{ selectedDiscountLabel }}</td>
|
||||||
<td>{{ formatCurrency(product.allPrice) }}</td>
|
<td>{{ formatCurrency(getDisplayAllPrice(product, selectedDiscount)) }}</td>
|
||||||
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
|
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
|
||||||
<td>
|
<td>
|
||||||
<el-input
|
<el-input
|
||||||
|
|
@ -142,7 +142,7 @@
|
||||||
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
|
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
|
||||||
<td>{{ formatCurrency(product.price) }}</td>
|
<td>{{ formatCurrency(product.price) }}</td>
|
||||||
<td>{{ selectedDiscountLabel }}</td>
|
<td>{{ selectedDiscountLabel }}</td>
|
||||||
<td>{{ formatCurrency(product.allPrice) }}</td>
|
<td>{{ formatCurrency(getDisplayAllPrice(product, selectedDiscount)) }}</td>
|
||||||
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
|
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
|
||||||
<td>
|
<td>
|
||||||
<el-input
|
<el-input
|
||||||
|
|
@ -178,7 +178,7 @@
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row type="flex" justify="end" align="middle" class="summary-row">
|
<el-row type="flex" justify="end" align="middle" class="summary-row">
|
||||||
<el-col :span="18" style="text-align: center;">
|
<el-col :span="orderData.orderStatus==='1'?18:24" style="text-align: center;">
|
||||||
<span style="margin-right: 5px;">商业折扣</span>
|
<span style="margin-right: 5px;">商业折扣</span>
|
||||||
<el-select
|
<el-select
|
||||||
v-model="selectedDiscount"
|
v-model="selectedDiscount"
|
||||||
|
|
@ -195,7 +195,7 @@
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6" v-if="orderData.orderStatus==='1'">
|
||||||
<span class="summary-label"> 折后总价合计</span> <span
|
<span class="summary-label"> 折后总价合计</span> <span
|
||||||
class="summary-value-right"> {{ formatCurrency(finalTotal) }}</span>
|
class="summary-value-right"> {{ formatCurrency(finalTotal) }}</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
@ -278,7 +278,12 @@ export default {
|
||||||
return this.calculateTotal(this.order.maintenanceProjectProductInfoList, this.selectedDiscount);
|
return this.calculateTotal(this.order.maintenanceProjectProductInfoList, this.selectedDiscount);
|
||||||
},
|
},
|
||||||
grandTotal() {
|
grandTotal() {
|
||||||
|
if (this.orderData.orderStatus === '1'){
|
||||||
|
|
||||||
return this.softwareTotal + this.hardwareTotal + this.maintenanceTotal;
|
return this.softwareTotal + this.hardwareTotal + this.maintenanceTotal;
|
||||||
|
}else{
|
||||||
|
return this.finalTotal;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
finalTotal() {
|
finalTotal() {
|
||||||
return this.softwareDiscountedTotal + this.hardwareDiscountedTotal + this.maintenanceDiscountedTotal;
|
return this.softwareDiscountedTotal + this.hardwareDiscountedTotal + this.maintenanceDiscountedTotal;
|
||||||
|
|
@ -297,13 +302,20 @@ export default {
|
||||||
// 税率变化时通知父组件
|
// 税率变化时通知父组件
|
||||||
this.$emit('tax-rate-change', product);
|
this.$emit('tax-rate-change', product);
|
||||||
},
|
},
|
||||||
getDiscountedAllPrice(product, discount) {
|
getDisplayAllPrice(product, discount) {
|
||||||
if (discount === 1) {
|
const allPrice = product.allPrice || 0;
|
||||||
return product.allPrice;
|
if (this.order.orderStatus === '2' || this.order.orderStatus === 2) {
|
||||||
|
return this.$calc.div(allPrice, discount || 1);
|
||||||
}
|
}
|
||||||
const roundedDiscountedUnitPrice = this.$calc.mul( product.price,discount);
|
return allPrice;
|
||||||
|
},
|
||||||
return this.$calc.mul(roundedDiscountedUnitPrice , product.quantity);
|
getDiscountedAllPrice(product, discount) {
|
||||||
|
const allPrice = product.allPrice || 0;
|
||||||
|
if (this.order.orderStatus === '2' || this.order.orderStatus === 2) {
|
||||||
|
return allPrice;
|
||||||
|
}
|
||||||
|
const discountedPrice = this.$calc.mul(product.price || 0, discount == null ? 1 : discount, 2);
|
||||||
|
return this.$calc.mul(discountedPrice, product.quantity || 0, 2);
|
||||||
},
|
},
|
||||||
calculateTotal(productList, discount) {
|
calculateTotal(productList, discount) {
|
||||||
if (!productList) return 0;
|
if (!productList) return 0;
|
||||||
|
|
|
||||||
|
|
@ -49,15 +49,28 @@
|
||||||
@keyup.enter.native="handleQuery"
|
@keyup.enter.native="handleQuery"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="产品编码" prop="productCode">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.productCode"
|
||||||
|
placeholder="请输入产品编码"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品型号" prop="productModel">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.productModel"
|
||||||
|
placeholder="请输入产品型号"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<el-row :gutter="10" class="mb8">
|
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="10" class="mb8">
|
<el-row :gutter="10" class="mb8">
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button
|
<el-button
|
||||||
|
|
@ -75,7 +88,12 @@
|
||||||
<el-table-column label="项目名称" align="center" prop="projectName" />
|
<el-table-column label="项目名称" align="center" prop="projectName" />
|
||||||
<el-table-column label="项目编号" align="center" prop="projectCode" />
|
<el-table-column label="项目编号" align="center" prop="projectCode" />
|
||||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||||
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" />
|
|
||||||
|
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" >
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ formatCurrency(scope.row.actualPurchaseAmount || scope.row.shipmentAmount) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="汇智负责人" align="center" prop="dutyName" />
|
<el-table-column label="汇智负责人" align="center" prop="dutyName" />
|
||||||
<el-table-column label="审批节点" align="center" prop="approveNode">
|
<el-table-column label="审批节点" align="center" prop="approveNode">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
|
|
@ -106,6 +124,7 @@
|
||||||
<el-dialog
|
<el-dialog
|
||||||
title="订单审批"
|
title="订单审批"
|
||||||
:visible.sync="approveDialogVisible"
|
:visible.sync="approveDialogVisible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
custom-class="approve-dialog"
|
custom-class="approve-dialog"
|
||||||
width="80%"
|
width="80%"
|
||||||
top="5vh"
|
top="5vh"
|
||||||
|
|
@ -130,6 +149,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { listOrder } from "@/api/approve/order";
|
import { listOrder } from "@/api/approve/order";
|
||||||
import ApproveDialog from './Approve.vue';
|
import ApproveDialog from './Approve.vue';
|
||||||
|
import {formatCurrency} from "../../../utils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Order",
|
name: "Order",
|
||||||
|
|
@ -156,9 +176,14 @@ export default {
|
||||||
orderCode: null,
|
orderCode: null,
|
||||||
projectName: null,
|
projectName: null,
|
||||||
projectCode: null,
|
projectCode: null,
|
||||||
|
productCode: null,
|
||||||
|
productModel: null,
|
||||||
customerName: null,
|
customerName: null,
|
||||||
dutyName: null,
|
dutyName: null,
|
||||||
approveNode: null,
|
approveNode: null,
|
||||||
|
orderStatus: '1',
|
||||||
|
orderByColumn: 't2.id',
|
||||||
|
isAsc: 'desc',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
@ -166,6 +191,7 @@ export default {
|
||||||
this.getList();
|
this.getList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
formatCurrency,
|
||||||
toApproved(){
|
toApproved(){
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
path: '/approve/orderLog',
|
path: '/approve/orderLog',
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,22 @@
|
||||||
end-placeholder="结束日期"
|
end-placeholder="结束日期"
|
||||||
></el-date-picker>
|
></el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="产品编码" prop="productCode">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.productCode"
|
||||||
|
placeholder="请输入产品编码"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品型号" prop="productModel">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.productModel"
|
||||||
|
placeholder="请输入产品型号"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
|
@ -47,7 +62,7 @@
|
||||||
<el-table-column label="采购单号" align="center" prop="purchaseNo" />
|
<el-table-column label="采购单号" align="center" prop="purchaseNo" />
|
||||||
<el-table-column label="制造商名称" align="center" prop="vendorName"/>
|
<el-table-column label="制造商名称" align="center" prop="vendorName"/>
|
||||||
<el-table-column label="联系人" align="center" prop="vendorUser" />
|
<el-table-column label="联系人" align="center" prop="vendorUser" />
|
||||||
<el-table-column label="含税总金额" align="center" prop="totalAmount" />
|
<el-table-column label="含税总金额" align="center" prop="totalAmount" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
|
||||||
<el-table-column label="提交日期" align="center" prop="applyTime" >
|
<el-table-column label="提交日期" align="center" prop="applyTime" >
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
|
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
|
||||||
|
|
@ -142,12 +157,15 @@ export default {
|
||||||
buyerName: null,
|
buyerName: null,
|
||||||
vendorName: null,
|
vendorName: null,
|
||||||
ownerName: null,
|
ownerName: null,
|
||||||
|
productCode: null,
|
||||||
|
productModel: null,
|
||||||
approveStatus: '1',
|
approveStatus: '1',
|
||||||
params:{
|
params:{
|
||||||
applyTimeStart: null,
|
applyTimeStart: null,
|
||||||
applyTimeEnd: null,
|
applyTimeEnd: null,
|
||||||
}
|
},
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
},
|
},
|
||||||
// 时间范围选择器
|
// 时间范围选择器
|
||||||
dateRangeApplyTime: [],
|
dateRangeApplyTime: [],
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,22 @@
|
||||||
end-placeholder="结束日期"
|
end-placeholder="结束日期"
|
||||||
></el-date-picker>
|
></el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="产品编码" prop="productCode">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.productCode"
|
||||||
|
placeholder="请输入产品编码"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品型号" prop="productModel">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.productModel"
|
||||||
|
placeholder="请输入产品型号"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
|
@ -47,7 +62,7 @@
|
||||||
<el-table-column label="采购单号" align="center" prop="purchaseNo" />
|
<el-table-column label="采购单号" align="center" prop="purchaseNo" />
|
||||||
<el-table-column label="制造商名称" align="center" prop="vendorName"/>
|
<el-table-column label="制造商名称" align="center" prop="vendorName"/>
|
||||||
<el-table-column label="联系人" align="center" prop="vendorUser" />
|
<el-table-column label="联系人" align="center" prop="vendorUser" />
|
||||||
<el-table-column label="含税总金额" align="center" prop="totalAmount" />
|
<el-table-column label="含税总金额" align="center" prop="totalAmount" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
|
||||||
<el-table-column label="提交日期" align="center" prop="applyTime" >
|
<el-table-column label="提交日期" align="center" prop="applyTime" >
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
|
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
|
||||||
|
|
@ -134,12 +149,16 @@ export default {
|
||||||
purchaseNo: null,
|
purchaseNo: null,
|
||||||
buyerName: null,
|
buyerName: null,
|
||||||
vendorName: null,
|
vendorName: null,
|
||||||
|
productCode: null,
|
||||||
|
productModel: null,
|
||||||
ownerName: null,
|
ownerName: null,
|
||||||
approveStatus: '3', // 已审批状态通常为3
|
approveStatus: '3', // 已审批状态通常为3
|
||||||
params:{
|
params:{
|
||||||
applyTimeStart: null,
|
applyTimeStart: null,
|
||||||
applyTimeEnd: null,
|
applyTimeEnd: null,
|
||||||
}
|
},
|
||||||
|
orderByColumn: 'applyTime',
|
||||||
|
isAsc: 'desc'
|
||||||
},
|
},
|
||||||
// 时间范围选择器
|
// 时间范围选择器
|
||||||
dateRangeApplyTime: [],
|
dateRangeApplyTime: [],
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
<template>
|
||||||
|
<el-drawer
|
||||||
|
title="报价单详情"
|
||||||
|
:visible.sync="visible"
|
||||||
|
direction="rtl"
|
||||||
|
size="80%"
|
||||||
|
:before-close="handleClose"
|
||||||
|
append-to-body
|
||||||
|
>
|
||||||
|
<div class="detail-container" v-loading="loading">
|
||||||
|
<!-- Basic Info -->
|
||||||
|
<el-divider content-position="left">基本信息</el-divider>
|
||||||
|
<el-descriptions :column="2" border size="medium">
|
||||||
|
<el-descriptions-item label="报价单号">{{ form.quotationCode }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="报价单名称">{{ form.quotationName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="币种">
|
||||||
|
<dict-tag :options="dict.type.currency_type" :value="form.amountType"/>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="代表处">{{ agentName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="客户名称">{{ form.customerName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="状态">{{ form.quotationStatus }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="创建时间">{{ parseTime(form.createTime) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="备注" :span="2">{{ form.remark }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div v-if="form.quotationCode || catalogueTotalPrice > 0 || discountedTotalPrice > 0" style="margin-top: 20px;">
|
||||||
|
<el-row type="flex" justify="space-between" style="margin-bottom: 20px; font-size: 14px;">
|
||||||
|
<el-col :span="24" style="text-align: right;">
|
||||||
|
<span v-if="catalogueTotalPrice > 0" style="margin-right: 20px;">
|
||||||
|
<span style="font-weight: bold;">目录总价:</span>{{ formatAmount(catalogueTotalPrice) }}
|
||||||
|
</span>
|
||||||
|
<span v-if="discountedTotalPrice > 0">
|
||||||
|
<span style="font-weight: bold;">折后总价:</span>{{ formatAmount(discountedTotalPrice) }}
|
||||||
|
</span>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Config Info -->
|
||||||
|
<product-config :value="form" readonly />
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getQuotation } from "@/api/base/quotation";
|
||||||
|
import ProductConfig from "@/views/project/info/ProductConfig";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "QuotationDetail",
|
||||||
|
components: { ProductConfig },
|
||||||
|
dicts: ['currency_type'],
|
||||||
|
props: {
|
||||||
|
agentOptions: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
loading: false,
|
||||||
|
form: {
|
||||||
|
softwareProjectProductInfoList: [],
|
||||||
|
hardwareProjectProductInfoList: [],
|
||||||
|
maintenanceProjectProductInfoList: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
agentName() {
|
||||||
|
if (!this.form.agentCode || !this.agentOptions) return this.form.agentCode;
|
||||||
|
const agent = this.agentOptions.find(item => item.agentCode === this.form.agentCode);
|
||||||
|
return agent ? agent.agentName : this.form.agentCode;
|
||||||
|
},
|
||||||
|
catalogueTotalPrice() {
|
||||||
|
let total = 0;
|
||||||
|
const lists = [
|
||||||
|
this.form.softwareProjectProductInfoList,
|
||||||
|
this.form.hardwareProjectProductInfoList,
|
||||||
|
this.form.maintenanceProjectProductInfoList
|
||||||
|
];
|
||||||
|
lists.forEach(list => {
|
||||||
|
if (list && list.length > 0) {
|
||||||
|
list.forEach(item => {
|
||||||
|
total += Number(item.catalogueAllPrice) || 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return total;
|
||||||
|
},
|
||||||
|
discountedTotalPrice() {
|
||||||
|
let total = 0;
|
||||||
|
const lists = [
|
||||||
|
this.form.softwareProjectProductInfoList,
|
||||||
|
this.form.hardwareProjectProductInfoList,
|
||||||
|
this.form.maintenanceProjectProductInfoList
|
||||||
|
];
|
||||||
|
lists.forEach(list => {
|
||||||
|
if (list && list.length > 0) {
|
||||||
|
list.forEach(item => {
|
||||||
|
total += Number(item.allPrice) || 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(id) {
|
||||||
|
this.visible = true;
|
||||||
|
this.getDetail(id);
|
||||||
|
},
|
||||||
|
getDetail(id) {
|
||||||
|
this.loading = true;
|
||||||
|
getQuotation(id).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
// Ensure lists are arrays
|
||||||
|
this.form.softwareProjectProductInfoList = this.form.softwareProjectProductInfoList || [];
|
||||||
|
this.form.hardwareProjectProductInfoList = this.form.hardwareProjectProductInfoList || [];
|
||||||
|
this.form.maintenanceProjectProductInfoList = this.form.maintenanceProjectProductInfoList || [];
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleClose(done) {
|
||||||
|
this.visible = false;
|
||||||
|
if (done) done();
|
||||||
|
},
|
||||||
|
formatAmount(value) {
|
||||||
|
if (value === null || value === undefined) return '';
|
||||||
|
return Number(value).toLocaleString('en-US', {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.detail-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue