Compare commits

...

275 Commits

Author SHA1 Message Date
kangwenjing 73cacea552 feat: 新增带数据的发货导入模板下载功能,优化审批状态显示与产品筛选逻辑
1. 新增/importTemplateData接口,支持下载带查询数据的发货导入模板
2. 修复审批标签页和状态显示文本不一致问题,将"已通过"改为"已处理"
3. 优化产品二级类型筛选逻辑,配件类型不显示终端配置项
4. 移除本地日志路径配置,修正MyBatis权限查询条件
5. 更新前端模板下载调用逻辑,传入查询参数
2026-09-02 09:36:33 +08:00
jiangpeng a249239e77 fix:发货管理2608 2026-08-28 17:01:31 +08:00
jiangpeng 415b9b3e46 fix:发货管理2608 2026-08-28 16:48:21 +08:00
jiangpeng 625519c2c2 fix:产品库存导出功能 2026-08-24 17:31:56 +08:00
jiangpeng 6e6d29511c fix:应付单关联采购单bug处理 2026-08-20 10:57:04 +08:00
jiangpeng 43fc48a3a5 fix:应付单详情数据内容修改 2026-08-17 17:50:06 +08:00
jiangpeng 87a3901694 fix:test配置文件修改 2026-08-05 17:22:56 +08:00
jiangpeng 2d5cffb78e fix:审批单合并成审批中心 2026-08-05 11:17:04 +08:00
jiangpeng 0b693760df fix:审批单合并成审批中心 2026-08-03 14:49:41 +08:00
jiangpeng a9ed35793a fix:审批单合并成审批中心 2026-08-03 09:54:46 +08:00
jiangpeng 0106da0c29 fix:出库bug处理,产品库存页面增加权限限制 2026-07-30 16:30:55 +08:00
jiangpeng 2c6547f5ec fix:审批单查询不限制系统管理员 2026-07-21 20:31:51 +08:00
jiangpeng 4e4792459c fix:生成订单按钮权限字符使用“项目修改权限” 2026-07-20 20:20:14 +08:00
jiangpeng aba73af268 fix:订单审批折上折计算调整 2026-07-20 20:12:21 +08:00
jiangpeng 34e9047763 fix:项目风险信息项调整 2026-07-16 19:48:42 +08:00
jiangpeng e3d77525dd fix:项目风险信息项调整 2026-07-16 18:43:04 +08:00
jiangpeng 818454311f fix:硬件维保时间计算bug处理 2026-07-16 10:54:14 +08:00
jiangpeng 4d911c93ae fix:删除会审功能 2026-07-14 17:43:01 +08:00
jiangpeng 934b2990f1 fix:推送CRM相关调整 2026-07-10 18:05:17 +08:00
jiangpeng 6d4923ac8b fix:产品库存数据展示调整 2026-07-09 19:01:06 +08:00
jiangpeng 2913740e36 fix:产品库存数据展示调整 2026-07-09 18:45:23 +08:00
jiangpeng 3883b75b3b fix:dev配置文件 2026-07-09 15:21:15 +08:00
jiangpeng 300c1f63b2 fix:产品库存数据展示调整 2026-07-09 15:20:30 +08:00
jiangpeng 4b2e8538b1 fix:产品库存数据展示调整 2026-07-08 17:30:10 +08:00
jiangpeng cabdd4c531 fix:产品库存数据展示调整 2026-07-08 10:58:01 +08:00
jiangpeng 3d94027618 fix:产品库存数据展示调整 2026-07-08 02:01:08 +08:00
jiangpeng 972d121580 fix:入库单详情信息调整 2026-07-03 14:16:47 +08:00
jiangpeng 239009a59b fix:入库单详情信息调整 2026-07-02 17:27:28 +08:00
jiangpeng 4bd4fae390 fix:硬件维保时间计算规则调整 2026-07-01 13:52:33 +08:00
jiangpeng 40f40baf5e fix:制造商相关问题调整 2026-06-30 18:13:52 +08:00
jiangpeng c10a81f271 fix:菜单“出库管理”默认展开 2026-06-30 17:00:40 +08:00
jiangpeng f3f7a99b10 fix:采购单附件上传多个 2026-06-30 14:31:57 +08:00
jiangpeng 09fb371292 fix:bug处理 2026-06-29 17:30:59 +08:00
jiangpeng 61ba3c9680 fix:推送CRM失败记录日志 2026-06-29 16:51:22 +08:00
jiangpeng d0d6919b11 fix:发货管理(指定发货) 2026-06-26 23:21:21 +08:00
jiangpeng 9ee56a83a7 fix:发货管理(指定发货) 2026-06-26 17:27:08 +08:00
jiangpeng 954be37aa4 fix:MCP成本计算规则调整 2026-06-22 19:45:49 +08:00
jiangpeng 79010a0f36 fix:商务邮件发送抄送人变更 2026-06-17 10:23:09 +08:00
jiangpeng 134ca89b9a fix:订单配备货相关问题 2026-06-15 14:12:35 +08:00
jiangpeng eb1f4fb7b3 fix:用户登录和修改密码相关调整,处理了一些问题 2026-06-15 02:23:20 +08:00
jiangpeng 348eb137a8 fix:用户登录和修改密码相关调整,处理了一些问题 2026-06-11 17:55:33 +08:00
jiangpeng c440cb0357 fix:处理了一些问题 2026-06-10 17:51:20 +08:00
jiangpeng a5b4446202 fix:折上折bug修复 2026-06-09 18:04:15 +08:00
jiangpeng d55fc11e24 fix:订单配备货规则修改、添加已发货产品的虚拟采购单生成数据处理接口 2026-06-09 01:28:30 +08:00
jiangpeng 3b84d8d87c fix:修改密码相关功能 2026-06-05 18:01:19 +08:00
jiangpeng 1545771196 fix:修改密码相关功能 2026-06-05 15:15:16 +08:00
jiangpeng 2aaccc1193 fix:订单配备货相关调整 2026-06-04 17:56:57 +08:00
jiangpeng 0c451fcbed fix:导出订单信息Excel调整 2026-06-04 17:24:59 +08:00
jiangpeng 3b41b6b3d3 fix:MCP数据订单信息调整 2026-06-04 13:54:57 +08:00
jiangpeng 015ba6da70 fix:数据处理方法-生成虚拟采购单(已发货未绑定采购单的数据) 2026-06-04 09:20:57 +08:00
wangy c246975914 Merge remote-tracking branch 'origin/dev_1.0.2' into dev_1.0.2 2026-06-03 18:15:52 +08:00
wangy 8c2ecc89c8 feat(sip):bug修改,修改配置 2026-06-03 18:14:38 +08:00
jiangpeng 5fb2faecdc fix:MCP成本计算规则调整 2026-06-03 18:03:27 +08:00
jiangpeng 671bbd5692 fix:订单配备货相关链路功能调整 2026-06-01 14:40:36 +08:00
jiangpeng 8acc68141d fix:订单配备货相关链路功能调整 2026-06-01 02:30:13 +08:00
jiangpeng 71e8eb1e37 fix:订单配备货相关链路功能调整 2026-06-01 01:02:48 +08:00
wangy 7597a444b9 feat(sip):bug修改,入库仓ip修改为12,汇智虚拟仓 2026-05-29 14:32:15 +08:00
wangy d01a1faa1a feat(sip):bug修改,nex采购单增加姓名,邮箱和电话 2026-05-28 15:03:05 +08:00
wangy c27bfd26cf Merge remote-tracking branch 'origin/dev_1.0.2' into dev_1.0.2 2026-05-28 15:02:36 +08:00
wangy 11cdcdb83c feat(sip):bug修改,nex采购单增加姓名,邮箱和电话 2026-05-28 15:01:18 +08:00
jiangpeng 802b169818 fix:维保前端项目标题修改 2026-05-26 17:25:23 +08:00
jiangpeng 341000a3b0 fix:订单配备货 2026-05-26 16:12:54 +08:00
jiangpeng a1748ed05b fix:恢复后端test.yml修改 2026-05-26 15:48:11 +08:00
jiangpeng 1c601a908e fix:后端test.yml修改 2026-05-26 15:39:04 +08:00
jiangpeng 8bab6d7d17 fix:订单配备货 2026-05-26 15:25:15 +08:00
wangy 02f4c47383 Merge remote-tracking branch 'origin/dev_1.0.2' into dev_1.0.2 2026-05-22 15:33:08 +08:00
wangy ea52bec18d feat(sip):bug修改 2026-05-22 15:28:17 +08:00
chenhao 3584c72284 feat: 在清除出库信息时清空 order_code 字段 2026-05-21 20:29:43 +08:00
chenhao ee84487fe0 feat: 优化订单产品匹配和库存查询逻辑
- 在 `ProjectOrderInfoServiceImpl` 中,更新 `selectProductMatchList` 方法,增加绑定数量计算
- 修改 `selectPurchaseSnList` 方法,直接使用 `snList.size()` 计算总数
- 更新 `resetPwd.vue` 密码验证规则,增强密码强度要求
- 注释掉 `SysLoginController` 中的密码强度检查逻辑
- 优化 `ProjectOrderInfoMapper.xml` 查询语句,减少子查询
- 在 `index.vue` 中,增加缺货提示,并优化配额量输入处理
- 更新 `VendorInfoMapper.xml` 的 SQL 语句格式
2026-05-21 19:57:36 +08:00
wangy 8aa2b92fa6 feat(sip):bug修改 2026-05-21 17:20:02 +08:00
wangy 6bb9738dc5 feat(sip):bug修改 2026-05-21 17:00:29 +08:00
wangy e992a62a5b feat(sip):bug修改,税率税额取的是产品信息中的数据 2026-05-21 15:31:37 +08:00
wangy 5749d20c80 Merge remote-tracking branch 'origin/dev_1.0.2' into dev_1.0.2 2026-05-21 14:29:08 +08:00
wangy a7e4d9c1f4 feat(sip):bug修改,人员调整 2026-05-21 14:26:28 +08:00
jiangpeng f68242d2e7 fix:订单配备货 2026-05-21 10:28:15 +08:00
jiangpeng 98b9097ff2 fix:强制用户使用强密码使用邮箱验证码校验 2026-05-21 05:31:47 +08:00
jiangpeng 475229295a fix:测试环境数据库切换 2026-05-21 02:18:33 +08:00
jiangpeng 73a7f6d6ca fix:项目信息推送CRM处理接口 2026-05-15 15:16:16 +08:00
wangy 2fdbafa0c3 Merge remote-tracking branch 'origin/dev_1.0.2' into dev_1.0.2 2026-05-15 12:34:30 +08:00
wangy d9effe68c0 feat(sip):增加删除接口 2026-05-15 12:34:18 +08:00
jiangpeng 00f91b394a fix:发货bug处理,导入SN码bug处理 2026-05-15 12:16:40 +08:00
wangy 1e96076576 feat(sip):增加删除接口 2026-05-15 11:27:34 +08:00
jiangpeng 66d315069c fix:切换域名后的相关问题处理 2026-05-12 16:54:53 +08:00
jiangpeng af74c4033d fix:售后联系信息页面修改 2026-05-09 16:13:21 +08:00
jiangpeng 1aa1e592af fix:维保查询独立前端项目 2026-05-09 10:55:32 +08:00
jiangpeng cca2215a60 fix:订单全部发货后生成服务应收单应付单 2026-05-08 16:56:23 +08:00
chenhao 626d1f1b09 更新生产环境配置和数据库连接信息
- 修改 `VUE_APP_SERVICE_HOST` 为 `wb.oms.unisspace.com`
- 更新主库数据源 URL 和密码
2026-05-07 18:04:13 +08:00
jiangpeng d6b41e7cb2 fix:主要数据列表导出时可按选中行导出 2026-04-28 19:52:01 +08:00
jiangpeng 8be71600a2 fix:付款单页面字段改名,代理商“金牌”过滤调整,维保页面标签调整 2026-04-28 18:29:50 +08:00
jiangpeng 1735901ffe fix:付款单申请退款时可修改金额、可分多笔退款 2026-04-27 20:19:58 +08:00
jiangpeng 27ac07ad0a fix:运维管理功能(项目和订单转移) 2026-04-27 11:07:04 +08:00
jiangpeng c2bbeb7cdc fix:付款单详情添加导出PDF和打印功能,付款单列表导出添加应付单信息 2026-04-24 15:38:14 +08:00
jiangpeng 4bdbc1daf7 fix:开票信息表单修改、发货展示收货地址 2026-04-24 09:45:43 +08:00
jiangpeng ae727d0fdd fix:订单审批通过时生成订单信息pdf 2026-04-23 02:00:18 +08:00
jiangpeng 515abca80f fix:付款单附件规则修改、修改订单流程调整 2026-04-22 18:55:30 +08:00
jiangpeng 773f400add fix:产品管理导出功能调整 2026-04-22 17:00:59 +08:00
jiangpeng b274c5e4af fix:入库单新增授权码字段 2026-04-22 15:46:16 +08:00
jiangpeng af6d748b86 fix:售后页面调整 2026-04-21 19:08:11 +08:00
jiangpeng fd5a2e60e4 fix:售后页面调整 2026-04-21 18:13:22 +08:00
jiangpeng ce4044bf8c fix:入库单录入时修改采购状态 2026-04-21 17:46:42 +08:00
jiangpeng 53b84e0c9b fix:开票单信息调整,开票单导出Excel 2026-04-21 12:32:26 +08:00
jiangpeng c410d4e2e3 fix:审批通过时推送信息到CRM,发货记录详情中展示收货地址 2026-04-20 17:09:02 +08:00
jiangpeng f7516a9ffe fix:维保页面调整,文件下载文件名格式调整 2026-04-20 15:36:20 +08:00
jiangpeng 778f855ee9 fix:供应商确认功能调整、售后查询页面调整 2026-04-16 19:17:12 +08:00
jiangpeng feb99bb10e fix:维保页面调整 2026-04-16 14:14:55 +08:00
UNISINSIGHT\rdpnr_jiangpeng b7067082ed fix:同步新华三接口调整,数据处理功能 2026-04-16 12:50:04 +08:00
UNISINSIGHT\rdpnr_jiangpeng 9a8fe13f28 fix:同步新华三接口调整,应付单详情添加超链接 2026-04-14 18:06:42 +08:00
UNISINSIGHT\rdpnr_jiangpeng 4ac353a83a fix:订单撤回bug处理 2026-04-14 11:07:05 +08:00
UNISINSIGHT\rdpnr_jiangpeng 7f39e3029f fix:产品类型变更后的服务金额计算调整 2026-04-13 18:08:14 +08:00
UNISINSIGHT\rdpnr_jiangpeng 086af70f22 fix:联系电话修改 2026-04-10 18:12:07 +08:00
UNISINSIGHT\rdpnr_jiangpeng 0e96989aaa fix:订单管理按条件查询导出,应收单、应付单添加项目编号查询,维保服务页面调整 2026-04-10 17:11:38 +08:00
UNISINSIGHT\rdpnr_jiangpeng 9fd0d3caf4 fix:产品类型问题处理 2026-04-10 15:24:42 +08:00
UNISINSIGHT\rdpnr_jiangpeng 6328264f90 fix:应收单列表查询调整 2026-04-10 09:57:24 +08:00
UNISINSIGHT\rdpnr_jiangpeng 2ebd64ab41 fix:应付单、应收单列表查询调整 2026-04-09 18:08:42 +08:00
UNISINSIGHT\rdpnr_jiangpeng 6a26d412c4 fix:新维保查询页面 2026-04-09 15:23:35 +08:00
UNISINSIGHT\rdpnr_jiangpeng 504379f3e3 fix:订单管理查询条件调整 2026-04-07 23:30:06 +08:00
UNISINSIGHT\rdpnr_jiangpeng 81c561d28a fix:补全代理商生成二维码功能 2026-04-07 18:05:26 +08:00
UNISINSIGHT\rdpnr_jiangpeng 8784a143f1 fix:项目信息推送CRM 2026-04-07 17:44:22 +08:00
UNISINSIGHT\rdpnr_jiangpeng 9439b82081 fix:订单信息导出excel字段调整 2026-04-03 11:31:21 +08:00
UNISINSIGHT\rdpnr_jiangpeng e036fe7e10 fix:项目管理列表查询条件优化 2026-04-03 10:28:41 +08:00
chenhao 7ffd5135cc feat: 添加SysBotCredentialMapper以支持Bot凭证管理
- 新增 `SysBotCredentialMapper.xml` 文件,定义Bot凭证的CRUD操作
- 包含查询、插入、更新和访问信息更新的方法
2026-04-02 16:10:21 +08:00
UNISINSIGHT\rdpnr_jiangpeng f03fc06949 fix:数据同步新华三调整 2026-04-02 14:43:33 +08:00
UNISINSIGHT\rdpnr_jiangpeng 4d8e4fa01d fix:对接CRM 2026-04-01 17:28:28 +08:00
UNISINSIGHT\rdpnr_jiangpeng 980a8147ce fix:订单版本号变更bug修复 2026-04-01 14:47:10 +08:00
UNISINSIGHT\rdpnr_jiangpeng 60a9bdab2e fix:订单版本号变更bug修复 2026-04-01 14:06:23 +08:00
UNISINSIGHT\rdpnr_jiangpeng e9c73d1925 fix:订单版本号变更bug修复 2026-03-31 17:55:18 +08:00
UNISINSIGHT\rdpnr_jiangpeng 6f3b51f461 fix:发货单撤回bug修复 2026-03-30 16:09:58 +08:00
chenhao 5eec808514 feat: 更新项目订单导出和批量查询功能
- 修改 `VueProjectOrderInfoController` 中的导出方法,调用 `exportMcpList` 代替 `exportList`
- 在 `IProjectOrderInfoService` 接口中添加 `exportMcpList` 方法
- 在 `ProjectInfo` 实体类中添加 `orderCode` 属性
- 在 `OmsInventoryInnerMapper` 和 `InventoryInfoMapper` 中添加按 `orderCodeList` 和 `innerCodeList` 查询的方法
- 在 `ProjectInfoMapper` 中添加按 `orderCodeList` 查询的方法
- 扩展 `ProjectOrderInfoToolProvider` 类,添加相关依赖和服务,并更新处理逻辑
2026-03-30 16:04:59 +08:00
UNISINSIGHT\rdpnr_jiangpeng 9e8edf01b8 fix:发货单撤回bug修复 2026-03-30 15:37:13 +08:00
UNISINSIGHT\rdpnr_jiangpeng baae63153a fix:产品二级类型相关调整 2026-03-30 14:52:13 +08:00
UNISINSIGHT\rdpnr_jiangpeng 9a491ab1c7 fix:付款审批详情:采购应付单列表调整 2026-03-30 12:48:38 +08:00
UNISINSIGHT\rdpnr_jiangpeng 1f86f382df fix:付款审批详情:采购应付单列表调整 2026-03-30 10:58:39 +08:00
chenhao 7a7b549de3 feat(router): 添加服务主机路径配置和产品维保查询标题更新
- 引入 `isServiceHost` 函数并根据服务主机设置路由路径
- 更新产品维保查询页面标题
- 在生产环境配置中添加 `VUE_APP_SERVICE_HOST`
- 更新权限检查逻辑以支持服务主机路径
2026-03-26 20:09:59 +08:00
UNISINSIGHT\rdpnr_jiangpeng 9b90d44e34 fix:供应商名称调整,采购列表导出调整 2026-03-26 18:40:47 +08:00
UNISINSIGHT\rdpnr_jiangpeng c66bdbcc74 fix:产品管理折扣值浮点精度问题处理 2026-03-26 16:40:13 +08:00
UNISINSIGHT\rdpnr_jiangpeng 4b7021b45e fix:付款单审批展示附件信息 2026-03-26 15:42:33 +08:00
UNISINSIGHT\rdpnr_jiangpeng 3d2266f4a1 fix:产品号相加结果确保为整数 2026-03-26 14:40:01 +08:00
UNISINSIGHT\rdpnr_jiangpeng 2fae492f2b fix:产品折扣金额bug处理 2026-03-26 11:27:53 +08:00
UNISINSIGHT\rdpnr_jiangpeng 5ac798c295 fix:付款单、收票单、收款单、开票单详情展示流转意见信息 2026-03-25 19:25:06 +08:00
UNISINSIGHT\rdpnr_jiangpeng 61143a39f7 fix:数据同步新华三调整 2026-03-25 14:17:58 +08:00
UNISINSIGHT\rdpnr_jiangpeng 9d067ab748 fix:产品折扣金额bug处理 2026-03-25 10:22:12 +08:00
UNISINSIGHT\rdpnr_jiangpeng 1860ac322e fix:产品二级类型相关调整 2026-03-24 17:06:01 +08:00
UNISINSIGHT\rdpnr_jiangpeng 50855a68ce fix:产品二级类型相关调整 2026-03-24 13:06:59 +08:00
UNISINSIGHT\rdpnr_jiangpeng 53d900ca22 fix:应付付款审批添加应付单、项目超链接 2026-03-24 11:03:51 +08:00
UNISINSIGHT\rdpnr_jiangpeng 6280850e30 fix:产品管理页面调整 2026-03-24 10:15:38 +08:00
UNISINSIGHT\rdpnr_jiangpeng 96ede9b575 fix:产品配置列表查询调整 2026-03-24 09:56:26 +08:00
chenhao a7e0598951 feat(inventory): 添加批量查询出库单条码和价格详情功能
- 新增OutboundOrderInventoryInfoToolProvider工具类,支持通过出库单号批量查询条码和价格详情
- 扩展IInventoryInfoService接口及其实现类InventoryInfoServiceImpl,添加按出库单号列表查询库存信息的方法
- 更新InventoryOuter实体类,添加outerCodeList属性
- 修改InventoryOuterMapper.xml,支持按出库单号列表查询
- 更新InventoryInfoMapper.java及InventoryInfoMapper.xml,支持按出库单号列表查询库存信息
2026-03-23 17:36:07 +08:00
UNISINSIGHT\rdpnr_jiangpeng 487bf5e0c6 fix:审批中心数据列表排序字段调整 2026-03-23 16:35:22 +08:00
UNISINSIGHT\rdpnr_jiangpeng 93053025a2 fix:无效代码清理 2026-03-23 12:41:55 +08:00
UNISINSIGHT\rdpnr_jiangpeng 5409a3c225 fix:付款单/收票单/收款单/开票单详情中添加应付单/应收单详情页 2026-03-23 12:31:08 +08:00
chenhao c39ed025b9 Merge remote-tracking branch 'origin/dev_1.0.2' into dev_1.0.2 2026-03-20 16:55:05 +08:00
chenhao af221e4486 feat: 添加Bot配置页面
- 创建botConfig.vue页面,展示Bot ID、状态、最近使用时间、最近使用IP和过期时间
- 实现生成和重置Bot Secret功能,并提供复制Secret按钮
- 加载Bot信息并在页面上显示相关数据
2026-03-20 16:54:55 +08:00
UNISINSIGHT\rdpnr_jiangpeng 0f656cfed6 fix:新增产品类型 2026-03-20 16:51:19 +08:00
UNISINSIGHT\rdpnr_jiangpeng 6b62162e8d fix:合同改单,废单时与新华三保持数据同步 2026-03-20 16:49:46 +08:00
wangy 2373b13fc6 feat(sip):订单合同修改新增产品经理 2026-03-20 16:21:04 +08:00
chenhao 4cc420799e fix: 修复角色数据权限分配中的注释代码问题并添加数据范围注解
- 在DataScopeAspect中修复角色数据权限分配的逻辑
- 在ProjectOrderInfoServiceImpl中添加@DataScope注解以限制导出和列表数据范围
2026-03-20 13:52:27 +08:00
chenhao 48bad8a9a6 feat: 添加MCP Bot凭证生成和管理功能
- 新增McpBotCredentialUtils工具类,提供Bot ID、Secret、Salt及哈希生成方法
- 在SysProfileController中添加Bot凭证相关接口,支持查询和生成Bot凭证
- 引入新的依赖ruoyi-framework,并更新pom.xml
- 扩展InventoryOuter实体类,添加orderCodeList属性
- 实现IOrderLLMService接口及其服务实现类IOrderLLMServiceImpl
- 添加McpToolProvider接口及相关实现类ProjectOrderInfoToolProvider和ProductInfoToolProvider
- 增加AbstractMcpToolProvider抽象类,提供通用的MCP工具构建逻辑
- 更新ProjectOrderInfo实体类,添加createTimeStart和createTimeEnd属性
- 新增SysBotCredentialMapper接口,定义Bot凭证相关数据库操作
- 添加McpBotAuthFilter过滤器,处理Bot身份验证逻辑
2026-03-20 11:24:23 +08:00
UNISINSIGHT\rdpnr_jiangpeng adae1ab595 fix:制造商数据操作bug处理 2026-03-18 15:39:37 +08:00
UNISINSIGHT\rdpnr_jiangpeng 7bcc08ff0e fix:客户信息重名校验 2026-03-17 16:08:54 +08:00
UNISINSIGHT\rdpnr_jiangpeng 6b9263ca1b fix:数据列表排序调整 2026-03-16 15:52:33 +08:00
UNISINSIGHT\rdpnr_jiangpeng 8abca803f9 fix:报错sql修复 2026-03-16 15:51:44 +08:00
UNISINSIGHT\rdpnr_jiangpeng 983780b73b fix:列表页面排序问题调整 2026-03-13 17:17:04 +08:00
UNISINSIGHT\rdpnr_jiangpeng 04b59e281a fix:财务模块-付款单页面查询条件修改 2026-03-11 10:17:41 +08:00
UNISINSIGHT\rdpnr_jiangpeng 9f11895847 fix:财务模块-付款单页面添加附件补充功能 2026-03-10 14:32:45 +08:00
UNISINSIGHT\rdpnr_jiangpeng d1910e2497 fix:应付单列表(发起付款、发起收票)添加备注信息 2026-03-09 14:46:46 +08:00
UNISINSIGHT\rdpnr_jiangpeng 268c56e7ce fix:采购列表页面调整 2026-03-06 16:17:23 +08:00
UNISINSIGHT\rdpnr_jiangpeng e251f3fccb fix:采购列表返回字段修改 2026-03-06 15:10:15 +08:00
UNISINSIGHT\rdpnr_jiangpeng abcd3c1c6f fix:抄送邮箱修改 2026-03-06 12:34:22 +08:00
UNISINSIGHT\rdpnr_jiangpeng 4b8cd8b9e8 fix:财务模块数据列表调整 2026-03-05 17:54:06 +08:00
UNISINSIGHT\rdpnr_jiangpeng aff3016e4f fix:采购列表页面优化 2026-03-05 15:14:43 +08:00
chenhao 115394106a fix(inventory): 解决发货数据仓库一致性验证问题
- 添加仓库ID一致性检查逻辑
- 验证发货数据仓库与出库单仓库是否匹配
- 不一致时抛出服务异常并提示用户重新确认
- 恢复票据类型选择框功能而非只读标签显示
2026-03-04 19:08:21 +08:00
chenhao ddcde9e1da fix(inventory): 解决发货数据仓库一致性验证问题
- 添加仓库ID一致性检查逻辑
- 验证发货数据仓库与出库单仓库是否匹配
- 不一致时抛出服务异常并提示用户重新确认
- 恢复票据类型选择框功能而非只读标签显示
2026-02-28 16:27:07 +08:00
wangy 6f072b97c9 feat(sip):1.0.2合并 2026-02-27 15:01:41 +08:00
wangy ab03f70fd6 Merge remote-tracking branch 'origin/dev_1.0.2' into dev_1.0.1
# Conflicts:
#	ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/InventoryDeliveryServiceImpl.java
2026-02-27 14:57:01 +08:00
chenhao c759c8bc55 feat(inventory): 添加订单执行跟踪导出功能
- 新增 exportExecution API 接口用于导出订单数据
- 在前端界面添加导出按钮并集成权限控制
- 更新服务层实现导出逻辑并调整表头字段顺序
- 增加订单生效时间和要求到货时间字段显示
- 完善后端控制器的导出接口权限验证机制
2026-02-11 09:21:39 +08:00
chenhao e298748d45 feat(purchaseorder): 添加产品编码和型号查询功能
- 在采购订单列表页面添加产品编码和产品型号查询条件输入框
- 在表格中新增产品编码、产品型号和数量列显示相关信息
- 实现多条目时通过popover弹窗展示所有产品的详细信息
- 后端增加按产品编码和型号过滤采购订单的功能
- 优化采购订单详情查询逻辑以支持新的筛选条件
- 调整产品描述列宽度以改善显示效果
2026-02-10 11:24:51 +08:00
chenhao e04b80ff25 fix(order): 修复订单详情金额计算精度问题
- 订单详情页面总金额计算增加toFixed(2)精度控制
- 付款单据上传权限标识从finance:payment:upload调整为finance:payment:uploadReceipt
- 收款单据上传权限标识从finance:receive:upload调整为finance🧾uploadReceipt
2026-02-10 11:02:23 +08:00
chenhao 830fde4bc0 feat(inventory): 添加产品编码和型号搜索功能
- 在库存交付页面添加产品编码和型号搜索字段
- 在库存执行页面添加产品编码和型号搜索字段
- 在外部库存页面添加产品编码和型号搜索字段
- 在项目信息页面添加产品编码和型号搜索字段
- 更新库存交付数据映射器支持产品编码和型号查询
- 扩展项目信息实体类添加产品编码和型号属性
- 更新项目信息数据映射器支持产品编码和型号过滤查询
2026-02-09 10:09:37 +08:00
chenhao 7ff9552621 feat(quotation): 更新报价产品信息精度和列表排序功能
- 将QuotationProductInfo实体类中的数值字段从Double改为BigDecimal以提高精度
- 在多个页面组件中添加默认排序配置(isAsc和orderByColumn字段)
- 更新报价导出功能的表头和数据格式,添加折扣百分比显示
- 修复供应商和客户页面的时间戳字段名称(createAt改为createTime)
- 添加产品编码和型号查询条件到订单页面
- 优化数值计算逻辑,使用BigDecimal进行精确计算
2026-02-06 17:54:58 +08:00
wangy 6776fe3f0d Merge remote-tracking branch 'origin/dev_1.0.2' into dev_1.0.1
# Conflicts:
#	ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/InventoryDeliveryServiceImpl.java
2026-02-04 17:12:01 +08:00
chenhao e2f9e0e393 fix(order): 修复采购订单邮件抄送地址配置
- 移除错误的抄送邮箱地址 chenhao2@unisinsight.com
- 恢复正确的抄送邮箱地址 laiyk@dsipc.com.cn
- 针对供应商代码 P004 的邮件抄送逻辑修正
2026-02-04 16:31:56 +08:00
chenhao 7d8ad420e4 feat(purchase): 添加供应商确认邮件通知功能
- 在采购订单实体类中新增供应商邮箱字段
- 更新采购订单查询映射,添加供应商代码和邮箱信息
- 集成模板邮件工具类,实现供应商确认邮件发送
- 添加供应商确认邮件模板HTML文件
- 实现邮件发送逻辑,支持抄送特定供应商邮件
- 修复角色数据权限分配中的注释代码问题
2026-02-04 16:14:41 +08:00
chenhao 356fc1995e feat(quotation): 更新报价单页面展示创建人信息
- 在表格中添加创建人列并显示创建人姓名
- 为报价金额列添加货币格式化显示
- 更新步骤组件样式,添加自定义图标和激活状态高亮
- 修改数据查询逻辑,关联用户表获取创建人姓名
- 在实体类中添加createByName字段用于接收创建人信息
2026-02-04 13:39:36 +08:00
chenhao d9da314968 Merge branch 'refs/heads/dev_1.0.0' into dev_1.0.2
# Conflicts:
#	ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/InventoryDeliveryServiceImpl.java
2026-02-04 09:59:33 +08:00
wangy 3951ef325d Merge remote-tracking branch 'origin/dev_1.0.0' into dev_1.0.1 2026-02-04 09:24:12 +08:00
chenhao a7f7a29d74 feat(quotation): 新增报价单详情页面并完善相关功能
- 新增报价单详情页面实现查看功能
- 添加选择报价单组件支持项目关联报价单
- 更新权限配置将base模块改为sip模块
- 在项目表中新增合作商系统用户ID字段
- 完善报价单控制器的权限注解
- 优化报价单导出Excel格式和样式
- 修复查询条件中的表别名问题
- 添加省代服务产品类型支持
2026-02-03 15:15:12 +08:00
chenhao 50ee54d6ef feat(quotation): 实现报价单绑定项目功能
- 在报价单表格中添加状态显示,使用字典标签展示报价状态
- 增加quotation_status字典类型用于状态管理
- 扩展ProjectInfo实体类,添加quotationId和quotationIdList字段
- 更新数据库映射文件,增加报价单ID相关字段和查询条件
- 在项目创建和更新流程中实现报价单绑定逻辑
- 添加bind和unBind方法到报价单服务接口和实现类
- 实现报价单状态枚举类,定义未绑定和已绑定状态
- 清理报价单表中废弃的项目代码和项目ID字段
- 优化报价单导出Excel模板中的项目信息展示
- 添加延迟注入项目信息服务以解决循环依赖问题
2026-02-03 10:32:34 +08:00
chenhao 701d90779a feat(quotation): 增加报价单详情查看和复制创建功能
- 关闭开发环境邮件发送功能
- 隐藏报价金额显示折后金额
- 添加详情按钮用于查看报价单详情
- 添加复制创建按钮实现基于现有报价单创建新报价单
- 实现复制创建功能清除原报价单ID和状态信息
- 添加项目表单中的报价单导入功能
- 更新删除接口路径为批量删除模式
- 修复删除确认提示信息过长问题
2026-01-30 11:22:24 +08:00
chenhao 61b10eba26 feat(quotation): 添加报价单导出功能
- 在报价单列表页面添加导出按钮,支持单个报价单导出为Excel文件
- 实现exportSingle方法,支持按ID导出指定报价单数据
- 集成EasyExcel库,实现报价单数据的Excel格式化导出
- 添加报价单导出的API接口和权限控制
- 实现包含Logo、标题、产品明细等完整报价单格式的Excel文件生成
- 添加导出功能的前端调用和下载处理逻辑
2026-01-29 18:07:31 +08:00
chenhao 45f69d527e feat(finance): 新增付款单附件功能并优化表格显示
- 在新增付款单表单中添加附件上传组件
- 修改表格样式设置避免滚动条冲突
- 移除表格固定高度限制提升用户体验
- 在付款单详情页面显示附件信息
- 添加付款单删除功能支持预付款单删除
- 付款单编号列添加链接跳转至详情页
- 后端实体类和数据库映射增加文件ID字段
- 实现附件查询服务关联付款单数据
- 货币金额显示格式化为货币格式
- 文件上传组件集成到付款单流程中
2026-01-29 17:14:42 +08:00
chenhao a56d750f2d feat(finance): 添加金额格式化显示功能并优化表单界面
- 在多个财务模块的表格列中添加金额格式化显示功能
- 统一使用 formatCurrency 方法格式化含税总价、未开票金额等数值
- 在付款单新增表单中添加其它特别说明字段
- 调整表格最大高度以优化界面显示
- 在详情抽屉中统一应用金额格式化处理
- 修改数据默认值以避免空值错误
- 优化审批流程中的金额显示格式化
2026-01-29 13:44:28 +08:00
chenhao aa2efbd42e feat(quotation): 添加报价单功能模块
- 新增报价单管理界面,支持报价单的增删改查操作
- 实现报价单基本信息设置和产品配置功能
- 添加报价单号自动生成机制,支持按日期和序列号生成
- 集成代表处选择和客户信息管理功能
- 实现多步骤表单流程,支持基本信息和配置信息分步填写
- 添加产品列表管理,支持软件、硬件和服务产品的分类配置
- 实现报价金额和折后金额的自动计算功能
- 优化发货记录中的服务年限计算逻辑
- 修复代码生成器中编号从0开始的问题,调整为从1开始
2026-01-27 18:04:44 +08:00
chenhao 6bed84cf3e feat(delivery): 添加发货管理的数据权限控制功能
- 在发货清单查询中集成数据权限注解支持
- 为发货服务类添加 DataScope 注解配置
- 更新 MyBatis 映射文件以支持数据权限 SQL 注入
- 添加用户职责关联查询以支持权限判断
- 修改前端组件移除不必要的自动加载逻辑
- 在用户控制器中增加角色授权接口实现
2026-01-27 13:40:43 +08:00
chenhao 330f2f7d54 feat(project): 优化项目信息表格布局并添加导出功能
- 调整表格列宽度以改善显示效果
- 在项目详情抽屉中添加技术方案终审下载按钮
- 实现导出单个项目技术方案的功能
- 移除原表格中的冗余导出按钮并改为注释形式
- 新增handleExportSingle方法处理文件导出逻辑
2026-01-27 10:55:38 +08:00
chenhao b96d006f9c feat(approve): 添加产品编码和型号筛选功能并优化订单金额显示
- 在审批订单页面添加产品编码和产品型号搜索条件
- 实现订单金额的货币格式化显示功能
- 添加格式化工具函数用于金额显示
- 修改订单总金额计算逻辑以支持不同订单状态
- 调整表格列布局以适应不同的订单状态显示需求
- 在采购审批相关页面添加产品筛选条件
- 更新后端实体类以支持产品编码和型号字段
- 优化库存发货服务中的代码格式和业务逻辑
- 添加年份计算的乘法逻辑用于服务期限计算
2026-01-26 17:48:19 +08:00
chenhao baa4b52553 feat(purchaseorder): 添加采购单导出功能并优化界面显示
- 在后端控制器中实现export方法返回AjaxResult并设置审批状态过滤
- 添加exportPurchaseorder API函数用于导出采购订单数据
- 在前端界面添加导出按钮及权限控制
- 修改下载插件中的方法名为download替代name
- 修复多个选择组件中的表单提交阻止默认行为
- 优化采购单详情对话框和抽屉组件的属性格式化
- 隐藏采购订单实体类中部分字段的Excel导出注解
- 修正审批时间Excel导出的时间格式化设置
- 更新多个select组件中的键盘事件处理方式
2026-01-26 14:20:27 +08:00
chenhao dd2a7d99b6 fix(inventory): 解决导入SN数据时状态设置时机问题
- 将isImported状态设置移到下一个DOM更新周期后执行
- 确保表格选择操作完成后再更新导入状态
- 避免因状态设置过早导致的UI更新异常
2026-01-26 09:57:21 +08:00
chenh e1643a6331 Merge branch 'dev_1.0.0' into dev_1.0.1 2026-01-15 17:00:37 +08:00
chenh 5f6f014226 feat(sip):订单合同修改新增产品经理 2026-01-15 16:54:27 +08:00
chenh b3e5568216 feat(sip):提供给NEX的接口调试 2026-01-15 16:52:26 +08:00
chenh cc29006013 Merge branch 'dev_1.0.0' into dev_1.0.1
# Conflicts:
#	ruoyi-sip/src/main/java/com/ruoyi/sip/controller/vue/OmsPurchaseOrderController.java
2026-01-14 14:00:51 +08:00
chenh db343353cc feat(sip):提供给NEX的接口调试 2026-01-14 13:59:29 +08:00
chenh 01cd03213e feat(sip):新增一个根据订单id查询审批状态的接口 2026-01-14 09:33:44 +08:00
chenh 31a8de5569 Merge remote-tracking branch 'origin/dev_1.0.0' into dev_1.0.0 2026-01-12 17:49:48 +08:00
chenh ec3e34ad21 feat(sip):修改进销存的采购订单的导出接口 2026-01-12 17:48:58 +08:00
chenhao 0d14477705 fix(project): 修复项目会审状态修改逻辑
- 添加 canGenerate 检查防止已存在订单的项目取消会审
- 在无法生成订单时设置 jointTrial 为 '1'
- 显示错误消息提示用户操作被阻止
- 确保会审状态在订单存在时不被意外修改
2026-01-09 13:14:07 +08:00
chenhao 74ed165f71 fix(finance): 修复财务报表数据查询和权限控制问题
- 修复应付票据明细查询中关联核销单的SQL逻辑
- 修复应收收款明细查询中关联核销单的SQL逻辑
- 在收款对话框上传按钮添加权限控制指令
- 在收款对话框上传按钮添加权限控制指令
- 将报表中毛利字段标签从"金额"更正为"毛利"
- 注释掉业务计收时间和财务计收时间列以修复数据显示问题
2026-01-08 18:13:30 +08:00
chenhao f2cbf8f9cb feat(finance): 为财务模块各页面添加默认排序功能
- 在费用页面添加按创建时间降序排序
- 在应付账款页面添加按创建时间降序排序
- 在付款页面添加按创建时间降序排序
- 在收款页面添加按创建时间降序排序
- 在应收账款页面添加按创建时间降序排序
- 在收票页面添加按创建时间降序排序
- 在财务报表页面添加按创建时间降序排序
- 在应付账款报表页面添加按创建时间降序排序
- 在收票报表页面添加按创建时间降序排序
- 在发票核销记录页面添加按创建时间降序排序
- 在付款核销记录页面添加按创建时间降序排序
- 在收款核销记录页面添加按创建时间降序排序
- 在票据核销记录页面添加按创建时间降序排序
2026-01-08 17:11:49 +08:00
chenhao 5485a827be fix(finance): 修复财务模块表单验证和显示问题
- 为申请开票表单添加disabled属性控制
- 注释掉开票详情表格中的操作列
- 调整condensed-item样式间距和错误提示层级
- 为申请付款对话框添加表单验证规则
- 修复银行账号字段名称错误
- 为申请退款对话框添加表单验证规则
- 实现退款信息变更确认提示功能
- 动态显示开票单和红冲开票详情标题
- 为财务详情页面金额字段添加负数红色显示
- 在表格中显示绝对值金额
- 为审批页面添加标签状态颜色
- 修复审批历史加载的单据编号
- 调整审批页面付款编号显示
- 为开票列表页面金额字段添加负数红色显示
- 修正生成收票单按钮文本为生成开票单
- 修复上传发票对话框票据类型显示问题
2026-01-08 17:00:09 +08:00
chenhao 0dfdeee19d feat(finance): 优化财务模块界面显示和业务逻辑
- 添加paymentRedLog路由用于付款退款审批日志
- 统一各审批页面底部编号显示格式,移除冗余标签文字
- 为金额列添加红色样式高亮显示
- 优化审批状态标签颜色显示,根据状态值动态设置标签类型
- 修复收票单详情页面跳转路径错误
- 在各财务单据详情页添加标题标识
- 优化发票详情页金额显示颜色
- 修复发票单据数据更新时时间字段处理逻辑
- 优化供应商信息回填逻辑,避免覆盖已有数据
- 完善退款流程中的单据状态检查
- 修复收票单红冲功能中的业务逻辑错误
- 更新付款单详情页显示字段
- 添加技术方案会审分隔区域
- 修复附件获取类型参数错误
2026-01-08 15:51:33 +08:00
chenhao d1e23c58ef fix(inventory): 解决生成发货单表单数据残留问题
- 在 GenerateDeliveryForm.vue 中打开表单时清空已选SN列表
- 修复表格初始化加载时的选择状态清理逻辑
- 修正收票单详情页面显示的编号字段错误
- 更新项目管理日志标题为会审相关
- 添加项目订单文件修改权限相关注释说明
2026-01-08 13:47:50 +08:00
chenh 7a6abccf2f feat(sip):新增提供给nex的接口 2026-01-08 09:34:11 +08:00
chenhao 602d06aafc feat(project): 添加项目列表排序功能和调整界面布局
- 在项目列表中添加排序功能,支持按最后操作时间等字段排序
- 新增最后操作时间列显示项目更新时间
- 将操作列宽度从300调整为400以适应更多操作按钮
- 调整详情抽屉和表单的标签宽度从120px到150px
- 修复后端时间格式化问题,将更新时间格式调整为完整时间格式
- 优化数据库查询,添加更新时间字段到查询结果中
- 修复更新语句中合作伙伴代码和文件ID的更新逻辑
2026-01-07 20:41:53 +08:00
chenhao 59b045d457 feat(project): 项目管理功能增强
- 实现行业类型多选功能,支持多个行业筛选
- 添加技术方案终审下载按钮,仅在联合试用为1时显示
- 集成文件上传组件,支持项目相关附件管理
- 新增授权、终端、服务器等技术信息字段管理
- 实现技术方案单个导出功能,添加权限控制
- 优化项目详情展示,增加虚拟机配置信息
- 添加会审结论和项目风险技术问题字段
- 完善项目表单验证和数据绑定逻辑
- 修复联合试用状态变更时的业务逻辑验证
2026-01-07 20:07:36 +08:00
chenhao 97bcc1ac0a feat(project): 优化项目管理和收款单功能
- 在application-prod.yml中配置flowable数据库模式更新设置
- 为项目信息表格的收藏和会审列添加固定右侧显示功能
- 修改订单生成逻辑,添加会审状态检查和权限验证
- 实现项目会审状态修改时的订单存在性验证
- 添加收款单按编码查询的接口和实现
- 重构收款单审批回调逻辑,支持多实例审批流程
- 更新项目生成订单权限判断逻辑,移除会审状态依赖
- 在订单创建时添加项目会审状态验证机制
2026-01-07 15:39:11 +08:00
chenhao b1cbbec237 feat(project): 添加项目收藏和会审功能
- 在项目信息页面添加是否收藏和是否会审的筛选条件
- 在项目表格中添加收藏和会审状态显示列
- 实现项目收藏状态的修改功能
- 实现项目会审状态的修改功能
- 添加权限控制,无权限用户无法修改会审状态
- 生成订单时增加会审状态判断,只有会审通过的项目才能生成订单
- 添加项目用户收藏信息的数据表和相关服务
- 在项目列表查询时关联用户收藏状态
- 修复代理处变更校验的逻辑错误
- 更新项目导出功能,添加收藏和会审字段的导出
2026-01-06 20:49:27 +08:00
chenhao 4ce68abe84 fix(finance): 修复财务模块显示和数据处理问题
- 修正付款单和收票单状态显示,将'已生成'改为'全部生成'以保持一致性
- 修正收款单和开票单状态显示,将'已生成'改为'全部生成'
- 修正应收模块中生成时间列的布局问题,将span从16改为8
- 修正订单编号标签为合同编号
- 将供应商标签改为制造商
- 修正核销模块中表格列标题,将'应付单生成时间'和'付款单生成时间'统一为'生成时间'
- 隐藏付款单核销总额显示
- 为库存查询添加limit 1优化
- 添加付款单按编码查询的接口和实现
- 修正合并发票和收票对话框中的进货商名称显示宽度
- 在付款明细查询中添加产品类型字段
- 修正项目名称为空时的显示,使用'应付'或'应收'占位符
- 添加付款类型和配置天数字段到付款单实体
- 修正收票单查询中的合作伙伴名称条件
- 移除预收款自动核销的TODO注释
- 修正预付款恢复通知邮件标题为预收款恢复通知
- 修正附件删除时的单据类型枚举
- 添加收票单审批完成后的自动核销功能
- 修正开票和收款计划时间格式,从日期改为日期时间
- 添加订单详情中产品信息和折扣验证的空值检查
- 添加付款详情页面的标题显示
- 修正应付单列表标题为应付单信息
- 修正邮件模板类型定义,更新预付款和预收款相关通知的描述
- 在待办服务中添加业务信息处理和流程键匹配逻辑
2026-01-06 18:04:24 +08:00
chenhao 3102a2c0a0 feat(sip): 实现供应商和合作伙伴邮件通知功能
- 启用邮件发送功能配置
- 添加预付款和预收款变动的邮件模板
- 实现在核销和恢复操作时自动发送邮件通知给供应商和合作伙伴
- 集成邮件工具类支持模板邮件发送
- 添加供应商信息批量查询功能
- 实现在预付款核销时发送余额变动通知
- 实现在预付款恢复时发送退款通知
- 实现在预收款处理时发送相应通知邮件
- 添加新的DTO类用于项目付款单数据传输
2026-01-05 20:57:34 +08:00
chenhao 357410c3e7 feat(finance): 添加应付和应收报表功能
- 创建应付报表页面,包含项目编号、名称、计收状态、付款状态、收票状态等查询条件
- 实现应付报表表格展示,包含计收统计、应付单、付款单、收票单等数据列
- 添加应付报表导出功能,支持权限控制和数据导出
- 创建应收报表页面,包含项目编号、名称、计收状态、收款状态、开票状态等查询条件
- 实现应收报表表格展示,包含应收单、计收统计、收款单、开票单等数据列
- 添加应收报表导出功能,支持权限控制和数据导出
2026-01-05 17:26:33 +08:00
chenhao a4ce1ba45a feat(finance): 添加财务运营报表功能并优化收款单详情展示
- 新增财务运营报表页面,支持项目报表的查询和导出功能
- 添加财务运营报表相关实体类和数据访问接口
- 优化收款单详情抽屉组件,调整字段显示和布局
- 修复订单配置信息中折后总价合计的显示条件
- 更新定时任务白名单配置,添加sip模块包路径
- 修复右侧工具栏下拉菜单样式问题
- 调整财务计收状态枚举值描述,优化计收流程状态显示
- 添加财务模块报表统计功能接口和实现
- 优化财务计收列表查询条件和导出功能返回值类型
2026-01-05 17:02:26 +08:00
chenhao 7db1b6ed37 Merge branch 'refs/heads/master' into dev_1.0.0 2026-01-04 12:15:05 +08:00
chenhao 4238f345cb feat(finance): 实现财务计收页面表格列动态显示和数据关联查询
- 在charge页面添加columns配置对象,支持表格列的动态显示和隐藏
- 集成right-toolbar组件的columns属性,实现列显隐控制功能
- 为表格列添加v-if条件渲染和key属性,优化列显示控制
- 在订单通路列添加标签显示,区分总代和直签类型
- 在实体类OmsFinanceCharge中添加项目、供应商、合作伙伴等关联字段
- 使用lombok注解配置getter/setter访问级别,避免自动生成方法冲突
- 实现毛利、毛利率、成本合计等计算字段的业务逻辑
- 更新数据访问层,通过多表关联查询获取项目和合作伙伴信息
- 优化SQL查询语句,添加表别名并完善查询条件的字段引用
- 增加项目编码、项目名称、合作伙伴名称等查询条件支持
2026-01-04 10:36:43 +08:00
chenhao caec1d4bd0 feat(finance): 新增财务计收模块并完善相关功能
- 新增财务计收API接口文件,包含查询、新增、修改、删除等操作
- 创建财务计收页面组件,实现列表展示、搜索、导出等功能
- 配置财务计收路由,添加菜单权限控制
- 在订单页面添加申请计收按钮,实现订单与计收关联
- 完善库存发货服务,在发货完成时自动创建计收数据
- 新增财务计收相关实体类、服务接口和数据访问层
- 添加库存信息按订单编号查询功能
- 修复内部库存页面数量验证逻辑问题
- 优化发货表单中序列号列表加载时机
2025-12-31 17:08:12 +08:00
chenhao 695c77ca60 feat(invoice): 添加红冲功能和相关审批流程
- 修改ApplyInvoice组件支持红冲操作,添加isRedRush属性控制红冲逻辑
- 在输入验证中修复数字验证正则表达式,支持负数输入
- 添加applyRefund API接口用于提交红冲申请
- 实现红冲审批流程,包括待审批和已审批页面
- 更新发票详情显示组件支持负数金额显示
- 在发票列表页面添加红冲按钮和相关状态判断
- 修改后端applyRefund接口接收完整对象参数
- 实现发票明细项导出功能
- 添加红冲状态和发票类型的字典数据支持
- 优化金额转中文大写函数支持负数显示
- 添加发票详情导出PDF功能
2025-12-30 15:20:25 +08:00
chenhao f0294212b2 feat(finance): 实现附件批量上传和多文件预览功能
- 修改附件字段从单个attachment为attachments数组
- 实现多文件上传功能支持同时上传多个附件
- 添加全局文件预览组件支持PDF和图片预览
- 更新下载功能支持批量下载多个附件
- 重构发票对话框中的附件展示和下载逻辑
- 修改后端服务接口支持多文件上传处理
- 更新数据库查询逻辑适配多附件关联关系
2025-12-29 20:54:57 +08:00
chenhao f992b2b29e feat(finance): 更新开票申请功能和审批流程
- 修改ApplyInvoice.vue中的表单字段映射,将projectName改为productName
- 为表格输入框添加禁用状态,当存在产品编码时不可编辑
- 更新表格列配置,将"产品型号"改为"规格型号",新增"单位"列
- 添加数值输入验证,限制数量字段只能输入数字和小数点
- 修正发票类型默认值从"-"改为"2"
- 将detailList改为detailItemList以保持数据结构一致性
- 在handleSubmit方法中添加表格明细校验逻辑
- 为发票详情页面添加PDF导出功能
- 创建新的开票审批历史页面receivableInvoice/approved/index.vue
- 创建新的开票审批页面receivableInvoice/index.vue,包含审批流程
- 更新发票列表页面的字段标签和查询条件
- 调整日期选择器格式为包含时间的datetimerange
- 隐藏实际收票时间筛选条件并调整列显示
- 更新按钮显示条件,仅在审批状态为0时显示申请开票
- 在InventoryOuter实体类中添加receivableBillCode字段
- 重构库存出库Mapper的查询语句,添加应收单编号关联查询
2025-12-29 20:21:28 +08:00
chenhao 5583bb0f11 feat(finance): 更新收款和开票审批功能
- 修改代理商为进货商的标签和提示文本
- 优化收款单表格列显示,添加预期收款金额和比例
- 实现预收和非预收单据的类型切换功能
- 添加应收单和订单表格的数据加载状态指示
- 更新应用付款对话框中的支付方式相关文本
- 增加收款金额与确认金额的一致性验证
- 完善应收单详情页面的字段展示和布局
- 添加发票详情类型字典配置
- 实现收款审批流程的审批历史和审批功能
- 创建收款单和退款单的审批相关路由
- 新增流程关联数据传输对象和相关配置
2025-12-29 15:31:27 +08:00
chenhao 47879d029a Merge branch 'refs/heads/master' into dev_1.0.0 2025-12-29 09:24:06 +08:00
chenhao 5ead2c13b2 feat(invoice): 实现发票申请功能优化
- 将销售方名称改为下拉选择框,支持公司信息自动填充
- 新增公司信息管理模块,包含增删改查功能
- 集成公司信息API接口,支持公司列表查询
- 实现销售方银行信息自动填充功能
- 优化发票申请表单,支持银行信息自动带入
- 更新发票状态筛选,使用字典数据动态渲染选项
- 添加公司信息详情查看功能
- 实现发票备注信息自动生成,包含购销双方银行信息
2025-12-26 10:28:52 +08:00
chenhao 389deac133 feat(finance): 添加提交日期筛选功能和金额汇总功能
- 在收票、红冲、付款等审批页面添加提交日期范围筛选功能
- 在财务页面表格中添加金额汇总显示功能
- 修复收票单详情页面字段显示问题
- 优化数据库查询时间范围处理逻辑
- 添加附件金额相关字段支持
2025-12-25 17:57:27 +08:00
chenhao b21c54a901 fix(finance): 优化付款和收票单据表单及列表功能
- 移除预计付款时间字段并调整表单布局
- 将是否预付改为复选框并更新相关字段名称
- 更新表格列标题为更准确的业务术语
- 添加表格合计功能和金额格式化显示
- 优化日期选择器类型和样式
- 修复收票单时间字段验证和显示问题
- 调整合并付款和收票单验证逻辑
- 添加付款日期预警颜色标识
- 优化金额字段显示单位和格式
- 修复多个表单和列表组件的字段映射问题
- 添加表格摘要统计功能
- 优化查询表单字段标签和宽度
- 调整按钮文字和操作提示信息
2025-12-25 15:33:23 +08:00
chenhao e767c6aa50 Merge branch 'refs/heads/master' into dev_1.0.0 2025-12-25 14:58:47 +08:00
chenhao 8ea80a4dbe feat(finance): 实现发票核销功能
- 新增发票核销API接口文件,包含列表查询、详情获取和删除功能
- 添加应收票据核销单服务接口和实现类
- 创建应收票据核销单数据实体和数据库映射
- 实现发票核销相关的数据库操作和业务逻辑
- 添加核销详情处理和批量更新功能
- 实现系统自动收票核销机制
- 更新发票单据服务以支持核销操作
- 添加核销记录的增删改查控制器接口
- 实现核销单据的查询和导出功能
2025-12-24 16:35:26 +08:00
chenhao fb3f3c721e feat(finance): 添加收款单手工核销功能
- 实现收款单手工匹配核销界面,支持应收单与收款单关联
- 新增应收单和收款单过滤查询功能,支持按进货商和单据编号筛选
- 添加核销金额计算逻辑,包含含税和未税金额转换
- 实现收款计划选择功能,支持批量核销操作
- 添加核销金额校验机制,确保应收与收款金额平衡
- 新增收款单核销状态管理和金额更新功能
- 实现核销详情批量处理和数据同步机制
2025-12-24 14:48:30 +08:00
chenhao d3bbf2da62 feat(finance): 更新开票单详情和操作功能
- 修改详情抽屉中的字段显示,将创建时间改为预计开票时间
- 添加开票状态、审批节点、审批状态和审批通过时间显示
- 更新销售-应收单表格的数据源和字段映射
- 扩展操作列宽度以适应更多按钮
- 添加发票按钮的显示条件控制
- 添加申请开票按钮的显示条件控制
- 添加申请红冲按钮的显示条件控制
- 实现撤销功能按钮和相关逻辑
- 添加撤销发票API接口
- 优化票据类型选择的交互逻辑
- 添加发票金额验证和自动计算功能
- 实现附件上传的权限控制
- 完善发票撤销的业务逻辑处理
2025-12-23 19:52:57 +08:00
chenhao 693899f45c Merge branch 'refs/heads/master' into dev_1.0.0 2025-12-23 16:00:55 +08:00
chenhao fe2791f815 feat(finance): 新增申请开票功能模块
- 添加申请开票Vue组件,包含发票信息录入与提交逻辑
- 更新开票单详情抽屉展示字段及标签分类
- 扩展后端接口支持根据开票单号查询产品明细
- 新增申请开票API接口及前端调用服务
- 完善开票相关字典类型与状态显示
- 优化开票列表页字段展示与操作按钮布局
- 增加项目ID字段以支持更精确的产品关联
- 提供数字转中文大写金额转换功能
- 实现发票明细表格动态增删行与自动计算
- 引入电子发票类型选择与样式美化
2025-12-22 20:44:38 +08:00
chenhao 54e7d394bb feat(invoice): 新增收票单管理功能
- 新增收票单列表页面,支持查询、新增、查看详情等操作
- 实现收票单新增表单,支持选择客户、关联应收单及收票计划
- 开发收票单详情抽屉,展示收票单基本信息及关联应收单明细
- 添加收票附件上传对话框,支持上传并查看收票相关附件
- 在应收单编辑页面增加开票明细展示及附件预览功能
- 更新审批订单页面布局,移除冗余工具栏
- 优化文件预览组件集成,提升附件查看体验
2025-12-22 15:59:31 +08:00
chenhao 6ec773ce3d Merge branch 'refs/heads/master' into dev_1.0.0 2025-12-22 14:39:26 +08:00
chenhao 0774e81fd0 fix(finance): 修复收款明细显示及字典配置问题
- 修改对方支付方式显示逻辑,使用dict-tag组件展示
- 补充收款相关字典类型配置
- 移除Java服务类中无用的PolicyUtils导入
2025-12-22 09:46:26 +08:00
chenhao 1111d285c5 feat(finance): 新增收款申请付款与退款功能
- 新增申请付款对话框组件,支持文件上传与预览
- 新增申请退款对话框组件,用于处理退款请求
- 修改收款详情抽屉,增加附件预览与下载功能
- 优化付款单编辑表单,统一文件预览组件
- 在收款管理页面添加申请付款与退款操作按钮
- 后端新增收款申请、退款及附件上传接口
- 数据库映射文件更新,增加原始账单ID字段
- 实现收款单据服务层逻辑,包括退款状态管理
- 增加全局文件预览组件引用与相关方法
- 更新权限控制与业务流程处理逻辑
2025-12-19 16:40:17 +08:00
chenhao c257cdc5cd feat(finance): 新增收款单管理功能
- 新增收款单新增表单页面,支持预收和应收两种类型
- 实现收款单详情查看抽屉组件
- 添加收款单列表查询、筛选和分页功能
- 集成收款单新增、提交、退回和红冲操作
- 支持收款计划选择和金额计算逻辑
- 完善收款单据关联数据展示和处理
- 提供收款单附件管理和审批状态跟踪
- 增加代理商选择器和订单数据联动加载
- 实现收款单据类型和状态字典显示
- 添加收款单据验证规则和提交前检查
2025-12-19 13:44:22 +08:00
chenhao 1d85e92997 feat(finance): 调整付款单和收票单详情展示及附件删除功能
- 修改付款单详情抽屉中的字段展示顺序和内容
- 移除收票单列表中的预计收票时间和实际收票时间列
- 增加附件作废按钮并实现删除逻辑
- 更新附件删除接口以支持根据关联ID软删除
- 在付款单中增加审批节点信息传递
- 调整合并收票对话框中日期选择器布局并修改提示文字
- 优化付款单状态更新逻辑,在删除附件后重置支付状态和时间
2025-12-18 17:57:59 +08:00
chenhao e7ed169f94 feat(finance): 添加采购应付收票核销功能及相关优化
- 在 EditForm.vue 中为回执单和发票图添加预览功能,支持 PDF 和图片格式
- 增加 PDF 预览对话框和图片预览对话框组件
- 优化附件显示逻辑,当无附件时展示 '-' 占位符
- 禁止订单审批弹窗点击遮罩关闭,防止误操作
- 调整应付单列表项目名称列宽及操作列宽度,提升界面布局合理性
- 修改生成收票单按钮显示条件,确保仅在未收票金额大于 0 时可见
- 移除付款单中的删除按钮,替换为生成收票单按钮
- 放宽发起付款按钮的显示限制,不再限定付款单类型
- 新增 IOmsPayableTicketWriteOffService 接口及其实现类,支持收票核销业务
- 添加 OmsPayableTicketWriteOff 实体类及相关 Mapper、Controller 层代码
- 实现用户手动收票核销与系统自动收票核销逻辑
- 为 MergePaymentDialog 添加禁止点击遮罩关闭属性,增强用户体验
- 在 IOmsPayablePaymentDetailService 和 IOmsPayableTicketDetailService 中新增批量更新方法
- 补充 MyBatis XML 映射文件中的 updateBatch SQL 语句
- 完善收票核销单据编号生成规则,确保唯一性和可读性
- 增强核销删除功能,实现关联数据的反核销处理和金额恢复逻辑
2025-12-18 17:21:27 +08:00
chenhao 9be3b62aaf feat(finance): 应付单详情页新增收票信息展示与预付金额计算
- 调整应付单详情页字段布局,新增项目编号、项目名称等显示项
- 新增“该制造商是否有预付单”和“预付金额”字段展示
- 新增“生成付款单”、“生成收票单”状态展示逻辑
- 新增采购收票单表格展示功能,包括发票类型、开票时间、收票状态等
- 新增收票计划和付款计划组件间的数据同步方法
- 扩展字典类型支持,增加收票相关枚举值
- 增加收票明细列表查询接口及附件信息加载逻辑
- Java后端增加预付金额计算逻辑,并在应付单中返回对应数据
- 优化支付明细和收票明细的状态统计逻辑,提升准确性
- 修改数据库映射文件,补充缺失字段如税率、不含税金额等
- 增加核销相关操作接口,支持批量删除和插入收票明细记录
- 修复部分SQL语句条件判断问题,确保查询结果准确无误
2025-12-18 16:13:40 +08:00
chenhao 6c3f344b92 feat(finance): 新增财务应收应付核销功能
- 新增应收单据查询、详情查看接口
- 新增收款计划与开票计划管理接口
- 新增核销记录列表与详情查询接口
- 新增付款核销详情抽屉组件
- 新增核销记录查询页面,支持多条件筛选
- 实现核销记录反核销功能
- 添加核销类型字典配置支持
2025-12-17 20:14:26 +08:00
chenhao ed90f7ce87 feat(sip): 实现应付核销功能及相关服务优化
- 新增自动核销与手动核销逻辑,支持通过付款单自动触发核销流程
- 完善核销单号生成规则,确保唯一性和可追溯性
- 增强付款单与应付单之间的关联处理,包括核销后的反向操作
- 添加附件删除接口,支持按支付单ID及类型批量逻辑删除
- 扩展应付明细查询能力,新增根据核销ID列表查询相关应付明细
- 优化付款单状态更新机制,在核销撤销时同步更新付款状态及附件信息
- 引入WriteOffDetailDTO用于封装核销详情数据,提升前后端交互效率
2025-12-17 20:13:49 +08:00
chenhao f79150abc9 feat(finance): 实现手工匹配核销功能
- 新增付款单与应付单的手工匹配核销界面
- 支持应付单和付款单的独立过滤查询
- 实现核销金额的实时计算与校验逻辑
- 添加应付计划选择弹窗及交互逻辑
- 集成税务相关金额的自动计算功能
- 提供核销明细数据的批量插入接口支持
- 优化表格展示与分页查询用户体验
2025-12-17 17:25:43 +08:00
chenhao 8d6ca0b64f feat(finance): 新增收款与核销功能模块
- 实现销售应收单详情查看与编辑功能
- 添加合并收款单与开票单发起流程
- 开发手动核销应付单与付款单功能
- 支持应付计划选择与核销金额计算
- 增加税务相关金额计算逻辑
2025-12-17 11:15:10 +08:00
chenhao 6613b3612c feat(approve): 实现审批单据PDF导出功能
- 引入通用PDF导出工具函数exportElementToPDF
- 在多个审批组件中添加PDF导出按钮和相关逻辑
- 优化导出时的样式显示,隐藏不必要的交互元素
- 统一处理PDF文件命名规则
- 移除原有的html2canvas和jsPDF直接调用代码
- 添加导出状态loading效果和异常处理提示
2025-12-16 16:35:53 +08:00
chenhao af12674d7b feat(finance): 新增收票与红冲发票审批功能
- 在路由中新增发票红冲和收票审批相关页面路径
- 新增红冲发票审批页面及详情展示组件
- 修改收票单据字段显示,统一使用 ticketBillCode 字段
- 调整收票和红冲发票的审批流程跳转逻辑
- 更新附件上传人字段为 createByName
- 后端区分不同类型票据启动不同的审批流程
- 优化前端接口请求增加 loading 状态控制
- 移除付款明细中的冗余附件获取逻辑
- 新增收票相关 API 接口文件并实现基本功能方法
2025-12-16 16:17:25 +08:00
chenhao 7c46ae5db4 feat(finance): 新增收票审批功能模块
- 新增收票待审批与已审批列表查询接口
- 实现收票审批与驳回流程处理逻辑
- 添加收票单详情查看及附件预览下载功能
- 配置收票审批相关路由与页面组件
- 完善收票单据明细展示与数据字典支持
- 优化附件查询SQL关联创建人姓名字段
- 修复流程定义中财务票据退款键名拼写错误
- 调整付款单据查询逻辑以适配指定流程KEY筛选
- 更新实体类增加审批相关字段支持动态查询
- 补充Vue页面样式与交互细节提升用户体验
2025-12-16 15:45:14 +08:00
chenhao 56d79e96b6 feat(finance): 新增财务票据审批功能
- 在配置文件中增加财务票据相关流程定义
- 实现票据退款审批流程的开启与处理逻辑
- 新增付款退款审批页面及详情组件
- 提供付款退款审批接口及业务方法
- 支持在审批流中动态设置流程变量并启动指定流程
- 优化审批节点信息填充方法以支持多流程场景
- 修复部分SQL查询条件限制提高灵活性
- 更新前端页面去除冗余字段展示提升用户体验
2025-12-16 14:47:15 +08:00
chenhao 995f7f8b03 feat(approve): 新增财务付款审批功能
- 添加付款审批列表页面,支持搜索和分页
- 实现付款单详情查看功能
- 集成审批流程操作,包括同意和驳回
- 添加审批意见输入和验证逻辑
- 展示付款单流转历史记录
- 创建付款详情组件,显示付款单和应付单信息
- 添加跳转至审批历史页面的功能按钮
2025-12-16 09:52:53 +08:00
chenhao 2cf2fdff08 feat(finance): 新增财务付款与退款审批功能
- 在配置文件中增加财务付款和退款的流程定义及实例Bean配置
- 扩展OmsPaymentBill实体类,新增审批相关字段如审批人、申请时间等
- 实现付款和退款的审批流程启动、撤销以及退款申请功能
- 增加前端页面按钮控制及对应API调用逻辑
- 添加付款审批列表和已审批列表查询接口
- 更新路由配置以支持新的财务付款审批页面访问路径
- 引入流程删除命令确保重复提交时旧流程实例被清理
- 完善付款单据审批状态更新及相关业务回调处理逻辑
- 优化审批流中的公司领导审批分支处理逻辑
- 提供付款单据回执单查看、发起付款/退款、退回及撤销操作界面交互支持
2025-12-16 09:43:34 +08:00
chenhao dc1f5f7302 feat(finance): 重构收票单新增功能并完善相关逻辑
- 新增收票计划选择器组件及交互逻辑
- 优化收票单表单结构,支持从应付单合并生成收票单
- 增加收票计划金额和比例计算功能
- 完善收票单提交校验逻辑,确保数据完整性
- 更新收票单详情展示字段,修正显示错误
- 调整收票单列表操作按钮权限控制
- 扩展后端服务接口,支持根据收票单编号查询明细
- 优化收票单审批状态管理及相关业务逻辑处理
2025-12-15 17:12:08 +08:00
chenhao 49cd27c221 feat(finance): 优化付款单新增功能及界面交互
- 移除预付单类型切换时的冗余处理逻辑
- 修复付款状态字典引用错误,统一使用 dict.type.payment_status
- 预付订单列表增加选择控制,限制只能选中一个订单
- 表格组件添加 ref 引用以便手动清除选中状态
- 新增预计付款时间必填校验规则
- 提交数据结构调整,明确区分应付单与预付单字段
- 增加含税总价大于0的前置校验
- 日志提示优化,增强用户操作引导信息
- 接口调用分离,针对不同付款类型调用对应后端接口
- 回执单组件默认值调整,避免空对象异常
2025-12-15 14:59:27 +08:00
chenhao a4a93ee484 feat(finance): 更新付款单表单逻辑与数据源
- 修改付款单类型选项,将 NORMAL 替换为 FROM_PAYABLE
- 调整应付单列表显示条件,适配新的付款单类型
- 更新订单列表标题及字段展示内容
- 修改默认付款单类型为 FROM_PAYABLE
- 优化订单列表接口调用方式,使用 POST 请求并设置 Content-Type
- 增加供应商编码查询条件,支持通过项目产品信息过滤订单
- 调整提交校验逻辑,确保选择应付单时进行有效性检查
2025-12-15 11:03:16 +08:00
chenhao c940880a9d feat(finance): 重构付款单新增功能并支持预付与应付单合并付款
- 重写 AddForm.vue 组件以支持预付和正常付款两种模式
- 新增付款计划选择器组件用于应付单付款计划管理
- 修改后端接口 URL 并增加查询应付单及采购订单的 API 方法
- 扩展付款明细服务以支持通过付款单编码查询数据
- 更新付款单退回逻辑以正确处理关联应付单金额更新
- 移除旧有的付款单应付明细查询方法及相关 XML 配置
2025-12-15 10:23:57 +08:00
chenhao 3e45254fc1 feat(finance): 优化应付单付款与收票逻辑
- 修改计算工具函数,确保数值运算精度
- 更新应付单页面字段映射,统一命名规范
- 增加税率输入框并同步到库存信息
- 调整应付单合并支付与收票计划的处理方式
- 完善应付单实体类结构及数据库映射关系
- 优化采购订单关联应付单的生成逻辑
- 引入厂家开票时间字段以完善票据管理流程
2025-12-15 09:27:08 +08:00
chenhao a19909d1bf Merge branch 'refs/heads/master' into dev_1.0.0 2025-12-12 11:00:27 +08:00
chenhao 2f51b56298 feat(finance): 新增付款计划同步至发票计划功能
- 在后端服务中实现 syncPaymentToTicketPlan 方法,用于将付款计划同步到发票计划
- 添加对应的 REST 接口 /sync/{payableBillId} 支持前端调用
- 前端页面新增“同步至发票计划”按钮,并绑定相应处理逻辑
- 提供 API 函数 syncToTicketPlan 以支持前后端通信
- 优化计算公式显示,确保数值精度和展示正确性
- 增强数据一致性校验,防止已执行的数据不匹配导致错误同步
2025-12-11 20:47:05 +08:00
chenhao 4cc2d3beb1 feat(finance): 新增收票单管理功能
- 新增收票单新增、详情查看、附件上传、退回及红冲申请功能
- 实现收票单与应付单关联展示及合并收票逻辑
- 提供收票计划选择与金额计算功能
- 支持收票状态、审批状态等多维度查询过滤
- 集成字典标签展示收票类型、审批状态等相关字段
- 添加收票单据的增删改查接口支持
2025-12-11 20:06:23 +08:00
chenhao ed9fda7fe3 feat(finance): 优化回执单上传与展示功能
- 根据支付类型动态显示"回执单"或"退款图"标题
- 重构上传流程,新增独立的上传对话框
- 支持PDF文件的预览功能,可点击查看大图
- 新增上传表单验证,包括文件类型、大小及价格确认
- 改进UI布局,提升用户体验和视觉效果
- 添加PDF预览弹窗,支持更好的文档查看体验
- 完善文件上传逻辑,确保数据完整性和一致性
2025-12-11 14:45:46 +08:00
chenhao bd830115d4 feat(finance): 添加合并收票单功能并优化付款单逻辑
- 在应付单页面增加“合并并发起收票单”按钮及弹窗组件
- 新增收票单相关接口与服务实现,支持合并多个应付单发起收票
- 优化付款单含税金额与无税金额的计算逻辑
- 补充付款明细查询接口,支持通过付款单编号获取关联应付单信息
- 调整部分字段命名以提升语义清晰度(如 paymentRatio 改为 paymentRate)
- 更新收票计划相关接口路径,统一归入 /finance/ticket/plan 路由下
2025-12-11 11:02:11 +08:00
chenhao a9142c0e1b feat(finance): 添加付款退款功能并优化应付单界面
- 在付款管理页面增加退款按钮及申请退款功能
- 新增退款状态枚举及相关的业务逻辑处理
- 优化应付单列表展示,移除部分冗余字段显示
- 调整付款明细服务,支持批量插入和退款明细生成
- 修改付款计划查询逻辑,增强数据准确性
- 完善退款接口,支持通过ID申请退款操作
2025-12-10 18:46:24 +08:00
chenhao d3c4776bab feat(finance): 增强付款单功能及附件管理
- 将付款时间相关字段格式从日期调整为日期时间格式
- 在详情页和列表页使用dict-tag展示支付方式、付款状态和审批状态
- 新增回执单下载功能和相关UI组件
- 增加收票计划Tab页支持
- 添加发起付款和回执单弹窗界面及相关逻辑
- 实现附件上传与管理服务,包括财务附件实体和接口定义
- 更新付款单和应付明细实体结构以支持附件关联
- 优化前端组件导入和数据处理逻辑
- 修复并增强多个API接口功能,如applyPayment、uploadReceipt等
- 补充Mapper XML配置和Service实现逻辑
- 调整后端实体类属性以匹配新的业务需求
- 增加对附件信息的查询与绑定处理逻辑
- 引入新的枚举类型用于区分不同类型的关联单据
- 前端增加对新接口调用的支持以及表单交互优化
2025-12-09 20:51:15 +08:00
chenhao a1ea52a934 feat(finance): 新增付款单功能并优化应付单详情展示
- 新增付款单新增表单组件,支持付款单类型、制造商、付款时间等字段录入
- 优化应付单编辑表单,将弹窗改为抽屉展示,增强用户体验
- 应付单详情页增加付款明细表格展示及统计功能
- 调整付款单生成逻辑,支持从应付单直接发起付款操作
- 完善应付单状态显示,明确展示未生成、部分生成、全部生成状态
- 后端增加应付单查询接口及付款明细关联查询功能
- 优化付款单退回按钮显示条件,仅在审批通过状态下可操作
- 重构合并付款弹窗,调整默认付款单类型及展示逻辑
2025-12-09 16:35:45 +08:00
chenhao 05865dc2ef feat(finance): 新增付款单管理功能
- 新增付款单详情抽屉组件,展示付款单及应付单详细信息
- 新增付款单列表页面,支持多条件筛选和分页查询
- 新增财务管理路由模块,包含付款单子菜单
- 优化应付单日期格式化逻辑,统一使用HH:mm:ss格式
- 实现合并付款单并发起支付功能,支持批量处理应付单
- 新增付款明细实体类及相关Mapper、Service实现
- 完善应付单查询条件,增加预计付款时间范围查询
- 调整付款计划相关类包结构,统一移至sip.domain包下
2025-12-09 09:11:32 +08:00
chenhao 30d551fe59 Merge branch 'refs/heads/master' into dev_1.0.0 2025-12-09 09:10:37 +08:00
chenhao 333b5e1a8e fix(finance): 优化付款计划选择逻辑并修正计算方式
- 移除重复的付款计划验证逻辑
- 修正订单当前付款比例的计算方法,使用含税总价替代未付金额
- 新增 handleChooseConfirm 方法处理付款计划的选择确认逻辑
- 在 PaymentPlan 组件中支持传入选中的付款计划,并初始化表格选中状态
- 更新 MergePaymentDialog 中对 PaymentPlanSelector 组件的引用及事件绑定
- 调整付款比例计算条件,允许未付金额为零的情况
2025-12-05 16:35:11 +08:00
chenhao 94348aebc4 feat(finance): 新增采购应付单管理功能
- 新增采购应付单列表页面,支持多条件筛选和分页查询
- 实现应付单详情弹窗,展示应付单基本信息和状态
- 添加合并付款单功能,支持多应付单合并发起付款
- 实现付款计划选择器,可为每个应付单配置付款计划
- 新增应付单修改和删除功能
- 添加数据字典支持,用于展示产品类型、付款状态等枚举值
- 实现应付单导出功能
- 新增后端付款计划相关接口和实体类
- 添加付款计划Mapper接口及实现
- 实现付款计划服务层接口及业务逻辑
2025-12-05 14:18:54 +08:00
chenhao 1d842d3bf2 Merge branch 'refs/heads/master' into dev_1.0.0 2025-12-05 14:09:36 +08:00
chenhao 1bdc88cd7c Merge branch 'refs/heads/master' into dev_1.0.0
# Conflicts:
#	oms_web/oms_vue/src/utils/ruoyi.js
2025-12-04 17:46:58 +08:00
chenhao 1a4f64865a feat(sip): 新增库存出库价同步及应付单生成功能
- 新增 InventoryInfo.listByDeliveryId 方法以支持按出库单查询库存信息
- 在 InventoryDeliveryServiceImpl 中实现保存出库信息时同步更新出库价
- 实现出库时自动生成应付单逻辑,包括税率配置读取与金额计算
- 增加 selectOutPriceByCode 查询用于获取项目产品的价格信息
- 扩展 VendorInfo 支持付款类型枚举,区分入库付款与出库付款
- 修改 OmsPayableBill 实体类,增加项目编码、名称等字段并优化结构
- 优化应付单 Controller 接口,支持更灵活的查询条件
- 重构应付单 Mapper XML 文件,增强关联查询与筛选功能
- 在应付单 Service 层新增事务控制与默认付款计划创建逻辑
- 移除前端精确货币舍入工具函数,改用 decimal.js 进行高精度运算
- 注册全局计算工具至 Vue 实例,替换原有金额计算方式提升准确性
2025-12-04 10:53:27 +08:00
604 changed files with 92583 additions and 2526 deletions

139
docx/prompt.md 100644
View File

@ -0,0 +1,139 @@
你是一个资深 Java 后端工程师,请在当前 Spring BootJava8项目中实现一个完整的 MCP Server并加入“AI 自动选 ToolRAG + 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 streamingwrite + 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 APIOpenAI / 本地模型)
3. 支持多 MCP Server未来扩展
# 十、输出要求
1. 输出完整代码:
* McpController
* McpService
* McpToolRegistry
* ToolRetriever
* ToolRouter
* ToolEmbedding
* ToolInitializer
* McpRequest
2. 代码必须:
* 可运行
* 无伪代码
* 包含必要注释
* 代码生成目录在ruoyi-sip/src/main/java/com/ruoyi/sip/llm
3. 不要解释,只输出代码

View File

@ -6,3 +6,4 @@ ENV = 'production'
# 若依管理系统/生产环境
VUE_APP_BASE_API = '/prod-api'
VUE_APP_SERVICE_HOST = 'wb.oms.unisspace.com'

View File

@ -28,18 +28,20 @@
"axios": "0.28.1",
"clipboard": "2.0.8",
"core-js": "3.37.1",
"decimal.js": "10.4.2",
"echarts": "5.4.0",
"element-ui": "2.15.14",
"file-saver": "2.0.5",
"fuse.js": "6.4.3",
"highlight.js": "9.18.5",
"html2canvas": "^1.4.1",
"js-beautify": "1.13.0",
"js-cookie": "3.0.1",
"jsencrypt": "3.0.0-rc.1",
"jspdf": "^2.5.1",
"jszip": "^3.10.1",
"nprogress": "0.2.0",
"quill": "2.0.2",
"html2canvas": "^1.4.1",
"jspdf": "^2.5.1",
"screenfull": "5.0.2",
"sortablejs": "1.10.2",
"splitpanes": "2.4.1",
@ -48,8 +50,7 @@
"vue-cropper": "0.5.5",
"vue-router": "3.4.9",
"vuedraggable": "2.24.3",
"vuex": "3.6.0",
"decimal.js": "10.4.2"
"vuex": "3.6.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "4.4.6",

View File

@ -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
})
}

View File

@ -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'
})
}

View File

@ -1,4 +1,5 @@
import request from '@/utils/request'
import { tansParams } from '@/utils/ruoyi'
// 查询制造商信息列表
export function listVendor(query) {
@ -48,7 +49,9 @@ export function exportVendor(query) {
return request({
url: '/system/vendor/export',
method: 'post',
params: query
data: query,
transformRequest: [(params) => tansParams(params)],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
}

View File

@ -0,0 +1,10 @@
import request from '@/utils/request'
// 按SN码查询库存数据
export function listInventoryInfoByProductSn(query) {
return request({
url: '/sip/dataProcess/inventoryInfoByProductSn',
method: 'get',
params: query
})
}

View File

@ -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
})
}

View File

@ -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
})
}

View File

@ -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'
})
}

View File

@ -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
})
}

View File

@ -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'
})
}

View File

@ -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'
})
}

View File

@ -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
})
}

View File

@ -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'
})
}

View File

@ -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'
})
}

View File

@ -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'
})
}

View File

@ -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'
})
}

View File

@ -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
})
}

View File

@ -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
})
}

View File

@ -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'
})
}

View File

@ -17,6 +17,14 @@ export function listCompletedFlows(data) {
data: data
})
}
// 按业务单号查询待办记录(查看申请详情用)
export function listApplyFlows(data) {
return request({
url: '/flow/todo/apply-list',
method: 'post',
data: data
})
}
// 通用审批
export function approveTask(data) {
return request({

View File

@ -52,6 +52,15 @@ export function recallDelivery(id) {
})
}
// 提交出库撤回审批(跨天发货记录)
export function recallApply(id, reason, amountChanged) {
return request({
url: '/inventory/delivery/vue/recall/apply',
method: 'post',
data: {id, reason, amountChanged}
})
}
// 导出采购合同
export function exportDelivery(query) {
return request({

View File

@ -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) {
return request({

View File

@ -85,3 +85,30 @@ export function getOuterLog(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
})
}

View File

@ -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) {
return request({

View File

@ -79,6 +79,14 @@ export function exportDownloadTemplate() {
method: 'get'
})
}
// 下载带列表数据的发货导入模板
export function exportDownloadTemplateData(data) {
return request({
url: '/inventory/outer/importTemplateData',
method: 'post',
data: data
})
}
export function importSnData(formData) {
return request({
url: '/inventory/outer/vue/importData',
@ -89,3 +97,13 @@ export function importSnData(formData) {
data: formData
})
}
export function assignDeliverySnData(formData) {
return request({
url: '/inventory/outer/vue/assignDeliveryData',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data: formData
})
}

View File

@ -1,5 +1,11 @@
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 格式
export function login(username, password, code, rememberMe) {
const formData = new FormData()
@ -9,6 +15,40 @@ export function login(username, password, code, rememberMe) {
formData.append('rememberMe', rememberMe || false)
return request({
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: {
isToken: false,
'Content-Type': 'multipart/form-data',

View File

@ -49,7 +49,7 @@ export function delOrder(id) {
export function exportOrder(query) {
return request({
url: `${baseURL}/export`,
method: 'post',
method: 'get',
params: query
})
}

View File

@ -1,28 +1,40 @@
import request from '@/utils/request'
// 查询产品信息
export function getProductInfo(serialNumber) {
function normalizeSerialNumbers(input) {
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({
url: '/manage/service/product',
method: 'get',
params: { serialNumber }
method: 'post',
data: buildQueryPayload(serialNumberOrNumbers)
})
}
// 查询相关合同信息
export function getOrderInfo(serialNumber) {
export function getOrderInfo(serialNumberOrNumbers) {
return request({
url: '/manage/service/order',
method: 'get',
params: { serialNumber }
method: 'post',
data: buildQueryPayload(serialNumberOrNumbers)
})
}
// 查询标准保修信息
export function getWarrantyInfo(serialNumber) {
export function getWarrantyInfo(serialNumberOrNumbers) {
return request({
url: '/manage/service/query',
method: 'get',
params: { serialNumber }
method: 'post',
data: buildQueryPayload(serialNumberOrNumbers)
})
}

View File

@ -1,11 +1,14 @@
import request from '@/utils/request'
import { tansParams } from '@/utils/ruoyi'
// 查询项目管理列表
export function listProject(query) {
return request({
url: '/sip/project/vue/list',
method: 'get',
params: query
method: 'post',
data: query,
headers: { 'Content-Type': 'multipart/form-data' },
})
}
@ -49,6 +52,34 @@ export function exportProject(query) {
return request({
url: '/sip/project/vue/export',
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
})
}

View File

@ -85,3 +85,48 @@ export function updateFinanceStatus(id, status) {
data: data
})
}
// 查询产品配货备货明细
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 }
})
}

View File

@ -106,6 +106,16 @@ export function recallPurchaseorder(id) {
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) {

View File

@ -1,4 +1,5 @@
import request from '@/utils/request'
import { tansParams } from '@/utils/ruoyi'
// 查询办事处信息列表
export function listAgent(query) {
@ -48,6 +49,8 @@ export function exportAgent(query) {
return request({
url: '/system/agent/vue/export',
method: 'post',
params: query
data: query,
transformRequest: [(params) => tansParams(params)],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
}

View File

@ -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'
})
}

View File

@ -1,4 +1,5 @@
import request from '@/utils/request'
import { tansParams } from '@/utils/ruoyi'
// 查询客户信息列表
export function listCustomer(query) {
@ -48,6 +49,8 @@ export function exportCustomer(query) {
return request({
url: '/system/customer/vue/export',
method: 'post',
params: query
data: query,
transformRequest: [(params) => tansParams(params)],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
}

View File

@ -1,4 +1,5 @@
import request from '@/utils/request'
import { tansParams } from '@/utils/ruoyi'
// 查询产品管理列表
export function listProduct(query) {
@ -48,7 +49,9 @@ export function exportProduct(query) {
return request({
url: '/system/product/export',
method: 'post',
params: query
data: query,
transformRequest: [(params) => tansParams(params)],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
}

View File

@ -1,7 +1,7 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
// 查询用户列表
// 鏌ヨ鐢ㄦ埛鍒楄〃
export function listUser(query) {
return request({
url: '/system/user/list',
@ -10,7 +10,7 @@ export function listUser(query) {
})
}
// 查询用户详细
// 鏌ヨ鐢ㄦ埛璇︾粏
export function getUser(userId) {
return request({
url: '/system/user/' + parseStrEmpty(userId),
@ -18,7 +18,7 @@ export function getUser(userId) {
})
}
// 新增用户
// 鏂板鐢ㄦ埛
export function addUser(data) {
return request({
url: '/system/user/add',
@ -30,7 +30,7 @@ export function addUser(data) {
})
}
// 修改用户
// 淇敼鐢ㄦ埛
export function updateUser(data) {
return request({
url: '/system/user',
@ -39,7 +39,7 @@ export function updateUser(data) {
})
}
// 删除用户
// 鍒犻櫎鐢ㄦ埛
export function delUser(userId) {
return request({
url: '/system/user/' + userId,
@ -47,7 +47,7 @@ export function delUser(userId) {
})
}
// 用户密码重置
// 鐢ㄦ埛瀵嗙爜閲嶇疆
export function resetUserPwd(data) {
return request({
@ -60,7 +60,7 @@ export function resetUserPwd(data) {
})
}
// 用户状态修改
// 鐢ㄦ埛鐘舵€佷慨鏀?
export function changeUserStatus(userId, status) {
const data = {
userId,
@ -76,7 +76,7 @@ export function changeUserStatus(userId, status) {
})
}
// 查询用户个人信息
// 鏌ヨ鐢ㄦ埛涓汉淇℃伅
export function getUserProfile() {
return request({
url: '/system/user/profile/vue',
@ -84,7 +84,7 @@ export function getUserProfile() {
})
}
// 修改用户个人信息
// 淇敼鐢ㄦ埛涓汉淇℃伅
export function updateUserProfile(data) {
return request({
url: '/system/user/profile',
@ -93,7 +93,7 @@ export function updateUserProfile(data) {
})
}
// 用户密码重置
// 鐢ㄦ埛瀵嗙爜閲嶇疆
export function updateUserPwd(data) {
return request({
url: '/system/user/profile/resetPwd',
@ -103,7 +103,7 @@ export function updateUserPwd(data) {
})
}
// 用户头像上传
// 鐢ㄦ埛澶村儚涓婁紶
export function uploadAvatar(data) {
return request({
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) {
return request({
url: '/system/user/vue/authRole/' + userId,
@ -121,7 +134,7 @@ export function getAuthRole(userId) {
})
}
// 保存授权角色
// 淇濆瓨鎺堟潈瑙掕壊
export function updateAuthRole(data) {
return request({
url: '/system/user/authRole',
@ -130,7 +143,7 @@ export function updateAuthRole(data) {
})
}
// 查询部门下拉树结构
// 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋?
export function deptTreeSelect() {
return request({
url: '/system/user/deptTree',

View File

@ -1,4 +1,5 @@
import request from '@/utils/request'
import { tansParams } from '@/utils/ruoyi'
// 查询仓库基础信息列表
export function listInfo(query) {
@ -48,6 +49,8 @@ export function exportInfo(query) {
return request({
url: '/warehouse/info/export',
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

View File

@ -104,6 +104,18 @@
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;
}
}
/** 表单布局 **/

View File

@ -0,0 +1,64 @@
<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>
import request from '@/utils/request';
export default {
name: "GlobalFilePreview",
data() {
return {
pdfPreviewVisible: false,
currentPdfUrl: '',
imagePreviewVisible: false,
currentImageUrl: ''
};
},
methods: {
isPdf(filePath) {
return filePath && filePath.toLowerCase().endsWith('.pdf');
},
getImageUrl(resource) {
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
},
handlePreview(attachment) {
if (!attachment) return;
if (this.isPdf(attachment.filePath)) {
request({
url: '/common/download/resource',
method: 'get',
params: { resource: attachment.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(attachment.filePath);
this.imagePreviewVisible = true;
}
},
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>

View File

@ -11,7 +11,7 @@
<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-button size="mini" circle icon="el-icon-menu" />
<el-dropdown-menu slot="dropdown">
<el-dropdown-menu slot="dropdown" >
<!-- 全选/反选 按钮 -->
<el-dropdown-item>
<el-checkbox :indeterminate="isIndeterminate" v-model="isChecked" @change="toggleCheckAll"> </el-checkbox>
@ -183,4 +183,8 @@ export default {
background-color: #ccc;
margin: 3px auto;
}
.el-dropdown-menu{
max-height: 400px;
overflow-y: auto;
}
</style>

View File

@ -3,6 +3,7 @@
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper">
<el-menu
ref="sideMenu"
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
@ -11,6 +12,8 @@
:active-text-color="settings.theme"
:collapse-transition="false"
mode="vertical"
@open="handleMenuOpen"
@close="handleMenuClose"
>
<sidebar-item
v-for="(route, index) in sidebarRouters"
@ -24,6 +27,7 @@
</template>
<script>
import pathLib from 'path'
import { mapGetters, mapState } from "vuex"
import Logo from "./Logo"
import SidebarItem from "./SidebarItem"
@ -52,6 +56,55 @@ export default {
isCollapse() {
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>

View File

@ -21,6 +21,7 @@ import './permission' // permission control
import { getDicts } from "@/api/system/dict/data"
import { getConfigKey } from "@/api/system/config"
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"
import { formatCurrency } from "@/utils"
// 分页组件
import Pagination from "@/components/Pagination"
// 自定义表格工具组件
@ -42,6 +43,7 @@ import DictData from '@/components/DictData'
Vue.prototype.getDicts = getDicts
Vue.prototype.getConfigKey = getConfigKey
Vue.prototype.parseTime = parseTime
Vue.prototype.formatCurrency = formatCurrency
Vue.prototype.resetForm = resetForm
Vue.prototype.addDateRange = addDateRange
Vue.prototype.selectDictLabel = selectDictLabel

View File

@ -5,12 +5,16 @@ import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { isPathMatch } from '@/utils/validate'
import { isRelogin } from '@/utils/request'
import { isServiceHost } from '@/utils/serviceHost'
NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/register', '/manage/service','/system/vendor/query','/system/partner/query']
const isWhiteList = (path) => {
if (path === '/' && isServiceHost()) {
return true
}
return whiteList.some(pattern => isPathMatch(pattern, path))
}

View File

@ -9,13 +9,12 @@ const baseURL = process.env.VUE_APP_BASE_API
let downloadLoadingInstance
export default {
name(name, isDelete = true) {
download(name, isDelete = true) {
var url = baseURL + "/common/download?fileName=" + encodeURIComponent(name) + "&delete=" + isDelete
axios({
method: 'get',
url: url,
responseType: 'blob',
headers: { 'Authorization': 'Bearer ' + getToken() }
}).then((res) => {
const isBlob = blobValidate(res.data)
if (isBlob) {

View File

@ -1,5 +1,6 @@
import Vue from 'vue'
import Router from 'vue-router'
import { isServiceHost } from '@/utils/serviceHost'
Vue.use(Router)
@ -62,10 +63,10 @@ export const constantRoutes = [
hidden: true
},
{
path: '',
path: isServiceHost() ? '/admin-root' : '',
component: Layout,
redirect: 'index',
children: [
redirect: isServiceHost() ? undefined : 'index',
children: isServiceHost() ? [] : [
{
path: 'index',
component: () => import('@/views/index'),
@ -94,6 +95,46 @@ export const constantRoutes = [
component: () => import('@/views/approve/approved_order/index'),
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',
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',
component: Layout,

View File

@ -52,7 +52,7 @@ const user = {
const rememberMe = userInfo.rememberMe
return new Promise((resolve, reject) => {
login(username, password, code, rememberMe).then(res => {
resolve()
resolve(res)
}).catch(error => {
reject(error)
})
@ -60,17 +60,16 @@ const user = {
},
// 获取用户信息
GetInfo({ commit, state }) {
GetInfo({ commit }) {
return new Promise((resolve, reject) => {
getInfo().then(res => {
// 后端返回的数据在 res.data 中
const data = res.data || res
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)) {
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_PERMISSIONS', data.permissions)
} else {
@ -82,14 +81,12 @@ const user = {
commit('SET_NAME', user.loginName)
commit('SET_NICK_NAME', user.userName)
commit('SET_AVATAR', avatar)
/* 初始密码提示 */
if(data.isDefaultModifyPwd) {
if (data.isDefaultModifyPwd) {
MessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
}).catch(() => {})
}
/* 过期密码提示 */
if(!data.isDefaultModifyPwd && data.isPasswordExpired) {
if (!data.isDefaultModifyPwd && data.isPasswordExpired) {
MessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
}).catch(() => {})
@ -102,7 +99,7 @@ const user = {
},
// 退出系统 - 基于 Session 认证
LogOut({ commit, state }) {
LogOut({ commit }) {
return new Promise((resolve, reject) => {
logout().then(() => {
commit('SET_ROLES', [])

View File

@ -19,17 +19,17 @@ export function toFixed(value, 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) {
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) {
return D(a).times(b).toDecimalPlaces(dp).toNumber()
return D(a).times(D(b)).toDecimalPlaces(dp).toNumber()
}
// 除法

View File

@ -14,7 +14,10 @@ export function formatDate(cellValue) {
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
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 {string} option

View File

@ -110,6 +110,8 @@ service.interceptors.response.use(res => {
} else if (code === 601) {
Message({ message: msg, type: 'warning' })
return Promise.reject('error')
} else if (code === 301 && res.config.headers && res.config.headers.allowWarning) {
return res.data
} else if (code !== 200) {
Notification.error({ title: msg })
return Promise.reject('error')

View File

@ -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)
}

View File

@ -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 extendField3extendField1
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>

View File

@ -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
},
// idSN
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,
// /SNouter_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

View File

@ -49,6 +49,22 @@
@keyup.enter.native="handleQuery"
/>
</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-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>
@ -72,13 +88,18 @@
<el-table-column label="项目名称" align="center" prop="projectName" />
<el-table-column label="项目编号" align="center" prop="projectCode" />
<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="approveNode">
<template slot-scope="scope">
<span>{{ scope.row.approveNode || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="提交时间" align="center" prop="approveTime" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
@ -127,6 +148,7 @@
<script>
import { listOrder } from "@/api/approve/order/orderLog";
import ApproveDialog from '../order/Approve.vue';
import {formatCurrency} from "../../../utils";
export default {
name: "ApprovedOrder",
@ -153,10 +175,12 @@ export default {
orderCode: null,
projectName: null,
projectCode: null,
productCode: null,
productModel: null,
customerName: null,
dutyName: null,
approveNode: null,
orderByColumn:'t7.todo_approve_time',
orderByColumn:'t1.approveTime desc, t1.id',
isAsc: 'desc'
},
};
@ -165,6 +189,7 @@ export default {
this.getList();
},
methods: {
formatCurrency,
/** 查询订单列表 */
getList() {
this.loading = true;

View File

@ -0,0 +1,259 @@
function escapeHtml(value) {
return String(value == null ? "" : value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function formatCurrency(value) {
const num = Number(value);
if (Number.isNaN(num)) {
return "0.00";
}
return num.toFixed(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">${allPriceTotal.toFixed(2)}</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;">${taxTotal.toFixed(2)}</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);
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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"></el-table-column>
<el-table-column prop="paymentAmount" label="本次付款金额" align="center"></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>

View File

@ -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>

View File

@ -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;
});
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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"></el-table-column>
<el-table-column prop="receiptAmount" label="本次收款金额" align="center"></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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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' : ''">{{ 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' : ''">{{ 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>

View File

@ -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>

View File

@ -135,8 +135,7 @@
import { approveOrder,getOrder } from "@/api/approve/order";
import ConfigInfo from './ConfigInfo.vue';
import ApproveLayout from '@/views/approve/ApproveLayout.vue';
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
import OrderInfoDisplay from '@/views/project/order/components/OrderInfoDisplay.vue';
@ -468,65 +467,21 @@ export default {
// PDF
async exportPDF() {
this.pdfExporting = true;
const disabledElements = [];
try {
// ApproveLayoutDOM
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; // A4mm
const pageHeight = 297; // A4mm
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`;
// PDF
pdf.save(fileName);
// PDF
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
// disabled
disabledElements.forEach(el => {
el.disabled = true;
});
this.pdfExporting = false;
}
}

View File

@ -30,7 +30,7 @@
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
<td>{{ formatCurrency(product.price) }}</td>
<td>{{ selectedDiscountLabel }}</td>
<td>{{ formatCurrency(product.allPrice) }}</td>
<td>{{ formatCurrency(getDisplayAllPrice(product, selectedDiscount)) }}</td>
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
<td>
<el-input
@ -86,7 +86,7 @@
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
<td>{{ formatCurrency(product.price) }}</td>
<td>{{ selectedDiscountLabel }}</td>
<td>{{ formatCurrency(product.allPrice) }}</td>
<td>{{ formatCurrency(getDisplayAllPrice(product, selectedDiscount)) }}</td>
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
<td>
<el-input
@ -142,7 +142,7 @@
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
<td>{{ formatCurrency(product.price) }}</td>
<td>{{ selectedDiscountLabel }}</td>
<td>{{ formatCurrency(product.allPrice) }}</td>
<td>{{ formatCurrency(getDisplayAllPrice(product, selectedDiscount)) }}</td>
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
<td>
<el-input
@ -178,7 +178,7 @@
</el-col>
</el-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>
<el-select
v-model="selectedDiscount"
@ -195,7 +195,7 @@
</el-option>
</el-select>
</el-col>
<el-col :span="6">
<el-col :span="6" v-if="orderData.orderStatus==='1'">
<span class="summary-label"> 折后总价合计</span> <span
class="summary-value-right"> {{ formatCurrency(finalTotal) }}</span>
</el-col>
@ -278,7 +278,12 @@ export default {
return this.calculateTotal(this.order.maintenanceProjectProductInfoList, this.selectedDiscount);
},
grandTotal() {
if (this.orderData.orderStatus === '1'){
return this.softwareTotal + this.hardwareTotal + this.maintenanceTotal;
}else{
return this.finalTotal;
}
},
finalTotal() {
return this.softwareDiscountedTotal + this.hardwareDiscountedTotal + this.maintenanceDiscountedTotal;
@ -297,13 +302,20 @@ export default {
//
this.$emit('tax-rate-change', product);
},
getDiscountedAllPrice(product, discount) {
if (discount === 1) {
return product.allPrice;
getDisplayAllPrice(product, discount) {
const allPrice = product.allPrice || 0;
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 this.$calc.mul(roundedDiscountedUnitPrice , product.quantity);
return allPrice;
},
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) {
if (!productList) return 0;

View File

@ -49,15 +49,28 @@
@keyup.enter.native="handleQuery"
/>
</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-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">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
@ -75,7 +88,12 @@
<el-table-column label="项目名称" align="center" prop="projectName" />
<el-table-column label="项目编号" align="center" prop="projectCode" />
<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="approveNode">
<template slot-scope="scope">
@ -106,6 +124,7 @@
<el-dialog
title="订单审批"
:visible.sync="approveDialogVisible"
:close-on-click-modal="false"
custom-class="approve-dialog"
width="80%"
top="5vh"
@ -130,6 +149,7 @@
<script>
import { listOrder } from "@/api/approve/order";
import ApproveDialog from './Approve.vue';
import {formatCurrency} from "../../../utils";
export default {
name: "Order",
@ -156,9 +176,14 @@ export default {
orderCode: null,
projectName: null,
projectCode: null,
productCode: null,
productModel: null,
customerName: null,
dutyName: null,
approveNode: null,
orderStatus: '1',
orderByColumn: 't2.id',
isAsc: 'desc',
},
};
},
@ -166,6 +191,7 @@ export default {
this.getList();
},
methods: {
formatCurrency,
toApproved(){
this.$router.push({
path: '/approve/orderLog',

View File

@ -23,7 +23,22 @@
end-placeholder="结束日期"
></el-date-picker>
</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-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>
@ -142,12 +157,15 @@ export default {
buyerName: null,
vendorName: null,
ownerName: null,
productCode: null,
productModel: null,
approveStatus: '1',
params:{
applyTimeStart: null,
applyTimeEnd: null,
}
},
orderByColumn: 'applyTime',
isAsc: 'desc'
},
//
dateRangeApplyTime: [],

View File

@ -23,7 +23,22 @@
end-placeholder="结束日期"
></el-date-picker>
</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-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>
@ -134,12 +149,16 @@ export default {
purchaseNo: null,
buyerName: null,
vendorName: null,
productCode: null,
productModel: null,
ownerName: null,
approveStatus: '3', // 3
params:{
applyTimeStart: null,
applyTimeEnd: null,
}
},
orderByColumn: 'applyTime',
isAsc: 'desc'
},
//
dateRangeApplyTime: [],

View File

@ -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>

View File

@ -0,0 +1,602 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="报价单号" prop="quotationCode">
<el-input
v-model="queryParams.quotationCode"
placeholder="请输入报价单号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="报价单" prop="quotationName">
<el-input
v-model="queryParams.quotationName"
placeholder="请输入报价单名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="quotationStatus">
<el-input
v-model="queryParams.quotationStatus"
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="datetimerange"
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
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['sip:quotation:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['sip:quotation:update']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['sip:quotation:delete']"
>删除</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="quotationList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="报价单号" align="center" prop="quotationCode" />
<el-table-column label="报价单" align="center" prop="quotationName" />
<el-table-column label="项目编号" align="center" prop="projectCode" />
<!-- <el-table-column label="报价金额" align="center" prop="quotationAmount" />-->
<el-table-column label="报价金额(¥)" align="center" prop="discountAmount" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
<el-table-column label="状态" align="center" prop="quotationStatus" >
<template slot-scope="scope">
<dict-tag :options="dict.type.quotation_status" :value="scope.row.quotationStatus"/>
</template>
</el-table-column>
<el-table-column label="创建人" align="center" prop="createByName" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-view"
@click="handleDetail(scope.row)"
>详情</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['sip:quotation:update']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-document-copy"
@click="handleCopy(scope.row)"
v-hasPermi="['sip:quotation:add']"
>复制创建</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['sip:quotation:delete']"
>删除</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-download"
@click="handleExport(scope.row)"
v-hasPermi="['sip:quotation:export']"
>导出</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="title" :visible.sync="open" width="80%" append-to-body :close-on-click-modal="false" @close="cancel">
<el-steps :active="activeStep" simple finish-status="success" style="margin-bottom: 20px; cursor: pointer;">
<el-step icon="el-icon-caret-left" @click.native="handleStepClick(0)">
<template #title>
<i class="el-icon-caret-left"></i>
报价单列表
</template>
</el-step>
<el-step title="设置基本信息" @click.native="handleStepClick(1)"></el-step>
<el-step title="配置信息" @click.native="handleStepClick(2)"></el-step>
</el-steps>
<div v-if="form.quotationCode || catalogueTotalPrice > 0 || discountedTotalPrice > 0">
<el-divider></el-divider>
<el-row type="flex" justify="space-between" style="margin-bottom: 20px; font-size: 14px;">
<el-col :span="8">
<span v-if="form.quotationCode">
<span style="font-weight: bold;">报价单号</span>{{ form.quotationCode }}
</span>
</el-col>
<el-col :span="16" 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>
<el-divider></el-divider>
</div>
<el-form ref="form" :model="form" :rules="rules" label-width="100px" style="max-height: 50vh;overflow-y: auto;padding: 10px">
<!-- Step 1 Content: Basic Info -->
<div v-show="activeStep === 1">
<el-row>
<el-col :span="12">
<el-form-item label="报价单名称" prop="quotationName">
<el-input v-model="form.quotationName" placeholder="请输入报价单名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="币种" prop="amountType">
<el-select v-model="form.amountType" placeholder="请选择币种" style="width: 100%">
<el-option
v-for="dict in dict.type.currency_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="代表处" prop="agentCode">
<el-select v-model="form.agentCode" placeholder="请选择代表处" style="width: 100%">
<el-option
v-for="item in agentOptions"
:key="item.agentCode"
:label="item.agentName"
:value="item.agentCode"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户名称" prop="customerName">
<el-input v-model="form.customerName" placeholder="请输入客户名称" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="报价单备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</div>
<!-- Step 2 Content: Config Info -->
<div v-show="activeStep === 2">
<product-config :value="form" @input="updateProductConfig" />
</div>
</el-form>
<div slot="footer" class="dialog-footer">
<div v-if="activeStep === 1">
<el-button @click="cancel"> </el-button>
<el-button type="primary" @click="nextStep"></el-button>
</div>
<div v-if="activeStep === 2">
<el-button @click="prevStep"></el-button>
<el-button type="primary" @click="submitForm"> </el-button>
</div>
</div>
</el-dialog>
<quotation-detail ref="detail" :agent-options="agentOptions" />
</div>
</template>
<script>
import { listQuotation, getQuotation, delQuotation, addQuotation, updateQuotation,exportSingleQuotation } from "@/api/base/quotation";
import { listAgent } from "@/api/system/agent";
import ProductConfig from "@/views/project/info/ProductConfig";
import QuotationDetail from "./detail";
import {isEmpty} from "@/utils/validate";
export default {
name: "Quotation",
components: {
ProductConfig,
QuotationDetail
},
dicts: ['currency_type','quotation_status'],
data() {
return {
//
activeStep: 1,
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
quotationList: [],
//
dateRange: [],
//
agentOptions: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
quotationCode: null,
quotationName: null,
projectCode: null,
quotationStatus: null,
createTimeStart: null,
createTimeEnd: null,
isAsc : 'desc',
orderByColumn : 'createTime',
},
//
form: {
softwareProjectProductInfoList: [],
hardwareProjectProductInfoList: [],
maintenanceProjectProductInfoList: []
},
//
rules: {
quotationName: [
{ required: true, message: "报价单名称不能为空", trigger: "blur" }
],
amountType: [
{ required: true, message: "币种不能为空", trigger: "change" }
],
agentCode: [
{ required: true, message: "代表处不能为空", trigger: "change" }
],
customerName: [
{ required: true, message: "客户名称不能为空", trigger: "blur" }
],
}
};
},
computed: {
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;
}
},
created() {
this.getList();
this.getAgentList();
},
methods: {
/** 查询报价单列表 */
getList() {
this.loading = true;
if (null != this.dateRange && '' != this.dateRange) {
this.queryParams.createTimeStart = this.dateRange[0];
this.queryParams.createTimeEnd = this.dateRange[1];
}
listQuotation(this.queryParams).then(response => {
this.quotationList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleDetail(row) {
this.$refs.detail.open(row.id);
},
handleExport(row){
this.$modal.confirm('是否确认导出已审批的采购数据项?').then(() => {
return exportSingleQuotation(row.id);
}).then(response => {
this.$download.download( response.msg)
})
},
/** 查询代表处列表 */
getAgentList() {
listAgent().then(response => {
this.agentOptions = response.rows;
});
},
//
cancel() {
this.open = false;
this.reset();
this.activeStep = 1;
},
//
reset() {
this.form = {
id: null,
quotationCode: null,
quotationName: null,
projectCode: null,
quotationAmount: null,
discountAmount: null,
quotationStatus: null,
createBy: null,
createTime: null,
updateBy: null,
updateTime: null,
remark: null,
agentCode: null,
amountType: null,
customerName: null,
softwareProjectProductInfoList: [],
hardwareProjectProductInfoList: [],
maintenanceProjectProductInfoList: []
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.queryParams.createTimeStart = null;
this.queryParams.createTimeEnd = null;
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.activeStep = 1;
this.title = "添加报价单";
},
/** 复制创建 */
handleCopy(row) {
this.reset();
const id = row.id || this.ids
getQuotation(id).then(response => {
this.form = response.data;
// Reset ID and Code for new creation
this.form.id = null;
this.form.quotationCode = null;
this.form.quotationStatus = null;
this.form.createTime = null;
this.form.updateTime = null;
this.form.createBy = null;
this.form.updateBy = null;
// Ensure product lists are initialized if null
this.form.softwareProjectProductInfoList = this.form.softwareProjectProductInfoList || [];
this.form.hardwareProjectProductInfoList = this.form.hardwareProjectProductInfoList || [];
this.form.maintenanceProjectProductInfoList = this.form.maintenanceProjectProductInfoList || [];
// Clear IDs in configuration lists to ensure they are created as new records
const clearIds = (list) => {
if (list && list.length > 0) {
list.forEach(item => {
item.id = null;
item.quotationId = null;
});
}
};
clearIds(this.form.softwareProjectProductInfoList);
clearIds(this.form.hardwareProjectProductInfoList);
clearIds(this.form.maintenanceProjectProductInfoList);
this.open = true;
this.activeStep = 1;
this.title = "复制创建报价单";
});
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id || this.ids
getQuotation(id).then(response => {
this.form = response.data;
// Ensure product lists are initialized if null
this.form.softwareProjectProductInfoList = this.form.softwareProjectProductInfoList || [];
this.form.hardwareProjectProductInfoList = this.form.hardwareProjectProductInfoList || [];
this.form.maintenanceProjectProductInfoList = this.form.maintenanceProjectProductInfoList || [];
this.open = true;
this.activeStep = 1;
this.title = "修改报价单";
});
},
/** 处理步骤点击 */
handleStepClick(stepIndex) {
if (stepIndex === 0) {
this.cancel();
} else if (stepIndex === 1) {
this.activeStep = 1;
} else if (stepIndex === 2) {
// Validate step 1 before going to step 2
this.$refs["form"].validateField(['quotationName', 'amountType', 'agentCode', 'customerName'], (errorMessage) => {
if (!errorMessage) {
this.activeStep = 2;
}
});
}
},
/** 下一步 */
nextStep() {
// Step 1
this.$refs["form"].validateField(['quotationName', 'amountType', 'agentCode', 'customerName'], (errorMessage) => {
if (!errorMessage) {
this.activeStep = 2;
}
});
},
/** 上一步 */
prevStep() {
this.activeStep = 1;
},
updateProductConfig(data) {
this.form.softwareProjectProductInfoList = data.softwareProjectProductInfoList;
this.form.hardwareProjectProductInfoList = data.hardwareProjectProductInfoList;
this.form.maintenanceProjectProductInfoList = data.maintenanceProjectProductInfoList;
},
formatAmount(value) {
if (value === null || value === undefined) return '';
return Number(value).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
const checkProduct=(list)=>isEmpty(list) ||( !isEmpty(list) && list.every(item => item.productBomCode!==''))
if (!checkProduct(this.form.softwareProjectProductInfoList) || !checkProduct(this.form.hardwareProjectProductInfoList) || !checkProduct(this.form.maintenanceProjectProductInfoList)) {
this.$modal.msgError("请完善产品信息");
return;
}
this.form.quotationAmount = this.catalogueTotalPrice;
this.form.discountAmount = this.discountedTotalPrice;
if (this.form.id != null) {
updateQuotation(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addQuotation(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除报价单?').then(function() {
return delQuotation(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
}
}
};
</script>
<style scoped>
/* 只有第一步显示自定义图标,覆盖完成状态的对勾 */
::v-deep .el-step:first-child .el-step__icon-inner.is-status {
display: none;
}
::v-deep .el-step:first-child .el-step__icon-inner:not(.is-status) {
display: inline-block;
}
/* 当前激活步骤标题颜色鲜明 */
::v-deep .el-step__title.is-process {
color: #1890ff;
font-weight: 700;
}
/* 当前激活步骤图标颜色鲜明 */
::v-deep .el-step__head.is-process {
color: #1890ff;
border-color: #1890ff;
}
</style>

View File

@ -0,0 +1,126 @@
<template>
<el-dialog title="选择报价单" :visible.sync="visible" :close-on-click-modal="false" width="900px" append-to-body @close="handleClose">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" @submit.native.prevent>
<el-form-item label="报价单号" prop="quotationCode">
<el-input
v-model="queryParams.quotationCode"
placeholder="请输入报价单号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="报价单名称" prop="quotationName">
<el-input
v-model="queryParams.quotationName"
placeholder="请输入报价单名称"
clearable
@keyup.enter.native="handleQuery"
/>
</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="quotationList" @row-click="handleRowClick" highlight-current-row>
<el-table-column label="报价单号" align="center" prop="quotationCode" />
<el-table-column label="报价单名称" align="center" prop="quotationName" />
<el-table-column label="项目编号" align="center" prop="projectCode" />
<el-table-column label="报价金额" align="center" prop="discountAmount" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { listQuotation } from "@/api/base/quotation";
export default {
name: "SelectQuotation",
props: {
visible: {
type: Boolean,
default: false,
},
createBy: {
type: String,
default: "-1",
},
},
data() {
return {
//
loading: true,
//
total: 0,
//
quotationList: [],
//
queryParams: {
pageNum: 1,
pageSize: 10,
// createBy: this.createBy,
quotationCode: null,
quotationName: null,
orderByColumn: 'createTime',
isAsc: 'desc'
},
};
},
watch: {
visible(val) {
if (val) {
// this.queryParams.createBy = this.createBy||"-1";
this.getList();
}
},
},
methods: {
/** 查询报价单列表 */
getList() {
this.loading = true;
listQuotation(this.queryParams).then(response => {
this.quotationList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 行点击事件 */
handleRowClick(row) {
this.$emit("quotation-selected", row);
this.handleClose();
},
/** 关闭按钮 */
handleClose() {
this.$emit("update:visible", false);
},
},
};
</script>

View File

@ -9,17 +9,17 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="制造商名称" prop="vendorName">
<el-form-item label="制造商名称" prop="vendorAddress">
<el-input
v-model="queryParams.vendorName"
v-model="queryParams.vendorAddress"
placeholder="请输入制造商名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="制造商全称" prop="vendorAddress">
<el-form-item label="制造商全称" prop="vendorName">
<el-input
v-model="queryParams.vendorAddress"
v-model="queryParams.vendorName"
placeholder="请输入制造商全称"
clearable
@keyup.enter.native="handleQuery"
@ -98,8 +98,8 @@
<el-table v-loading="loading" :data="vendorList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="制造商编码" align="center" prop="vendorCode" />
<el-table-column label="制造商名称" align="center" prop="vendorName" />
<el-table-column label="制造商全称" align="center" prop="vendorAddress" />
<el-table-column label="制造商名称" align="center" prop="vendorAddress" />
<el-table-column label="制造商全称" align="center" prop="vendorName" />
<el-table-column label="默认仓库" align="center" prop="warehouseName" />
<el-table-column label="联系人" align="center" prop="vendorUser" />
<el-table-column label="联系邮箱" align="center" prop="vendorEmail" />
@ -147,13 +147,13 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="制造商名称" prop="vendorName">
<el-input v-model="form.vendorName" placeholder="请输入制造商名称" />
<el-form-item label="制造商名称" prop="vendorAddress">
<el-input v-model="form.vendorAddress" placeholder="请输入制造商名称" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="制造商全称" prop="vendorAddress">
<el-input v-model="form.vendorAddress" placeholder="请输入制造商全称" />
<el-form-item label="制造商全称" prop="vendorName">
<el-input v-model="form.vendorName" placeholder="请输入制造商全称" />
</el-form-item>
<el-row>
<el-col :span="12">
@ -301,6 +301,8 @@ export default {
loading: true,
//
ids: [],
//
selectedVendorCodes: [],
//
single: true,
//
@ -330,6 +332,8 @@ export default {
vendorAddress: null,
vendorUser: null,
vendorStatus: null,
isAsc : 'desc',
orderByColumn : 'createTime',
},
//
form: {
@ -452,6 +456,7 @@ export default {
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.vendorId)
this.selectedVendorCodes = selection.map(item => item.vendorCode)
this.single = selection.length!==1
this.multiple = !selection.length
},
@ -524,8 +529,12 @@ export default {
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$modal.confirm('是否确认导出所有制造商信息数据项?').then(() => {
const queryParams = {
...this.queryParams,
vendorCodeList: this.selectedVendorCodes.length ? this.selectedVendorCodes : undefined
};
const exportTip = this.selectedVendorCodes.length ? '是否确认导出选中的制造商信息数据项?' : '是否确认导出所有制造商信息数据项?';
this.$modal.confirm(exportTip).then(() => {
this.exportLoading = true;
return exportVendor(queryParams);
}).then(response => {

View File

@ -0,0 +1,76 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="SN码" prop="productSn">
<el-input
v-model="queryParams.productSn"
placeholder="请输入SN码"
clearable
@keyup.enter.native="handleQuery"
/>
</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">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="inventoryInfoList">
<el-table-column label="SN码" align="center" prop="productSn" />
<el-table-column label="入库单号" align="center" prop="innerCode" />
<el-table-column label="出库单号" align="center" prop="outerCode" />
<el-table-column label="订单编号" align="center" prop="orderCode" />
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script>
import { listInventoryInfoByProductSn } from '@/api/dataProcess/inventoryInfo'
export default {
name: 'DataProcessInventoryInfo',
data() {
return {
loading: false,
showSearch: true,
total: 0,
inventoryInfoList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
productSn: null
}
}
},
methods: {
getList() {
this.loading = true
listInventoryInfoByProductSn(this.queryParams).then(response => {
this.inventoryInfoList = response.rows
this.total = response.total
this.loading = false
})
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery() {
this.resetForm('queryForm')
this.handleQuery()
}
}
}
</script>

View File

@ -0,0 +1,301 @@
<template xmlns="http://www.w3.org/1999/html">
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="项目编号" prop="projectCode">
<el-input
v-model="queryParams.projectCode"
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="代表处" prop="agentName">
<el-input
v-model="queryParams.agentName"
placeholder="请输入代表处"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="汇智负责人" prop="hzSupportUserName">
<el-input
v-model="queryParams.hzSupportUserName"
placeholder="请输入汇智负责人"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<br/>
<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
icon="el-icon-edit"
size="mini"
@click="handleTransfer"
v-hasPermi="['sip:project:add']"
>转移</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="projectList" @selection-change="handleSelectionChange" @sort-change="handleSortChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="项目编号" align="center" prop="projectCode" width="100" />
<el-table-column label="项目名称" align="center" prop="projectName" width="300">
<template slot-scope="scope">
<span>{{ scope.row.projectName }}</span>
</template>
</el-table-column>
<el-table-column label="最终客户" align="center" prop="customerName" width="200" />
<el-table-column label="BG" align="center" prop="bgProperty" width="70">
<template slot-scope="scope">
<dict-tag :options="dict.type.bg_type" :value="scope.row.bgProperty"/>
</template>
</el-table-column>
<el-table-column label="行业" align="center" prop="industryType" width="70">
<template slot-scope="scope">
<dict-tag v-if="scope.row.bgProperty === 'YYS'" :options="dict.type.bg_yys" :value="scope.row.industryType"/>
<dict-tag v-else :options="dict.type.bg_hysy" :value="scope.row.industryType"/>
</template>
</el-table-column>
<el-table-column label="代表处" align="center" prop="agentName" width="70" />
<el-table-column label="项目把握度" align="center" prop="projectGraspDegree" width="70" />
<el-table-column label="预计金额(元)" align="center" prop="estimatedAmount" width="120" sortable="custom" :sort-orders="['descending', 'ascending']">
<template slot-scope="scope">
<span>{{ formatAmountNumber(scope.row.estimatedAmount) }}</span>
</template>
</el-table-column>
<el-table-column label="项目阶段" align="center" prop="projectStage" width="160">
<template slot-scope="scope">
<dict-tag :options="dict.type.project_stage" :value="scope.row.projectStage"/>
</template>
</el-table-column>
<el-table-column label="建设类型" align="center" prop="constructionType" width="120" />
<el-table-column label="汇智负责人" align="center" prop="hzSupportUserName" width="100" />
<el-table-column label="POC" align="center" prop="poc" width="60">
<template slot-scope="scope">
<span>{{ scope.row.poc === '1' ? '是' : '否' }}</span>
</template>
</el-table-column>
<el-table-column label="预计下单时间" align="center" prop="estimatedOrderTime" width="140" sortable="custom" :sort-orders="['descending', 'ascending']">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.estimatedOrderTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="更新时间" align="center" prop="lastWorkUpdateTime" width="160" sortable="custom" :sort-orders="['descending', 'ascending']">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.lastWorkUpdateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="最后操作时间" align="center" prop="updateTime" width="160" sortable="custom" :sort-orders="['descending', 'ascending']">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</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="transferDialogVisible" width="500px" append-to-body>
<el-form ref="transferForm" :model="transferForm" :rules="transferRules" label-width="100px">
<el-form-item label="原汇智负责人" label-width="130px">
<el-input
v-model="transferForm.originalHzSupportUserName"
placeholder="原汇智负责人"
readonly
/>
</el-form-item>
<el-form-item label="新汇智负责人" prop="hzSupportUserName" label-width="130px">
<el-input
v-model="transferForm.hzSupportUserName"
placeholder="请选择新汇智负责人"
readonly
@click.native="openTransferUserSelect"
/>
<input type="hidden" v-model="transferForm.hzSupportUser" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="transferDialogVisible = false"> </el-button>
<el-button type="primary" @click="submitTransfer"> </el-button>
</div>
</el-dialog>
<select-user :visible.sync="selectUserVisible" @user-selected="handleUserSelected" />
</div>
</template>
<script>
import {listProject, transferProject} from "@/api/dataProcess/projectTransfer";
import SelectUser from "@/views/system/user/selectUser.vue";
export default {
name: "Project",
components: {
SelectUser
},
dicts: ['bg_type', 'bg_yys', 'bg_hysy', 'project_stage'],
data() {
return {
//
loading: true,
//
ids: [],
//
selectedRows: [],
//
showSearch: true,
//
total: 0,
//
projectList: [],
//
queryParams: {
pageNum: 1,
pageSize: 10,
projectCode: null,
projectName: null,
agentName: null,
hzSupportUserName: null,
isAsc: 'desc',
orderByColumn: 'createTime',
},
selectUserVisible: false,
transferDialogVisible: false,
transferForm: {
originalHzSupportUser: null,
originalHzSupportUserName: null,
hzSupportUser: null,
hzSupportUserName: null,
projectIdList: []
},
transferRules: {
hzSupportUserName: [{ required: true, message: "请选择汇智负责人", trigger: "blur" }]
}
};
},
created() {
this.getList();
},
methods: {
handleSortChange(column) {
this.queryParams.orderByColumn = column.prop;
if (column.order === 'ascending') {
this.queryParams.isAsc = 'asc';
} else if (column.order === 'descending') {
this.queryParams.isAsc = 'desc';
} else {
this.queryParams.isAsc = null;
this.queryParams.orderByColumn = null;
}
this.getList();
},
/** 查询项目管理列表 */
getList() {
this.loading = true;
listProject(this.queryParams).then(response => {
this.projectList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.selectedRows = selection
},
handleTransfer() {
if (!this.selectedRows || this.selectedRows.length === 0) {
this.$modal.msgWarning("请至少选择一条数据");
return;
}
const hasEmptyHzSupportUser = this.selectedRows.some(item => !item.hzSupportUser);
if (hasEmptyHzSupportUser) {
this.$modal.msgWarning("选中数据存在无汇智负责人的项目,无法转移");
return;
}
const currentHzSupportUser = this.selectedRows[0].hzSupportUser;
const isSameHzSupportUser = this.selectedRows.every(item => item.hzSupportUser === currentHzSupportUser);
if (!isSameHzSupportUser) {
this.$modal.msgWarning("请选择同一个汇智负责人的项目进行转移");
return;
}
this.transferForm = {
originalHzSupportUser: currentHzSupportUser,
originalHzSupportUserName: this.selectedRows[0].hzSupportUserName,
hzSupportUser: null,
hzSupportUserName: null,
projectIdList: [...this.ids]
};
this.transferDialogVisible = true;
},
openTransferUserSelect() {
this.selectUserVisible = true;
},
handleUserSelected(user) {
this.transferForm.hzSupportUser = user.userId;
this.transferForm.hzSupportUserName = user.userName;
},
submitTransfer() {
this.$refs["transferForm"].validate(valid => {
if (!valid) {
return;
}
const params = {
originalHzSupportUser: this.transferForm.originalHzSupportUser,
originalHzSupportUserName: this.transferForm.originalHzSupportUserName,
hzSupportUser: this.transferForm.hzSupportUser,
hzSupportUserName: this.transferForm.hzSupportUserName,
projectIdList: this.transferForm.projectIdList
};
transferProject(params).then(() => {
this.$modal.msgSuccess("提交成功");
this.transferDialogVisible = false;
this.getList();
});
});
},
/** 格式化金额 */
formatAmountNumber(value) {
if (value) {
return Number(value).toLocaleString('en-US');
}
return '';
}
}
};
</script>

View File

@ -0,0 +1,310 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="项目编号" prop="projectCode">
<el-input
v-model="queryParams.projectCode"
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="进货商" prop="partnerName">
<el-input
v-model="queryParams.partnerName"
placeholder="请输入进货商"
clearable
@keyup.enter.native="handleQuery"
/>
</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="danger"-->
<!-- plain-->
<!-- icon="el-icon-delete"-->
<!-- size="mini"-->
<!-- :disabled="multiple"-->
<!-- @click="handleDelete"-->
<!-- v-hasPermi="['finance:charge:remove']"-->
<!-- >删除</el-button>-->
<!-- </el-col>-->
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['finance:charge:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="chargeList" @selection-change="handleSelectionChange">
<!-- <el-table-column type="selection" width="55" align="center" />-->
<el-table-column label="项目编码" align="center" prop="projectCode" width="180" v-if="columns.projectCode.visible" key="projectCode"/>
<el-table-column label="项目名称" align="center" prop="projectName" width="240" v-if="columns.projectName.visible" key="projectName"/>
<el-table-column label="合同编号" align="center" prop="orderCode" width="180" v-if="columns.orderCode.visible" key="orderCode"/>
<el-table-column label="下单通路" align="center" prop="orderChannel" width="100" v-if="columns.orderChannel.visible" key="orderChannel">
<template slot-scope="scope">
<el-tag>{{ scope.row.orderChannel==='1'? "总代":"直签" }}</el-tag>
</template>
</el-table-column>
<el-table-column label="供货商" align="center" prop="supplier" width="180" v-if="columns.supplier.visible" key="supplier"/>
<el-table-column label="进货商" align="center" prop="partnerName" width="180" v-if="columns.partnerName.visible" key="partnerName"/>
<el-table-column label="业务侧可计收时间" align="center" prop="bizChargeDate" width="180" v-if="columns.bizChargeDate.visible" key="bizChargeDate">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.bizChargeDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="财务计收时间" align="center" prop="financeChargeDate" width="180" v-if="columns.financeChargeDate.visible" key="financeChargeDate">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.financeChargeDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="收入" align="center" v-if="columns.incomeWithTaxTotal.visible || columns.incomeWithoutTaxTotal.visible || columns.incomeTax.visible">
<el-table-column label="含税总价(元)" align="center" prop="incomeWithTaxTotal" width="180" v-if="columns.incomeWithTaxTotal.visible" key="incomeWithTaxTotal" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="未含税总价(元)" align="center" prop="incomeWithoutTaxTotal" width="180" v-if="columns.incomeWithoutTaxTotal.visible" key="incomeWithoutTaxTotal" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="税额(元)" align="center" prop="incomeTax" width="180" v-if="columns.incomeTax.visible" key="incomeTax">
<template slot-scope="scope">
<span>{{ formatCurrency($calc.sub(scope.row.incomeWithTaxTotal, scope.row.incomeWithoutTaxTotal)) }}</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="成本" align="center" v-if="columns.costSoftwareWithoutTax.visible || columns.costHardwareWithoutTax.visible || columns.costSoftwareMaintWithoutTax.visible || columns.costHardwareMaintWithoutTax.visible || columns.costProvinceServiceWithoutTax.visible || columns.costOtherWithoutTax.visible || columns.allCostWithoutTax.visible">
<el-table-column label="软件折后未税小计" align="center" prop="costSoftwareWithoutTax" width="180" v-if="columns.costSoftwareWithoutTax.visible" key="costSoftwareWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="硬件折后未税小计" align="center" prop="costHardwareWithoutTax" width="180" v-if="columns.costHardwareWithoutTax.visible" key="costHardwareWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="软件维保折后未税小计" align="center" prop="costSoftwareMaintWithoutTax" width="180" v-if="columns.costSoftwareMaintWithoutTax.visible" key="costSoftwareMaintWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="硬件维保折后未税小计" align="center" prop="costHardwareMaintWithoutTax" width="180" v-if="columns.costHardwareMaintWithoutTax.visible" key="costHardwareMaintWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="省代服务折后未税小计" align="center" prop="costProvinceServiceWithoutTax" width="180" v-if="columns.costProvinceServiceWithoutTax.visible" key="costProvinceServiceWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="其它折后未税小计" align="center" prop="costOtherWithoutTax" width="180" v-if="columns.costOtherWithoutTax.visible" key="costOtherWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="成本未税合计" align="center" prop="allCostWithoutTax" width="180" v-if="columns.allCostWithoutTax.visible" key="allCostWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)">
</el-table-column>
</el-table-column>
<el-table-column label="毛利" align="center" prop="grossProfit" width="180" v-if="columns.grossProfit.visible" key="grossProfit" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="毛利率" align="center" prop="grossProfitRate" width="180" v-if="columns.grossProfitRate.visible" key="grossProfitRate"/>
<el-table-column label="备注" align="center" prop="remark" width="180" v-if="columns.remark.visible" key="remark"/>
<el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleRemark(scope.row)"
v-hasPermi="['finance:charge:edit']"
>编辑备注</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-refresh-left"
@click="handleRevoke(scope.row)"
v-hasPermi="['finance:charge:remove']"
>撤销</el-button>
<!-- <el-button-->
<!-- size="mini"-->
<!-- type="text"-->
<!-- icon="el-icon-delete"-->
<!-- @click="handleDelete(scope.row)"-->
<!-- v-hasPermi="['finance:charge:remove']"-->
<!-- >删除</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="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注内容" :rows="4" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listCharge, getCharge, delCharge, updateCharge, revokeCharge } from "@/api/finance/charge";
export default {
name: "Charge",
dicts: ['finance_charge_status'],
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
chargeList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
projectCode: null,
projectName: null,
partnerName: null,
orderByColumn:'createTime',
isAsc: 'desc'
},
//
columns: {
projectCode: { label: '项目编码', visible: true },
projectName: { label: '项目名称', visible: true },
orderCode: { label: '合同编号', visible: true },
orderChannel: { label: '下单通路', visible: true },
supplier: { label: '供货商', visible: true },
partnerName: { label: '进货商', visible: true },
bizChargeDate: { label: '业务侧可计收时间', visible: true },
financeChargeDate: { label: '财务计收时间', visible: true },
incomeWithTaxTotal: { label: '含税总价(元)', visible: true },
incomeWithoutTaxTotal: { label: '未含税总价(元)', visible: true },
incomeTax: { label: '税额(元)', visible: true },
costSoftwareWithoutTax: { label: '软件折后未税小计', visible: true },
costHardwareWithoutTax: { label: '硬件折后未税小计', visible: true },
costSoftwareMaintWithoutTax: { label: '软件维保折后未税小计', visible: true },
costHardwareMaintWithoutTax: { label: '硬件维保折后未税小计', visible: true },
costProvinceServiceWithoutTax: { label: '省代服务折后未税小计', visible: true },
costOtherWithoutTax: { label: '其它折后未税小计', visible: true },
allCostWithoutTax: { label: '成本未税合计', visible: true },
grossProfit: { label: '毛利', visible: true },
grossProfitRate: { label: '毛利率', visible: true },
remark: { label: '备注', visible: true }
},
//
form: {},
};
},
created() {
this.getList();
},
methods: {
/** 查询财务计收列表 */
getList() {
this.loading = true;
listCharge(this.queryParams).then(response => {
this.chargeList = response.rows;
this.total = response.total;
this.loading = false;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
id: null,
remark: null,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 多选框选中数据 */
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 编辑备注按钮操作 */
handleRemark(row) {
this.reset();
const id = row.id || this.ids
// 使 getCharge row
getCharge(id).then(response => {
this.form = {
id: response.data.id,
remark: response.data.remark
};
this.open = true;
this.title = "编辑备注";
});
},
/** 撤销按钮操作 */
handleRevoke(row) {
this.$modal.confirm('是否确认撤销该条数据?').then(function() {
return revokeCharge({id: row.id});
}).then(() => {
this.getList();
this.$modal.msgSuccess("撤销成功");
}).catch(() => {});
},
/** 提交按钮 */
submitForm() {
//
updateCharge({id:this.form.id,remark:this.form.remark}).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除财务计收编号为"' + ids + '"的数据项?').then(function() {
return delCharge(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
this.download('finance/charge/export', {
...this.queryParams
}, `charge_${new Date().getTime()}.xlsx`)
}
}
};
</script>

Some files were not shown because too many files have changed in this diff Show More