Compare commits
318 Commits
dev1
..
70bb94a2e6
| Author | SHA1 | Date | |
|---|---|---|---|
| 70bb94a2e6 | |||
| f78e61b433 | |||
| 508d73fc0d | |||
| e73e1da877 | |||
| 4d5b99da82 | |||
| f3021d4bae | |||
| 7eb6cce2db | |||
| be2888d428 | |||
| 06f6950434 | |||
| 09f1af9a0a | |||
| 6d4961f404 | |||
| cba2ad3b3b | |||
| a7315b4491 | |||
| 0d070d4b34 | |||
| 6a6d10dc79 | |||
| 3bac9b6472 | |||
| 96f2fa0eb5 | |||
| e789f1647a | |||
| c63c7c3e33 | |||
| 23b23dc8e5 | |||
| a0a3d53927 | |||
| 42e4925fd6 | |||
| 024e77801e | |||
| 397490a165 | |||
| 9e14f31604 | |||
| 40925cb21f | |||
| 6f0f1842cb | |||
| 685a37b2df | |||
| b83e3c358d | |||
| 94da08e7a7 | |||
| 008ec7a6a8 | |||
| 429b5548dd | |||
| 6d4434f5c9 | |||
| 4ffceb0413 | |||
| 6eff4b7383 | |||
| a8986690bb | |||
| 87ec01c8dd | |||
| f25e38d67e | |||
| 8a0db99572 | |||
| 9c2a2ee6e6 | |||
| 252df72f87 | |||
| 80a15fc626 | |||
| f858eb2bcd | |||
| a9b4ab069b | |||
| 449e4e1884 | |||
| dea760ea1c | |||
| 9b9e652afd | |||
| 2451efdfcb | |||
| a75ac1cfa4 | |||
| 3a08e12c65 | |||
| a10eea247f | |||
| 4fb8c74b5c | |||
| 0a77fd2014 | |||
| 16af750030 | |||
| a3ff00eaaf | |||
| d901d484fd | |||
| b6fed6892a | |||
| d7ac4e4045 | |||
| f7eea88945 | |||
| 9a4e0f7c03 | |||
| 99d6284a58 | |||
| 17bbde81b2 | |||
| 3482d03905 | |||
| b446e34c94 | |||
| 704c3093d7 | |||
| 36cc9c751c | |||
| e98aa61cd2 | |||
| cec4c323a7 | |||
| 58abe5ebdf | |||
| fc70aeb2af | |||
| 9342e674c5 | |||
| a176608019 | |||
| 3a262216c7 | |||
| c9f673342a | |||
| 2acdcb08a9 | |||
| 2c08d0c362 | |||
| b36aefc7c4 | |||
| 7f70052afd | |||
| 84c46b9312 | |||
| 88c5f58548 | |||
| 1cf8abb162 | |||
| 201ca245a7 | |||
| bfb8659cd8 | |||
| ae45e88df9 | |||
| 9d512ebad4 | |||
| 5dc3c2d2c5 | |||
| 70ebe31103 | |||
| c3f05a342e | |||
| dca137df8d | |||
| 8b1ac09592 | |||
| 35a43cce7a | |||
| 808a52ae25 | |||
| 899a8c1c2b | |||
| dc5a2235eb | |||
| 3da2d428f3 | |||
| cb98355715 | |||
| 335e45c3f1 | |||
| 325e708ef1 | |||
| 112acbbf34 | |||
| d58f73ab50 | |||
| fdd17c5878 | |||
| 8bf0c31dcd | |||
| bb97c00a6a | |||
| f21e3d56d9 | |||
| cf39d63396 | |||
| 5033dc9e43 | |||
| 3cb365253b | |||
| 665503f43e | |||
| 4340c9fc59 | |||
| 7c12163966 | |||
| d6b6be0e9b | |||
| 55583551b5 | |||
| 9c783c802d | |||
| 495e6a073c | |||
| 1ebd4d0fa3 | |||
| 6a0ea777da | |||
| 1625fb292d | |||
| e309987199 | |||
| f3c29ef2bf | |||
| 2bd9d8c047 | |||
| 01c3bb5b57 | |||
| 8955ef16c7 | |||
| 9405c51de8 | |||
| e5992a0c63 | |||
| 1ef050dda6 | |||
| 21c4168867 | |||
| 8883186344 | |||
| 6bd8d18e20 | |||
| 4ca91f9b56 | |||
| e1bade226f | |||
| 44e31fbd22 | |||
| 5fcb82c0c0 | |||
| 4edaa8486d | |||
| 5cf688bf06 | |||
| bcdd412d95 | |||
| 851379f4de | |||
| 051bda5b3a | |||
| 039a08a86a | |||
| 5535dae24b | |||
| 6989486d8a | |||
| 037031b76c | |||
| 1962bee882 | |||
| 868ec4aac0 | |||
| 73b15e2a83 | |||
| 792356b146 | |||
| 4528117143 | |||
| 6381a7987c | |||
| 21b7877ff1 | |||
| 10878d4aee | |||
| 63256a9d21 | |||
| b07205be8c | |||
| 5d1d15d3a5 | |||
| 8a78f4d01a | |||
| e538b7f509 | |||
| 8870925c60 | |||
| 37a4eef515 | |||
| 8f35e6b265 | |||
| f6b05ed71d | |||
| 24abe2ecbd | |||
| 608a86588d | |||
| fd5b1484e6 | |||
| ddfd911d2e | |||
| 4199255186 | |||
| 15dda5f1ef | |||
| 285a281439 | |||
| b8decb1d73 | |||
| b3510cf233 | |||
| 8897f61bfa | |||
| a68cf04ff1 | |||
| e50391f4a5 | |||
| 230110c486 | |||
| c6217fcff7 | |||
| 574b1a52f4 | |||
| a69a2efe45 | |||
| 2fae0772fe | |||
| c68ce66405 | |||
| 9948c8f33b | |||
| 2b4179f3cd | |||
| bac0da2b5f | |||
| 9e5fbfd2d4 | |||
| 6bc9981ffc | |||
| 6e012d766c | |||
| 3bb0fddcca | |||
| eceac8b31f | |||
| 5c89140b21 | |||
| 27672dfaff | |||
| 723df52818 | |||
| b25b2fae20 | |||
| e48e5b6989 | |||
| fb633e27c6 | |||
| 8b35fcaa0d | |||
| b64465a4c3 | |||
| 490efe2ce5 | |||
| f62e36e846 | |||
| f7b7218e62 | |||
| a89cb44e36 | |||
| 8c7ef5822a | |||
| 563909fafa | |||
| 6554ad80dd | |||
| 748dd80d4a | |||
| 3e585b89fd | |||
| 8c85eb7f81 | |||
| 040456f4d2 | |||
| e8e6bc01b0 | |||
| c97f1fc0e3 | |||
| f0ee1e6ea3 | |||
| a76d494215 | |||
| 1f669bfd29 | |||
| 9c37fa4c61 | |||
| 58033f5a79 | |||
| 1e505d5dbf | |||
| 559d8e7391 | |||
| 0a5b3b2cab | |||
| 637de76165 | |||
| 9391c4db09 | |||
| 7fad45bc1e | |||
| 0469b3e10c | |||
| a8c495a102 | |||
| 44e7a2692b | |||
| 8e2ad08bf2 | |||
| d5528541ce | |||
| 9aa1bb4fab | |||
| 7fb8113c8e | |||
| 56729c8cc9 | |||
| 04ab648f5b | |||
| f550b55341 | |||
| 5aefa23f50 | |||
| c92d2a405a | |||
| be4ccc89f6 | |||
| 565553b3f7 | |||
| 52d4b6b486 | |||
| 6a02e9126b | |||
| 6364b593ce | |||
| bb580b2bfc | |||
| 6c91889e33 | |||
| 4bef3430e0 | |||
| 17d12d279d | |||
| 9cf005cece | |||
| d8db9b1edd | |||
| cf04d48010 | |||
| 454608d7be | |||
| d82d06dc78 | |||
| f03f764472 | |||
| 1e4de76978 | |||
| e6d4edf362 | |||
| 1862115e1d | |||
| 49236cad56 | |||
| 02b2e3b673 | |||
| 53676a1150 | |||
| 5e92124710 | |||
| 8a262d9d41 | |||
| badfed7ff1 | |||
| c339852e04 | |||
| 819a59e6fc | |||
| f663098b4a | |||
| ef417a0290 | |||
| e5a7aca09e | |||
| 4d0cc2d595 | |||
| f6451643e8 | |||
| 8dddb72328 | |||
| fa99cc3eff | |||
| 433c611195 | |||
| 6cf535cc79 | |||
| e683c2cc00 | |||
| 3268f12501 | |||
| f9f8221870 | |||
| 81c98f6ad8 | |||
| 6a4c5d02f4 | |||
| 70856ca44c | |||
| dd857414c9 | |||
| 4383e9956e | |||
| 79f8fbad1d | |||
| 70779fcf63 | |||
| 71dd52c650 | |||
| 7f01b119b5 | |||
| d1c8786043 | |||
| 368e027426 | |||
| c270258b7b | |||
| a9fd78b5b9 | |||
| e799b20428 | |||
| 638d09b19d | |||
| 06dc1c64f6 | |||
| 9b7f8db96b | |||
| 449719dfe1 | |||
| 81b2a3e99b | |||
| ccaca521d3 | |||
| af0675f26b | |||
| 89c4f0d4df | |||
| 08ec2389a5 | |||
| 46d34f489e | |||
| ee84cad033 | |||
| 17df5e8cbe | |||
| 780968b68f | |||
| de8a87af6c | |||
| ff91e8dbd7 | |||
| 90d96e01a4 | |||
| a71d2c9350 | |||
| a1c8e7c1ce | |||
| 6e71c38dad | |||
| d62f8bf320 | |||
| 5a66c89349 | |||
| 0b2e07f1fe | |||
| 1434bbf691 | |||
| c7e67a83b7 | |||
| d48b18c99e | |||
| 72c6abf122 | |||
| e460813480 | |||
| 6eba9d2c2f | |||
| 38ce8fa2bc | |||
| 8002fe3d50 | |||
| c695d72ec8 | |||
| 8ff9f6bb66 | |||
| e76cf70a20 | |||
| 1c7532e0de | |||
| e50d1b8fda | |||
| 76df2b9cd2 | |||
| c6dffb5d97 | |||
| e66b4a3680 |
@@ -3,6 +3,8 @@
|
|||||||
VITE_APP_ENV = 'development'
|
VITE_APP_ENV = 'development'
|
||||||
|
|
||||||
#接口地址
|
#接口地址
|
||||||
|
# 开发环境建议填 [/api]:由下方 vite.config.mjs 的 proxy 同源转发到后端(172.16.203.228:8000),避免跨域(CORS)被浏览器拦截
|
||||||
|
# 如需直连线上绝对地址(如 http://172.16.203.228:8000/api),必须让后端 CORS 把 Allow-Origin 改为具体前端域名,不能用 `*`(配合 credentials 会被浏览器拒绝)
|
||||||
VITE_APP_API=/api
|
VITE_APP_API=/api
|
||||||
|
|
||||||
#调试参数
|
#调试参数
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
## 2025-07-29
|
||||||
|
|
||||||
|
- 货物类型管理页面(cargo-type):搜索栏「类型」和「货物类型编码」字段 searchSpan 从 12 分别调整为 6 和 8,收窄 input 宽度使两字段在一排内紧凑展示
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# 2026-07-27 工作日志
|
||||||
|
|
||||||
|
## 开发环境启动与登录排查
|
||||||
|
|
||||||
|
- 终止了遗留的 2888 端口 dev server 进程,清理端口占用。
|
||||||
|
- 将 `.env.development` 的 `VITE_APP_API` 从 `http://172.16.203.228:8000/api` 改为 `/api`,让前端请求走 Vite 代理。
|
||||||
|
- 将 `vite.config.mjs` 的 `/api` 代理 target 从 `http://localhost` 改为 `http://172.16.203.228:8000`,并去掉 `rewrite`(后端接口本身就在 `/api` 前缀下,不能去掉)。
|
||||||
|
- 修复后发现登录无响应的根因:后端 CORS 返回 `Access-Control-Allow-Origin: *`,与 axios `withCredentials: true` 冲突,浏览器拦截所有跨域请求;走 Vite 代理后同源,CORS 问题消失。
|
||||||
|
- 启动 dev server 于 `http://localhost:2888`。
|
||||||
|
- 使用 agent-browser(系统 Chrome)打开登录页,填入账号 `admin` / 密码 `admin`,成功触发行为验证码弹窗。
|
||||||
|
- 行为验证码(滑块/拼图)需人工在浏览器窗口完成。
|
||||||
|
|
||||||
|
## 货物类型导入模板模拟数据
|
||||||
|
|
||||||
|
- 模板:`/Users/gxwebsoft/Downloads/货物类型导入模板.xlsx`,单 Sheet「货物类型导入模板」,表头 6 列:类型(必填)、上级货物类型、上级货物类型编码、货物类型(必填)、货物类型编码(必填)、备注。一级类型上级字段留空。
|
||||||
|
- 在 `/Users/gxwebsoft/Downloads/货物类型导入模板_模拟数据.xlsx` 生成 5 条模拟数据(2 条一级 + 3 条二级,演示层级)。原模板保持不变。
|
||||||
|
|
||||||
|
## 货物类型模块(/base/cargo-type)按需求完善
|
||||||
|
|
||||||
|
- 需求:新增默认二级;类型/上级/编码在编辑查看禁编辑;一级隐藏上级字段、二级显示;同上级名称唯一 + 编码全局唯一交后端;提交失败展示后端提示。
|
||||||
|
- 抽出 `src/option/base/cargo-type.js`,导出工厂 `getCargoTypeOption(ctx)`,依赖组件的校验/change 通过 `ctx` 闭包访问(`ctx.form`、`ctx.handleTypeLevelChange` 等),避免 Option API 写死 this。
|
||||||
|
- `cargo-type.vue` 改动:import 该 option;新增默认 `typeLevel:2`;`beforeOpen` 设置 `dialogType`(add/edit/view);上级下拉框 `:disabled="dialogType!=='add' || typeLevel!==2"`;列配置加 `editDisabled:true`(类型/编码/上级编码)控制编辑禁用;view 模式 Avue 自动禁用其余字段。
|
||||||
|
- 提交失败处理:axios 拦截器已对非 200 弹 `ElMessage`(后端 msg),故 rowSave/rowUpdate 的 error 仅 `loading()`,不再 console/重复 toast。
|
||||||
|
- 导入失败明细下载已合规:`src/utils/import-excel.js` 的 `handleImportExcel` 已实现 content-type 区分 JSON/Excel、失败下载 `货物类型导入失败明细时间.xlsx`、错误提示;businessName='货物类型'。
|
||||||
|
- 注意:导入/下载模板由后端 `/blade-system/cargo-type/import-cargo-type`、`/export-template` 控制,前端模板列名由后端决定,需后端保证与需求卡字段一致。
|
||||||
|
|
||||||
|
## 货物类型批量导入失败排查(19:12)
|
||||||
|
|
||||||
|
- 失败明细 `货物类型导入失败明细2026-07-27 19_12_24.xlsx`:5 条全部「请选择货物类型级别」。
|
||||||
|
- 根因(数据问题,非前端代码):模拟数据「类型」列误填为业务分类 `普通货物/冷链货物`,但需求卡 REQ-CARGO-007/008 规定「类型」是**货物类型级别**,仅允许填 `一级货物类型`/`二级货物类型`;且编码误用字母(`GOODS-ELEC-001`),违反 REQ-CARGO-006「一级 2 位数字、二级 4 位数字」。
|
||||||
|
- 已重造 `货物类型导入模板_模拟数据.xlsx`:类型改为一级/二级;编码改为纯数字(01/02 一级,0101/0102/0201 二级继承父级前 2 位)。
|
||||||
|
- 风险:二级行依赖一级行已存在,若后端按 DB 校验而非按文件顺序处理,二级行可能报「请选择上级货物类型」,建议先导入 2 条一级、再导入 3 条二级。
|
||||||
|
|
||||||
|
## 货物类型导入失败复查(23:27)
|
||||||
|
|
||||||
|
- 第二版失败明细 `货物类型导入失败明细2026-07-27 19_17_21.xlsx`:类型已正确,但一级报「货物类型编码格式不正确」、二级报「请选择上级货物类型」。
|
||||||
|
- 根因:编码列被存成**数字(int)**,前导零丢失('01'→1、'0101'→101),一级编码非 2 位、二级非 4 位;一级失败未入库 → 二级找不到上级。
|
||||||
|
- 修复:写入时强制 C/E 列 `number_format='@'`(文本)保留前导零;编码 '01'/'02'、'0101'/'0102'/'0201'。
|
||||||
|
- 为消除级联风险,拆成两个文件:货物类型导入_一级.xlsx(2条)、货物类型导入_二级.xlsx(3条),先导入一级再导入二级确保成功。
|
||||||
|
|
||||||
|
## 货物类型导入第三次复查(23:30)
|
||||||
|
|
||||||
|
- 第三版失败明细:一级导入也失败,报「该货物类型编码已存在」(01/02 已被占用)。根因:01/02 极可能是用户此前测试新增功能时手动建的,导入撞车;失败导入不写库,说明编码 pre-exist。
|
||||||
|
- 同期发现 2888 dev server 进程退出(lsof 无监听),导致 Vite 代理对 localhost:2888 全返回 502(含根路径)。已重启 `pnpm dev`(端口 2888,proxy target 172.16.203.228:8000),代理恢复 200。后端直连一直正常。
|
||||||
|
- 改用新编码重造:一级 11/12(电子产品/生鲜食品),二级 1101/1102(父11)、1201(父12),文本格式保留前导零。三份文件(一级/二级/合并)已更新。
|
||||||
|
- 结论:导入功能本身 OK,问题一直是造数(类型值、编码格式、编码冲突)+ dev server 偶发退出。建议导入前确认 dev server 在跑。
|
||||||
|
|
||||||
|
## 导入失败明细标红 + 原因改写(23:50)
|
||||||
|
|
||||||
|
- 需求:导入失败后,失败明细 Excel 中**仅违规那一栏标红**,且「导入失败原因」列改写为清晰格式,如`(第2行:货物类型编码格式不正确,一级2位数字,二级4位数字)`。
|
||||||
|
- 现状:失败明细 Excel 此前是后端生成、前端 `import-excel.js` 直接 `downloadXls` 原样下载,无加工。
|
||||||
|
- 改动:
|
||||||
|
1. `pnpm add exceljs`(4.4.0,运行时依赖;`import('exceljs')` 动态加载,不进主包)。
|
||||||
|
2. `src/utils/import-excel.js`:`openImportDialog` 增加可选 `options.failDetailDecorator`;`handleImportExcel` 在 Excel 响应分支用 exceljs 读取后端失败明细 blob → 调用 decorator 加工(标红+改写原因)→ 再下载。**未传 decorator 的模块保持原行为(向后兼容、通用)**。
|
||||||
|
3. `src/views/base/cargo-type.vue`:新增模块级 `decorateCargoTypeFailDetail(workbook)`,建立「表头列名→列号」映射,按错误文本匹配规则(7 类错误 → 标红列 + 规则说明),遍历数据行标红违规栏并将原因列改写为`(第N行:错误,规则)`;`handleImport` 传入 `{ failDetailDecorator: decorateCargoTypeFailDetail }`。标红填充色 `FFFFC7CE`(淡红),物理行号(表头第1行,数据第2行起)。
|
||||||
|
- 验证:Node + exceljs 跑 decorator 等价逻辑全 PASS(标红列命中、原因格式正确);vite 将 `import('exceljs')` 重写为浏览器版 chunk(/node_modules/.vite/deps/exceljs.js,HTTP 200,1.46MB),浏览器运行时无 polyfill 报错;cargo-type.vue 与 import-excel.js 经 vite 编译均 200。
|
||||||
|
- 注意:decorator 依赖后端失败明细 Excel 的「导入失败原因」列文本与模板列名(类型/上级货物类型/上级货物类型编码/货物类型/货物类型编码/备注/导入失败原因)精确一致;若后端改列名需同步更新 `CARGO_TYPE_FAIL_RULES` 的 col 值。
|
||||||
|
|
||||||
|
## 导入失败明细改为「不标红 + 加列宽」(00:08 次晨)
|
||||||
|
|
||||||
|
- 用户反馈两点:① 不要标红,只写清错误原因即可;② 失败明细 xls 列太挤,需加宽。
|
||||||
|
- 改动(同一对文件):
|
||||||
|
- `src/views/base/cargo-type.vue`:删除 `FAIL_RED_FILL` 常量与所有 `cell.fill` 标红逻辑,仅保留原因列改写为`(第N行:错误,规则)`;新增 `CARGO_TYPE_FAIL_COL_WIDTH` 列宽映射(类型14/上级货物类型16/上级货物类型编码20/货物类型16/货物类型编码16/备注18/导入失败原因60),遍历 colMap 设置 `ws.getColumn(n).width`;原因列额外 `alignment={wrapText:true,vertical:'top'}` 提升可读性。
|
||||||
|
- `src/utils/import-excel.js`:注释由「标红 / 改写原因列」改为中性「改写原因列、调整列宽等加工」。
|
||||||
|
- 验证:Node + exceljs 等价逻辑全 PASS(无红色填充=✅、原因含「第N行」、导入失败原因列宽=60);cargo-type.vue 与 import-excel.js vite 编译均 200,FAIL_RED_FILL 残留=0。
|
||||||
|
|
||||||
|
## 下载模板改为「前端本地生成 + 带示例」(00:11 次晨)
|
||||||
|
|
||||||
|
- 需求:点击「下载模板」给的导入模板要自带一个数据示例(参照可正常导入的 `货物类型导入模板_模拟数据.xlsx`)。
|
||||||
|
- 现状:`handleTemplate` 原调用后端 `/blade-system/cargo-type/export-template` 下载空模板(无示例)。
|
||||||
|
- 改动 `src/views/base/cargo-type.vue` 的 `handleTemplate`:改为 `import('exceljs')` 本地生成模板,不再依赖后端空模板。
|
||||||
|
- 表头严格使用真实模板列名(含 `*` 与括号说明):`*类型` / `上级货物类型(如为一级则不需填写)` / `上级货物类型编码(如为一级则不需填写)` / `*货物类型` / `*货物类型编码` / `备注`(与可正常导入的本地模板一致)。
|
||||||
|
- 示例数据 2 条:一级 `电子产品2/117` + 二级 `手机2/11014`(父117),演示层级,取值与用户确认可导入的模拟数据一致。
|
||||||
|
- 列宽加宽(类型14/上级货物类型24/上级货物类型编码26/货物类型16/货物类型编码16/备注18);编码列(3、5) `numFmt='@'` 文本格式防前导零丢失;表头行 `wrapText` 完整显示长说明。
|
||||||
|
- 下载文件名仍为 `货物类型导入模板.xlsx`。`exportBlob` 仍被 `handleExport` 使用,import 不冗余。
|
||||||
|
- 验证:Node + exceljs 等价逻辑确认表头/示例行/列宽/文本格式正确(稀疏 eachCell 跳过空单元格导致一次断言误报,按列号复核确认数据无误);cargo-type.vue vite 编译 200。
|
||||||
|
- 注意:示例行若被直接导入会创建 `电子产品2/手机2` 两条演示数据;用户应按需删除/替换示例再填自己的数据。
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# 2026-07-28 工作日志
|
||||||
|
|
||||||
|
## 货物类型导入模板美化
|
||||||
|
- 文件:`src/views/base/cargo-type.vue` 的 `handleTemplate()`
|
||||||
|
- 改动(第一次):表头行加主色底色 `#409EFF` + 白色加粗字体 + 居中对齐 + 细边框;行高 42。
|
||||||
|
- 改动(调整后):按用户反馈,表头底色改为统一灰色 `#D9D9D9` + 黑色加粗字体,行高降为 30。
|
||||||
|
- 导入失败明细 `decorateCargoTypeFailDetail` 调整:仅「导入失败原因」列字体红色;错误按 `A./B./C.` 编号,分号(;)隔开,并去掉后端重复的「第N行:」前缀;最终格式示例 `第2行:A.该货物类型编码已存在,请更换唯一编码`。
|
||||||
|
- 验证:Node + exceljs 4.4.0 跑通生成逻辑,确认表头 fill/font/alignment/height、失败原因编号格式与红色字体均正确。
|
||||||
|
|
||||||
|
## 货物类型导入失败明细 - 备注超长与标红强化
|
||||||
|
- 确认需求:除「导入失败原因」列外,其他列一律不标红;已增加 decorator 清除所有非失败原因列红色字体(含类型列,不论对错)。
|
||||||
|
- 失败原因按 A./B./C. 编号、分号(;)隔开;并优化:错误文本已包含规则说明时不重复拼接(避免「备注不能超过200字,备注不能超过200字」)。
|
||||||
|
- 新增兜底:失败明细行若「备注」列文本长度 > 200 且失败原因未含备注错误,自动追加一条「备注不能超过200字」编号项(Node 验证通过)。
|
||||||
|
- 后端根因(已读 /Users/gxwebsoft/JAVA/tms-erp-api-ws/blade-service/blade-system/.../CargoTypeServiceImpl.java):validate() 用 throw 实现,一行多个错误时只抛第一个,备注超长校验(第238行)可能被前置的编码/名称/上级错误挡住,导致失败明细不显示备注原因。彻底方案需后端改收集式校验(列出全部错误),待用户确认是否改 Java。
|
||||||
|
- **最终确认(样式)**:decorator 仅操作 G 列「导入失败原因」——黑色字体(argb `FF000000`)、无背景填充(fill→none);其余列单元格样式/内容不修改。
|
||||||
|
- **列宽修复(关键 bug)**:失败明细列宽之前全部失效,根因是 `CARGO_TYPE_FAIL_COL_WIDTH` 的 key 用了「类型/上级货物类型」等简写,而后端 `CargoTypeImportFailureExcel` 表头带 `*` 与括号说明(如 `*类型`、`上级货物类型(如为一级则不需填写)`),key 对不上导致走默认 18。已把 key 改为与后端表头完全一致,并恢复「给所有列设宽」逻辑。最终宽度:`*类型`16 / `上级货物类型(…)`22 / `上级货物类型编码(…)`26 / `*货物类型`18 / `*货物类型编码`18 / `备注`24 / `导入失败原因`70。Node 验证通过。
|
||||||
|
- **事务性导入(后端改动)**:`CargoTypeServiceImpl.importCargoType` 加 `@Transactional(rollbackFor=Exception.class)`,逐条 validate+save 后若有任一失败则 `TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()` 回滚整批,实现"全部正确才入库,任一错误则一条都不存"。需重新编译部署 tms-erp-api-ws 后端生效。
|
||||||
|
|
||||||
|
## 国标 GB-T 42820-2023 数据 → cargo-type 导入(分析+转换)
|
||||||
|
- 源文件:`/Users/gxwebsoft/Downloads/副本货物类型导入模板_模拟数据(1).xlsx`,单 Sheet,A1 是标题,真实表头在第 2 行。
|
||||||
|
- 源列:一级编码(A,2位)、一级类型(B)、二级编码(C,仅序号)、二级类型(D)、完整国标编码(E,4位)、一级类型/编码(F)、(G空)、(H标注不可靠)。
|
||||||
|
- 关键规则:二级真实编码 = E 列后 2 位,必须**忽略 C 列序号**;E 前 2 位 = A。116 行全为二级明细,唯一一级 19 个(01-99)。无重码、名称≤14字、无备注。
|
||||||
|
- 映射:系统 6 列模板(`*类型`/`上级货物类型(…)`/`上级货物类型编码(…)`/`*货物类型`/`*货物类型编码`/`备注`)。一级行=类型一级、上级空、名称=B、编码=A;二级行=类型二级、上级名称=B、上级编码=A、名称=D、编码=E。
|
||||||
|
- 已生成预览/导入文件(按系统模板表头,编码列设文本格式):`/Users/gxwebsoft/VUE/tms-erp-web-ws/import-cargo-type/货物类型导入_一级.xlsx`(19 条)、`货物类型导入_二级.xlsx`(116 条)。
|
||||||
|
- 用户确认:一级+二级都导、手动页面上传、测试/空环境直接导。导入动作由用户在系统"货物类型-批量导入"完成(两阶段:先一级后二级)。
|
||||||
|
- 后端导入机制补充(读 CargoTypeServiceImpl.importCargoType):逐行 build→validate→save,挂 @Transactional;二级父级解析走 `findParentByCode` 查库。因同事务同连接,前面已 save 的一级对后续二级查询可见 → **单文件(一级行严格在前、二级在后)也能一次性成功**。但整法"全有或全无":任一失败行触发 setRollbackOnly 回滚整批。分两文件主要规避"顺序依赖+全批回滚"两风险。
|
||||||
|
- 已新增单文件版:`/Users/gxwebsoft/VUE/tms-erp-web-ws/import-cargo-type/货物类型导入_单文件(一级在前).xlsx`(一级19条在前+二级116条在后,共135数据行)。
|
||||||
|
- 美化:对 import-cargo-type 下 3 个 xlsx 表头统一加样式(灰底 #D9D9D9 + 黑色加粗居中 + 细边框 #D0D7E2 + 行高30),数据区细边框、编码列文本格式、列宽适配长表头;表头文字保持不变以兼容后端解析。
|
||||||
|
- 重新美化(按原始模板蓝色主题):提取 Downloads/副本货物类型导入模板_模拟数据(1).xlsx 样式(表头 #0070C0 蓝底白字微软雅黑11居中、数据行 #EBF1F8 浅蓝黑字微软雅黑10、边框 #D9D9D9 细线、表头行高24→因我表头更长设为30、数据行高19),应用到 3 个导入文件。注意:原始第1行是标题"GB-T 42820-2023…",但导入文件必须表头在 row1(后端按 row1 读),故不照搬标题行,只复用表头+数据行样式。
|
||||||
|
|
||||||
|
## 模板下载改静态资源 + 导入两遍法加固(本次)
|
||||||
|
- 模板下载:`cargo-type.vue` 的 `handleTemplate()` 不再用 exceljs 本地生成 2 行示例,改为 fetch 工程内置静态文件 `public/cargo-type-import-template.xlsx`(内容=已生成的标准"单文件一级在前"模板,含一级19类+全部二级示例),下载名仍为 `货物类型导入模板.xlsx`。base 用 `import.meta.env.BASE_URL`(当前 `/`)拼路径,无需后端 export-template 接口、离线可用。
|
||||||
|
- 后端加固(`CargoTypeServiceImpl.importCargoType`):由单遍逐行改为两遍法——第一遍先 validate+save 全部一级,第二遍再解析父级并 save 全部二级;父级解析仍走 `findParentByCode` 查库(第一遍已入库,同事务可见)。目的:让"完整导入"不受 Excel 行顺序影响(即使某二级行排在所属一级之前也不会整批失败);事务边界(`@Transactional` + 任一失败 `setRollbackOnly` 整批回滚)与失败明细 Excel(表头=CargoTypeExcel+导入失败原因,与前端 decorator 列宽 key 完全一致)保持不变。
|
||||||
|
- 注意:后端 Java 改动未在本机编译,需在 tms-erp-api-ws 重新编译部署生效;校验文案(请选择货物类型级别/货物类型编码格式不正确/二级编码前2位必须与上级货物类型编码一致/该货物类型编码已存在/该上级下已存在同名货物类型/货物类型名称不能超过50字符/备注不能超过200字)保持不变以匹配前端 `CARGO_TYPE_FAIL_RULES`。
|
||||||
|
|
||||||
|
## 货物类型页面搜索区紧凑化 + 展开/收缩功能
|
||||||
|
- **文件**: `src/views/base/cargo-type.vue` + `src/option/base/cargo-type.js`
|
||||||
|
- **改动**: 搜索区域紧凑化改造——两个搜索字段(类别、货物类型编码)改为单行并排(searchSpan:12);添加展开/收缩功能,默认收缩状态,通过 searchMenuSlot 在查询/重置按钮后追加「展开/收起」切换按钮(带箭头图标动画);option 配置中 searchShow 默认 false、searchMenuSlot 启用、移除 searchIcon。
|
||||||
|
|
||||||
|
## cargo-type 搜索区重构为自定义紧凑单行搜索栏
|
||||||
|
- **文件**: `src/views/base/cargo-type.vue` + `src/option/base/cargo-type.js`
|
||||||
|
- **改动**: 将 Avue 默认搜索表单完全替换为自定义紧凑单行搜索栏(对齐设计稿截图风格):
|
||||||
|
- 左侧:`el-select` 类型下拉(选择类型)+ `el-input` 搜索输入框(搜索分类名称,带搜索图标按钮)
|
||||||
|
- 右侧:添加分类(主色按钮)、批量导入、批量删除操作按钮
|
||||||
|
- 隐藏 Avue 内置搜索(`searchShow: false`),移除 `menu-left` 插槽中的按钮
|
||||||
|
- 新增 `handleCompactSearch` 方法,支持回车/点击/清除/下拉切换触发搜索,keyword 映射为 `cargoName` 参数
|
||||||
|
- 引入 Element Plus 图标组件(Search, Plus)
|
||||||
|
- 样式:flex 单行布局、轻量阴影 `0 2px 8px rgba(0,0,0,0.06)`、select 160px / input 280px 固定宽度
|
||||||
|
|
||||||
|
## cargo-type 操作按钮恢复(导出/模板)
|
||||||
|
- 紧凑搜索栏重构时误删了「下载模板」「批量导出」两个按钮(原在 menu-left 插槽)。
|
||||||
|
- 已重新加回按钮区:添加分类、批量导入、下载模板(`cargo_type_template`+`handleTemplate`)、批量导出(`cargo_type_export`+`handleExport`)、批量删除。
|
||||||
|
- `handleExport` / `handleTemplate` 方法始终未删,仅缺按钮入口,现已恢复。
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# 2026-07-29 工作日志
|
||||||
|
|
||||||
|
## cargo-type 顶部按钮区优化
|
||||||
|
- **文件**: `src/views/base/cargo-type.vue` + `src/option/base/cargo-type.js`
|
||||||
|
- **改动**:
|
||||||
|
1. 新增按钮文字 `添加分类` → `新增`(与截图一致)
|
||||||
|
2. 为 4 个按钮补齐 Element Plus 图标:批量导入(Upload)、下载模板(Download)、批量导出(Download)、批量删除(Delete),均带 `el-icon--left`
|
||||||
|
3. 图标导入注册:`import { Plus, Upload, Download, Delete }`(移除不再需要的 Search),components 同步注册
|
||||||
|
4. 补充规范样式:`:deep(.basic-container__card) > .el-card__body { padding: 0 }`
|
||||||
|
|
||||||
|
## cargo-type 按钮迁移至 menu-left + 移除紧凑搜索栏(本次)
|
||||||
|
- **用户要求**:功能按钮从自定义紧凑搜索栏迁移到 avue-crud 的 `menu-left` 插槽(表格工具栏右侧),同时删除自定义紧凑搜索栏,恢复 Avue 原生搜索。
|
||||||
|
- **模板改动**:
|
||||||
|
- 删除整个 `.compact-search` 自定义搜索栏 div(含 el-select + el-input + 按钮)
|
||||||
|
- 在 `<avue-crud>` 内新增 `<template #menu-left>` 插槽,放置全部 5 个按钮(新增/批量导入/下载模板/批量导出/批量删除)
|
||||||
|
- **Option 改动**:`searchShow: false` → `searchShow: true`,恢复 Avue 原生搜索表单(类型 radio + 货物类型编码 input)
|
||||||
|
- **清理**:移除 `Search` 图标导入、`handleCompactSearch()` 方法、`.compact-search` 相关样式、隐藏 `.avue-crud__search-btn` 规则
|
||||||
|
- **确认无需改动项**:操作列(220px)、分页组件布局均符合规范
|
||||||
|
|
||||||
|
## cargo-type 导出逻辑修复 + 去重新增按钮(本次)
|
||||||
|
- **导出逻辑**:`buildExportParams()` 移除 `ids` 参数拼接,始终按当前搜索条件导出(不再受勾选行影响)
|
||||||
|
- **去重新增按钮**:Avue 内置 addBtn 由 `validData(permission, false)` 改为 `false`,隐藏内置"新增"按钮;仅保留 `menu-left` 插槽中带图标的"新增"按钮
|
||||||
|
|
||||||
|
## cargo-type 查看/编辑"请求未完成"修复(本次)
|
||||||
|
- **现象**:点击查看/编辑弹窗不打开,提示"请求未完成,请联系管理员"(该文案前端/后端 Java 源码均无字面匹配,疑似网关或全局异常统一包装的报错)。
|
||||||
|
- **根因**:`beforeOpen` 中对 view/edit 调用 `getDetail(this.form.id)` 重新拉取详情,但 `done()` 仅在 `.then` 内调用;该请求一旦失败/超时 → `done()` 不执行 → 弹窗卡死 + 拦截器 toast 报错。且 `getDetail` 本身冗余:列表 `selectCargoTypePage` 已直接返回完整 `CargoTypeVO`(id/typeLevel/parentId/parentCargoName/cargoName/cargoCode/remark 等全字段),Avue 打开弹窗前已把整行填充进 `this.form`。
|
||||||
|
- **修复**:删除 `beforeOpen` 中 view/edit 分支的 `getDetail` 调用,改为直接复用 Avue 已填充的 `this.form`,仅保留 `ensureParentOption(this.form)` + `syncColumnDisplay()` + `done()`;同步移除 `getDetail` 的 import(api 文件中的导出保留勿删)。
|
||||||
|
- **副作用消除**:查看/编辑不再发 detail 请求,彻底规避该接口失败导致的卡死与报错。若后续确需"打开时拉最新数据",需先排查后端 `/blade-system/cargo-type/detail` 为何报错(网关默认只拦 actuator/health,detail 不在拦截名单)。
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
# 2026-07-31 工作记录
|
||||||
|
|
||||||
|
## 货物类型(/base/cargo-type)批量导入「全或无」改造
|
||||||
|
|
||||||
|
- 需求:批量导入时只要任一行报错,整批都不入库,并返回失败明细 Excel。用户在「事务 vs 校验」之间选了"全或无 + 推荐方案"。
|
||||||
|
- 结论(已落地):**全量预校验 + 单事务兜底**,而非纯事务或纯校验二选一。
|
||||||
|
- 后端仓库:`/Users/gxwebsoft/JAVA/tms-erp-api-ws`(BladeX,本仓库 tms-erp-web-ws 是纯前端,无 Java)。
|
||||||
|
- 改动文件:`blade-service/blade-system/.../service/impl/CargoTypeServiceImpl.java`(仅此一文件,未动 Controller/前端)。
|
||||||
|
- 实现要点:
|
||||||
|
- `importCargoType` 拆为两阶段:阶段一 `validateImportAll`(只读,不写库)→ 任一失败直接返回失败明细、整批不入库;阶段二 `saveImportAll` 全部通过后才在 `@Transactional` 内统一写库(兜底防半截入库)。
|
||||||
|
- 补上原缺失的校验:文件内编码全局去重、一级名称去重、二级(同上级)名称去重;二级父级解析同时支持 DB 与"文件内一级映射"(解决"单文件一级在前"场景,二级引用同文件的一级)。
|
||||||
|
- 原代码其实已是 `@Transactional + setRollbackOnly`,但"边校验边写库"且缺文件内去重/父子引用校验;本次重构成更干净的预校验优先。
|
||||||
|
- 前端无需改动:`src/views/base/cargo-type.vue` + `utils/import-excel.js` 已支持失败明细 Excel 下载与 `decorateCargoTypeFailDetail` 装饰。
|
||||||
|
- 验证:本环境无 maven,无法编译;已做静态核对(imports/方法/字段/括号完整,无 TransactionAspectSupport 残留)。需在本地 `mvn -pl blade-service/blade-system compile` 确认。
|
||||||
|
- 遗留小问题(非本次引入,可选优化):后端二级编码前缀报错文案为"货物类型编码前2位必须与上级货物类型编码一致",而前端 `CARGO_TYPE_FAIL_RULES` 匹配的是"二级编码前2位必须与上级货物类型编码一致",二者不完全一致,导致该错误前端不会追加规则说明(仍会显示原因)。可后续对齐文案。
|
||||||
|
|
||||||
|
## cargo-type 分页隐藏「首页/上一页/下一页/尾页」
|
||||||
|
|
||||||
|
- 根因:页面有两套分页——有数据时用 `avue-crud` 自带分页(默认 layout `total, sizes, prev, pager, next, jumper`,原生上一页/下一页来自这里);无数据时用共享组件 `<empty-pagination>`(layout 写死同样带 prev/next)。「首页/尾页」按钮不是 Avue/EP 原生的,而是全局增强器 `src/utils/pagination.js` 的 `setupPaginationEnhancer()`(main.js 第 98 行全局启用)扫描所有 `.el-pagination`,在 `.btn-prev` 前插「首页」、`.btn-next` 后插「尾页」。
|
||||||
|
- 关键机制:增强器只在存在 `.btn-prev/.btn-next` 时才注入首页/尾页。因此去掉分页 layout 里的 prev/next,首页/尾页就不会被注入。
|
||||||
|
- 改动(仅 `src/views/base/cargo-type.vue`,未碰共享组件):① `page` 数据加 `layout: 'total, sizes, pager, jumper'` 处理有数据状态;② 新增 scoped `:deep(.avue-crud__pagination)` 规则隐藏 `.btn-prev/.btn-next/.erp-pagination-first/.erp-pagination-last`,覆盖无数据时的 empty-pagination 状态。
|
||||||
|
- 注意:`empty-pagination` 被 16+ 页面共用,本次未改它的默认 layout,避免影响其它页面。如需其它页面也隐藏,应给该组件加 `layout` prop 而非改默认值。
|
||||||
|
|
||||||
|
## 全站分页统一隐藏「首页/上一页/下一页/尾页」(接上一条)
|
||||||
|
|
||||||
|
- 用户要求全站统一。逐页改不现实(empty-pagination 被 16+ 页共用,还有大量 avue-crud 自带分页),改为**集中两处全局改动**:
|
||||||
|
1. `src/utils/pagination.js`:删除全局增强器对「首页/尾页」按钮的注入(保留「跳转」);同步精简 `updatePaginationButtons`(不再处理 first/last disabled)。
|
||||||
|
2. `src/styles/element-ui.scss`:新增全局规则 `.el-pagination { .btn-prev, .btn-next { display: none !important } }`,统一隐藏原生「上一页/下一页」。
|
||||||
|
- 因此**回退了 cargo-type.vue 之前的两处页面级改动**(`page.layout` 与 scoped 隐藏样式),避免与全局方案两套机制并存;cargo-type 现由全局方案统一接管。
|
||||||
|
- 效果:全站所有 el-pagination(含 avue-crud 自带分页与 empty-pagination)均只保留「总条数/每页条数/页码/跳转」,首页/上一页/下一页/尾页全部隐藏;「跳转」按钮保留。
|
||||||
|
- 风险点:全局 `display:none !important` 会影响所有分页,符合"全站统一"诉求;如后续某页面需恢复导航按钮,需针对该页用更高优先级样式覆盖或单独处理。
|
||||||
|
|
||||||
|
## 全站搜索按钮文案统一
|
||||||
|
|
||||||
|
- **文件**: `src/lang/zh.js`、`src/lang/en.js`
|
||||||
|
- **改动**:
|
||||||
|
- `tagsView.search`: `搜索` → `查询`(中)/ `Search` → `Query`(英)
|
||||||
|
- `crud.filter.resetBtn`: `清空条件` → `重置`
|
||||||
|
- **影响范围**: 全站所有 Avue CRUD 搜索栏按钮自动生效(通过 i18n 全局配置)
|
||||||
|
- **排除**: 地图搜索、日志清空等非 CRUD 场景的按钮文案保持不变
|
||||||
|
|
||||||
|
## 全站操作栏按钮隐藏图标
|
||||||
|
|
||||||
|
- **文件**: `src/styles/element-ui.scss`
|
||||||
|
- **改动**: 新增全局 CSS 规则 `.avue-crud__menu .el-button--text i, .avue-crud__menu .el-button i { display: none !important }`
|
||||||
|
- **效果**: 全站所有 Avue CRUD 操作列(查看/编辑/删除等)按钮前的图标统一隐藏,仅保留文字
|
||||||
|
- **方案选择**: CSS 全局覆盖,无需逐个修改 option 配置文件
|
||||||
|
|
||||||
|
## 全站操作栏按钮紧凑间距
|
||||||
|
|
||||||
|
- **文件**: `src/styles/element-ui.scss`
|
||||||
|
- **改动**: 新增全局规则 `.avue-crud__menu .el-button { margin-left: 4px; margin-right: 0; padding: 0 4px }`
|
||||||
|
- **效果**: 全站 CRUD 操作列按钮统一为 4px 间距 + 4px 内边距,与 cargo-type 一致
|
||||||
|
|
||||||
|
## 全站操作列宽度统一为 160px
|
||||||
|
|
||||||
|
- **目标**: 参考 cargo-type(menuWidth: 160)的紧凑效果,全站统一操作列宽度
|
||||||
|
- **改动**: ① `src/main.js` 全局默认 `menuWidth` 由 220 改为 160;② 批量将 src 下 72 处 option/vue 里显式 `menuWidth: 数字` 统一替换为 `menuWidth: 160`
|
||||||
|
- **影响范围**: 全站所有 Avue CRUD 操作列(含自定义 menu 插槽列,menuWidth 仍控制该列宽度)
|
||||||
|
- **风险**: 原部分页面操作按钮较多(如 tenant/customer-archive 等曾配 380~420),统一 160px 后若按钮数 > 3 可能出现换行/溢出;如某页溢出需单独放宽或精简按钮
|
||||||
|
|
||||||
|
## 全站操作列内容左对齐
|
||||||
|
|
||||||
|
- **文件**: `src/styles/element-ui.scss`
|
||||||
|
- **原因**: cargo-type 用自定义 `#menu` 模板默认左对齐;其他页面用 Avue 内置菜单(viewBtn:true)默认右对齐
|
||||||
|
- **改动**: 新增 `.avue-crud .el-table td .cell.avue-crud__menu { text-align: left }`
|
||||||
|
- **效果**: 全站操作列统一左对齐
|
||||||
|
|
||||||
|
## 顶部工具栏工具全部隐藏
|
||||||
|
|
||||||
|
- **文件**: `src/config/website.js` + `src/page/index/top/index.vue`
|
||||||
|
- **改动**: ① website.js `setting` 新增/关闭 `theme:false`、`debug:false`、`lang:false`、`settingBtn:false`(原 theme/debug 仍为 true);② index.vue 给 `<top-lang>` 加 `v-if="setting.lang"`、`<top-setting>` 加 `v-if="setting.settingBtn"`
|
||||||
|
- **效果**: 顶部工具排仅保留用户头像+姓名下拉(含退出登录);语言切换/设置齿轮/日志/皮肤图标均隐藏
|
||||||
|
- **默认主题**: `src/store/modules/common.js` 第 21 行 `themeName` 默认改为 `theme-d2`(已登录用户若 localStorage 存过旧主题需手动切一次或清存储)
|
||||||
|
|
||||||
|
## 欢迎页(/wel/index)重设计为 TMS-ERP 业务仪表盘
|
||||||
|
|
||||||
|
- **文件**: `src/views/wel/index.vue`(整文件重写,移除 Saber 默认营销内容)
|
||||||
|
- **设计**: 浅色 d2 主题风,纯 SVG/CSS 自绘图表(项目无 echarts,零依赖)
|
||||||
|
- **模块**: ① 问候横幅(用户名+日期+新建运单按钮) ② 4 张核心指标卡(今日运单/在途运输/待办事项/运力车辆,含环比) ③ 运单趋势(SVG柱状,近7日) ④ 货物类型分布(SVG环形donut) ⑤ 运输方式占比(横向条) ⑥ 预警提醒列表 ⑦ 快捷入口网格(跳真实路由)
|
||||||
|
- **数据**: 全 mock,data() 中每项标注对应后端接口(如 /blade-business/waybill/count、/trend、/cargoStat 等),后续替换真实接口即可
|
||||||
|
- **预览**: `npm run build` 通过;dev server `http://localhost:2889/`
|
||||||
|
- **注意**: 快捷入口 path 依据 views 目录结构推断(/business/waybill-manage 等),需与后端菜单 path 一致才能跳转
|
||||||
|
|
||||||
|
## 图标悬停圆形背景效果示例
|
||||||
|
|
||||||
|
- 按用户提供的两张截图(T 恤默认图标 / 铃铛带圆形背景图标),实现了一个悬停时显示浅灰圆形背景的图标按钮效果。
|
||||||
|
- 产物: `/Users/gxwebsoft/VUE/tms-erp-web-ws/icon-hover-demo.html`(含 CSS 核心代码与 Element Plus 用法示例)。
|
||||||
|
|
||||||
|
## 顶栏 T 恤图标悬停效果落地
|
||||||
|
|
||||||
|
- 用户要求把悬停圆形背景效果运用到顶部状态栏的 T 恤图标(实际指向主题图标位置 `<i class="icon-zhuti">`)。
|
||||||
|
- 改动文件: `src/page/index/top/top-theme.vue`
|
||||||
|
- 模板:将 `<i class="icon-zhuti" @click="open">` 替换为内联 SVG T 恤(保留 `@click="open"` 打开主题选择弹窗)。
|
||||||
|
- 样式:新增 scoped `.top-theme-icon`,`::before` 浅灰圆形背景 `#f2f3f5`,默认 `scale(0.6) opacity(0)`,hover/focus 弹性放大到 `scale(1)`,图标 `z-index:1` 压在上层。
|
||||||
|
- 调整:用户反馈 T 恤图标在顶栏中上下不居中,把 `.top-theme-icon` 由 `display:flex; height:100%` 改为 `display:inline-flex; vertical-align:middle; width:32px; height:32px`,使其与右侧头像/用户名在同一水平中线。
|
||||||
|
- 注意:该组件受 `website.setting.theme` 控制(之前曾设为 false 隐藏),若顶栏未显示主题图标,需确认 website.js 中 `setting.theme` 为 true。
|
||||||
|
|
||||||
|
## 顶栏头像触发下拉菜单(hover)
|
||||||
|
|
||||||
|
- **需求**: 鼠标移到头像(top-bar__img)也能触发下拉,行为与点用户名一致。
|
||||||
|
- **文件**: `src/page/index/top/index.vue`
|
||||||
|
- **改动**:
|
||||||
|
1. 把 `<img class="top-bar__img" :src="userInfo.avatar">` 从 el-dropdown 外的同级节点,移入 el-dropdown 触发区(`<span class="el-dropdown-link">`)内。
|
||||||
|
2. 给 `<el-dropdown>` 加 `trigger="hover"`,让头像+姓名整体支持 hover 触发。
|
||||||
|
3. scoped 样式新增 `.top-user__trigger`(inline-flex / padding 4px 8px / hover 浅灰背景 rgba(0,0,0,0.04)),并把 `.top-bar__img` 自身 `margin` 改为 `0 5px 0 0` 配合新容器。
|
||||||
|
- **效果**: 鼠标悬停在头像或姓名任一处都会展开下拉;保留 `:hide-on-click="false"`(菜单内点击不收起,el-tooltip 等仍可交互)。
|
||||||
|
- **注意**: 若用户后续希望「hover 触发」仅作用于头像、不影响姓名点击行为,需把姓名部分移出 el-dropdown 触发区;当前实现是统一行为。
|
||||||
|
|
||||||
|
## 全站 avue-crud 弹窗内部滚动高度统一
|
||||||
|
|
||||||
|
- **需求**:/business/process-config 与 /base/cargo-type 表单弹窗滚动高度不一致(process-config 用 dialogWidth:'96%'+dialogTop:'10px' 且内容很长被撑爆;cargo-type 用 dialogWidth:760 窄窗),要求全站统一。
|
||||||
|
- **关键结论**:@smallwei/avue 3.9.2 的 avue-crud **没有 `dialogHeight` 属性**(源码 avue.js 中无此字符串;dialogTop 默认值为 "0")。逐个 option 设 dialogHeight 不可行,故延续本项目「全局 CSS 统一」范式(见上「全站操作栏按钮」等先例)。
|
||||||
|
- **改动文件**:`src/styles/element-ui.scss`(全局生效,被 common.scss 通过 `@use './element-ui.scss'` 引入)
|
||||||
|
- **新增规则**:`.avue-crud__dialog .el-dialog__body { max-height: calc(100vh - 200px); overflow-y: auto; }`
|
||||||
|
- **效果**:全站所有 Avue CRUD 弹窗(含 process-config、cargo-type)的 body 在 `calc(100vh - 200px)` 内滚动,内容超长时在窗内滚动而非撑爆整窗;矮弹窗不受影响(max-height 仅为上限)。
|
||||||
|
- **验证**:已 `pnpm build` 后台运行验证全站编译。
|
||||||
|
|
||||||
|
## 顶部状态栏标题区加背景条
|
||||||
|
|
||||||
|
- **需求**:给顶部状态栏(`.top-bar__title` 中间区域)加一条背景条,与白色头部区分。
|
||||||
|
- **文件**:`src/styles/top.scss` 的 `.top-bar__title` 规则(原来背景是纯白 `#fff`,看不出边界)
|
||||||
|
- **改动**:`background: rgba(64,158,255,0.12)`(品牌色淡底)+ `border-left: 4px solid #409eff`(左侧主色竖条),沿用项目主色 `#409eff`;文字保持深色 `rgba(0,0,0,0.85)`、搜索框可读。
|
||||||
|
- **注意**:默认 light 主题(go/d2 等白底头部)效果好;dark 主题头部 `#2c3643` 下淡蓝条可能略冲突(原 `#fff` 背景在 dark 下本就冲突,属历史遗留),如需可给 dark 主题单独覆盖。
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# 2026-08-04 工作记录
|
||||||
|
|
||||||
|
## 撰写《AI 辅助编程规范与实战示例》文档
|
||||||
|
- 应业主想了解"如何用 AI 编程"的需求,以 `src/views/base/cargo-type.vue`(货物类型模块)为实例,撰写对外说明文档。
|
||||||
|
- 产出文件:`/Users/gxwebsoft/VUE/tms-erp-web-ws/AI辅助编程规范与实战示例.md`
|
||||||
|
- 文档结构:协作流程总览 → AGENTS.md 规范条款 → cargo-type 模块实例拆解(三级文件分离、校验三处一致、审计字段用姓名、导入失败明细加工、样式规范)→ 人机协作人工兜底项 → 5 条经验 → 给业主的价值建议。
|
||||||
|
- 文档引用了真实代码片段:cargo-type.vue / option / api / utils/import-excel.js,强调需求卡 + AGENTS.md + 复用 utils 三件套作为"单一事实来源"。
|
||||||
|
|
||||||
|
## 生成 Word(.docx) 版本
|
||||||
|
- 用 docx-js(managed node workspace)将《AI 辅助编程规范与实战示例.md》转成 Word 文档:`/Users/gxwebsoft/VUE/tms-erp-web-ws/AI辅助编程规范与实战示例.docx`。
|
||||||
|
- 踩坑:docx-js 会在段落间注入非法空节点 `<0/>`(`<N/>`),导致 XML 不合法;后处理用正则剔除 `<\d+/>` 后重新校验通过。
|
||||||
|
- 生成脚本:`/Users/gxwebsoft/.workbuddy/binaries/node/workspace/gen_doc.js`(含标题/表格/代码块/项目符号样式)。
|
||||||
|
|
||||||
|
## 分页组件:上一页/下一页改为原生箭头图标
|
||||||
|
- 需求:全站不隐藏上一页/下一页,改显示原生箭头图标;首页/尾页保持不注入。
|
||||||
|
- 改动文件:`src/styles/element-ui.scss`(3 处历史遗留规则)、`src/utils/pagination.js`(注释同步)。
|
||||||
|
- 删除的遗留规则:①隐藏原生 `.el-icon` 箭头;②用 `::before` 注入「上一页/下一页」文字;③整体 `display:none` 隐藏 `.btn-prev/.btn-next`。首页/尾页仍由 pagination.js 增强器不注入,且各页面 layout 无 first/last。
|
||||||
|
- 效果:上一页/下一页显示 Element Plus 原生箭头(‹ ›),带边框与页码按钮视觉统一,hover 变主色,禁用态灰显。
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# 2026-08-05 工作记录
|
||||||
|
|
||||||
|
## 排查:改线上接口后前端"运行不了"(CORS 根因)
|
||||||
|
|
||||||
|
- 现象:用户将 `.env.development` 的 `VITE_APP_API` 改为 `http://172.16.203.228:8000/api`(直连线上/测试后端)后仍运行不了。
|
||||||
|
- 根因:Saber3 的 axios 配了 `withCredentials = true`(`src/axios.js:79`)。该后端(openresty + blade-gateway)对跨域请求返回 `Access-Control-Allow-Origin: *` 且 `Access-Control-Allow-Credentials: true`,浏览器规定 credentials 模式下 Allow-Origin 不能是 `*`,非法组合会被直接拒绝 → 所有接口失败。
|
||||||
|
- 之前用 `/api` 能跑是因为走 Vite 代理,浏览器请求同源 localhost:2888,不触发跨域。
|
||||||
|
- 已改动:
|
||||||
|
1. `.env.development` 的 `VITE_APP_API` 改回 `/api`,并加注释说明两种模式。
|
||||||
|
2. `vite.config.mjs` 的 `server.proxy['/api']` target 改为 `http://172.16.203.228:8000`,并去掉 `rewrite` 去掉 `/api` 的逻辑(保留 `/api` 前缀,对齐 openresty 期望路径)。
|
||||||
|
- 注意:改 `.env` / `vite.config.mjs` 后必须重启 `npm run dev`(Vite 启动时加载环境变量,运行中改动不会热更新)。
|
||||||
|
- 若确需直连绝对地址(如生产/部署):必须让后端 CORS 把 Allow-Origin 改为具体前端域名(blade-gateway 的 CorsConfig 用 `setAllowedOriginPatterns` 或显式域名),不可用 `*` 配合 credentials。
|
||||||
|
|
||||||
|
## 调整:货物类型导入失败原因展示格式
|
||||||
|
|
||||||
|
- 文件:`src/views/base/cargo-type.vue`
|
||||||
|
- 修改 `decorateCargoTypeFailDetail`:
|
||||||
|
- 移除「第N行:」前缀;
|
||||||
|
- 失败原因编号从 `A. B. C.` 改为 `1. 2. 3.`;
|
||||||
|
- 备注超长兜底提示也同步使用数字编号。
|
||||||
|
|
||||||
|
## UI 优化:客商档案弹窗「基本信息」分组(vehicle/customer-archive.vue)
|
||||||
|
|
||||||
|
- 背景:原弹窗「基本信息」是一整块 `el-row` 平铺 20 个字段,信息密度过高。
|
||||||
|
- 方案(与用户确认):采用「分区标题平铺 + 浅色背景块」而非嵌套卡片(契合 AGENTS.md 去卡片化哲学;全局 `.dialog-section-title` 已自带 4px 主色竖条)。
|
||||||
|
- 改动:
|
||||||
|
- 模板:将单一 `el-row` 拆分为 4 个 `.archive-form__group`:工商信息 / 联系信息(3项 span=8) / 财务·信用信息 / 其他信息(备注 span=24);字段顺序重组但全部保留,地址、备注保持 span=24。
|
||||||
|
- 样式:新增 `.archive-form__group`(#fafafa 背景、8px 圆角、16px/20px 内边距、组间距 16px),子标题复用 `.dialog-section-title`(14px/500)。
|
||||||
|
- 校验:@vue/compiler-sfc 解析 0 错误;标签闭合平衡。
|
||||||
|
- 注:此前讨论中提及的「Tab 内容加内边距」「材料区标题竖条」未纳入本次(材料区已用 dialog-section-title);如需可后续追加。
|
||||||
|
|
||||||
|
## 排查:分页「上一页/下一页原生箭头」样式被还原
|
||||||
|
|
||||||
|
- 现象:昨日(08-04)已对 `src/styles/element-ui.scss` 三处历史遗留规则做删除(隐藏 `.el-icon` 箭头、用 `::before` 注入「上一页/下一页」文字、整体 `display:none` 隐藏 `.btn-prev/.btn-next`),今早用户反馈效果被覆盖。
|
||||||
|
- 排查:重新 Grep 确认 `element-ui.scss` 三处原隐藏规则全部恢复成原始状态;而 `src/utils/pagination.js` 的注释("统一展示为原生箭头图标")未被还原,说明只有该样式文件被外部覆盖(疑似 git 还原 / 重新拉取 / 某生成流程),并非逻辑层问题。
|
||||||
|
- 已重新逐条应用三处删除(单条提交,避开并行编辑的缓存冲突),并验证无遗留 `display:none`/`content:'上一页'`;`pagination.js` 无需再改。
|
||||||
|
- 提醒:`element-ui.scss` 若处于版本控制或生成流程中,可能被再次还原;如需长期生效,建议把该改动提交入 git,或排查是否有自动覆盖该文件的脚本。
|
||||||
|
|
||||||
|
## UI 调整:Avue CRUD 弹窗内容区背景改为灰色 #f5f6fa
|
||||||
|
|
||||||
|
- 文件:`src/styles/element-ui.scss` 第 225 行附近 `.avue-crud__dialog .el-dialog__body`
|
||||||
|
- 改动:在原 `max-height`/`overflow-y` 规则中新增 `background: #f5f6fa;`
|
||||||
|
- 范围:**全局生效**(所有 Avue CRUD 弹窗的内容区都会变灰)。起因是用户想在货物类型(cargo-type)新增弹窗用灰底衬托。
|
||||||
|
- 注意:弹窗默认 `append-to-body`,scoped 样式选不到;如需仅货物类型单页变灰,需给 option 加 `dialogCustomClass` 再单独写选择器。用户未要求收窄,暂保持全局。
|
||||||
|
|
||||||
|
## UI 改造:project-apply 弹窗灰底+白卡片布局
|
||||||
|
|
||||||
|
- 文件:`src/views/business/project-apply.vue`
|
||||||
|
- 需求:弹窗背景色 `#f5f6fa`,每个分组区域用白色卡片
|
||||||
|
- 改动:
|
||||||
|
- 弹窗 body(global 选择器):加 `background: #f5f6fa`
|
||||||
|
- `__grid`、`__table`、`__textarea-list`、`__material-head`、`__upload`、`__change-textarea` 统一加白底 + 6px 圆角 + 极淡阴影 + 内边距
|
||||||
|
- 效果:灰蓝底弹窗上浮白色分组卡片,与客商档案弹窗的「灰色分组块+白色内层卡片」方案一致
|
||||||
|
|
||||||
|
## 二次优化:project-apply 分区标题也包进白色卡片
|
||||||
|
|
||||||
|
- 文件:`src/views/business/project-apply.vue`
|
||||||
|
- 需求:把「项目基本信息」这类分区标题也装进白色卡片内(标题+内容合一张白卡)
|
||||||
|
- 改动:
|
||||||
|
- `.dialog-section-title`(排除 material 标题)加白底 + 上圆角 `6px 6px 0 0` + `z-index:1`,紧接内容块
|
||||||
|
- `__grid`/`__table`/`__textarea-list`/`__change-textarea` 圆角改为下圆角 `0 0 6px 6px`,与标题拼接成完整白卡
|
||||||
|
- 材料区(`__material-head`/`__upload`)标题与按钮本就在白卡内,不受影响
|
||||||
|
- 纯 CSS 改动,未动模板结构;用 `:not()` 排除材料标题避免破坏既有布局
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
# 2026-08-06 工作日志
|
||||||
|
|
||||||
|
## 全站表格操作列文字链接统一为蓝色
|
||||||
|
- 需求:客户档案页(/vehicle/customer-archive)操作列颜色统一蓝色,且全站统一风格。
|
||||||
|
- 实现:在 `src/styles/element-ui.scss` 新增全局规则,覆盖 Avue `#menu` 槽位(`.avue-crud__menu .el-link`)与手写表格操作列(`.el-table .el-table__body .cell:has(.el-link) .el-link`)中所有 `el-link` 的 `--el-link-text-color` / hover / active / disabled 变量为主色 `#409eff` 及其浅/深色,从而把 warning/success/danger 等类型强制转蓝。
|
||||||
|
- 单点生效,无需逐页改 `type`。dev server(2888) 已运行会热更新。
|
||||||
|
- 注意:选择器以「表格体内含 el-link 的单元格」为目标,本仓库表格内 el-link 仅用于操作列,无副作用;若未来某数据单元格用 el-link 作强调色需另行排除。
|
||||||
|
|
||||||
|
## 运输计划调度弹窗 UX 分析
|
||||||
|
- 完成 `/business/transport-plan` 调度弹窗的 UX 审查,对比 project-apply 页面风格
|
||||||
|
- 输出 6 项改进方案(P0-P5),核心问题为缺少灰底(#f5f6fa)+白卡(section-card)分组设计模式
|
||||||
|
- 所有改动集中在 `business-crud-page.vue` 一个文件
|
||||||
|
- 待用户确认后实施第一阶段(P0灰底 + P1白卡分组)
|
||||||
|
|
||||||
|
## 已实施的改进(全部在 business-crud-page.vue)
|
||||||
|
- P0: `__dispatch-dialog` 的 `.el-dialog__body` 改为 `padding:16px; background:#f5f6fa; overflow-y:auto`(原 padding:0)
|
||||||
|
- P1: 调度弹窗三块分区用全局 `<section-card>` 包裹:基本信息 / 收发货路线 / 待调度列表;"新增"按钮+汇总文字移入列表卡 `#extra` slot
|
||||||
|
- P2: `__dispatch-route-line` 由黑色实线改为 `#409EFF` 虚线 + 箭头(主色方向感)
|
||||||
|
- P3: 待调度列表纳入白卡容器(随 P1 完成)
|
||||||
|
- P4: 调度表操作列 `width` 180→220 预留扩展
|
||||||
|
- P5: `__dispatch-footer` 增加 `border-top` + padding/margin-top 分隔
|
||||||
|
- 旧 `__dispatch-list` / `__dispatch-top`(border-bottom/32px padding) 样式已清理;section-card 已全局注册,无需 import
|
||||||
|
- 验证:SFC 模板单独编译通过(6 处 section-card 引用配对平衡);完整 vite build 仍因预存 `xlsx` 幽灵依赖(pnpm 未装)失败,与本次改动无关
|
||||||
|
- 注意:本项目完整 build 依赖 `xlsx@0.18.5`(package.json 有声明但 node_modules 缺失),属环境预存问题,非本次引入
|
||||||
|
|
||||||
|
## 运输计划调度弹窗二次视觉紧凑化
|
||||||
|
- 客户反馈:弹窗“空、不紧凑、内容多但不好看”
|
||||||
|
- 改动仍集中在 `business-crud-page.vue`
|
||||||
|
- 模板:
|
||||||
|
- 标题与单号/状态标签合并到同一行,移除 `|` 分隔符
|
||||||
|
- 基本信息与收发货路线由左右双列改为上下堆叠的白卡,各自独占一行
|
||||||
|
- 基本信息网格改为 4 列:客户 / 项目 / 合同编号 / 计划执行时间;货物信息占 2 列;附件独占一行
|
||||||
|
- 日期展示由 `-` 改为 ` ~ `,更明确是区间
|
||||||
|
- 路线改为“横向节点 + 下方双列明细”:起点/终点徽章+名称居中,发货地址/收货地址左对齐分两列,避免居中留白
|
||||||
|
- 样式:
|
||||||
|
- 顶部标题字号 22px→18px,顶部 padding 22px→0
|
||||||
|
- 信息标签字号 14px→12px、颜色 #666→#909399,值行高 1.45→1.4
|
||||||
|
- 基本信息网格 3 列/48px 列间距/18px 行间距 → 4 列/24px 列间距/12px 行间距
|
||||||
|
- 路线徽章 46px→32px、字号 16px→13px、圆角 6px→4px
|
||||||
|
- 路线标题字号 22px→16px,取消居中,最长名称省略
|
||||||
|
- 路线区域取消 `min-height:200px` 和居中,改为紧凑堆叠
|
||||||
|
- 卡片间统一 `margin-bottom: 12px`
|
||||||
|
- 验证:`npm run build` 成功
|
||||||
|
|
||||||
|
## /business/project-apply 主表操作列收窄
|
||||||
|
- 需求:project-apply 主表(avue-crud #menu)操作列太宽,缩小一点。
|
||||||
|
- 原:`buildTableOption()` 调用 `applyTableMenuWidth(option, 4)` → 强制 `menuWidth: 320`(four 档),因按钮数≥4。
|
||||||
|
- 改动:`src/views/business/project-apply.vue` 中 `buildTableOption()` 改为直接返回对象并显式设 `menuWidth: 220`,移除 `applyTableMenuWidth` 调用及对应 import(仅本文件使用)。
|
||||||
|
- 说明:220 低于 AGENTS.md「>3 按钮≥320px」约定,但属用户明确要求;操作列链接已全局支持 flex-wrap,按钮多时会自动换行,不会裁切。
|
||||||
|
- 影响范围:仅 project-apply 主表;子表(附件/变更/用户权限)操作列 width 维持 110/100/100 不变;全局 `TABLE_MENU_WIDTH` 未改。
|
||||||
|
|
||||||
|
## /business/temporary-credit-limit 操作列宽度固定为 180
|
||||||
|
- 需求:该页操作列宽度也给 180。
|
||||||
|
- 背景:该页用共享组件 `business-crud-page.vue`,其 `option` 在 data() 中由 `cloneOption(crudOption, estimateMenuButtonCount(config))` 生成,菜单宽度按按钮数估算(≥4 按钮→320),option 文件本身无 menuWidth。
|
||||||
|
- 改动:
|
||||||
|
1. `business-crud-page.vue` 新增可选 prop `menuWidth`(Number,默认 null);data() 中生成 `option` 时若 `menuWidth != null` 则强制覆盖 `opt.menuWidth`。默认 null 不影响其他页面。
|
||||||
|
2. `temporary-credit-limit.vue` 调用处加 `:menu-width="180"`。
|
||||||
|
- 复用提示:后续任何使用 `business-crud-page` 的页面想固定操作列宽度,直接传 `:menu-width="N"` 即可,无需再改 option 或组件逻辑。
|
||||||
|
- 注意:180 同样低于 AGENTS「>3 按钮≥320」约定,按用户明确要求;链接支持换行兜底。
|
||||||
|
|
||||||
|
## /base/common-address 操作列宽度 220 → 180
|
||||||
|
- 需求:「这个也调一下宽度」。上一条刚把 temporary-credit-limit 设为 180,用户用「也」字,故对齐为 180。
|
||||||
|
- 机制:该页是独立 `avue-crud`,`option` 来自 `@/option/base/common-address.js` 的 `createOption()`,其中已显式设 `menuWidth: 220`。
|
||||||
|
- 改动:将 `src/option/base/common-address.js` 的 `menuWidth: 220` → `180`。操作列仅 3 个链接(查看/编辑/删除)。
|
||||||
|
- 说明:低于 AGENTS「>3 按钮≥320」约定,按用户明确要求;链接支持换行兜底。
|
||||||
|
|
||||||
|
## 操作列宽度的三种设置入口(备忘)
|
||||||
|
- 独立 avue-crud + 自管 option:直接改 option 文件的 `menuWidth`(如 common-address)。
|
||||||
|
- 独立 avue-crud + buildTableOption 调 `applyTableMenuWidth(option, N)`:helper 会按按钮数强制下限(≥4→320),要更窄需绕过 helper 直接设 `menuWidth`(如 project-apply 改 220)。
|
||||||
|
- 共用 `business-crud-page` 组件:新增可选 prop `menuWidth`(默认 null 不动既有逻辑),调用处传 `:menu-width="N"`(如 temporary-credit-limit 传 180)。
|
||||||
|
|
||||||
|
## /business/process-config 操作列宽度 320 → 180
|
||||||
|
- 需求:「这个也调一下宽度」。延续上几条把操作列设 180 的系列要求。
|
||||||
|
- 机制:该页是独立 `avue-crud`,`option` 来自 `@/option/business/process-config.js`,原先包在 `applyTableMenuWidth({...}, 5)` 中(five 档→强制 `menuWidth: 320`,因菜单有 6 个链接:查看/编辑/复制/启用/停用/删除)。
|
||||||
|
- 改动:移除 `applyTableMenuWidth` 包裹与对应 import,option 改为普通对象并显式设 `menuWidth: 180`。
|
||||||
|
- 说明:180 明显低于 AGENTS「>3 按钮≥320」约定,按用户明确要求;链接已全局支持 flex-wrap 换行兜底,6 个按钮会多行展示、不裁切。
|
||||||
|
|
||||||
|
## 修复 process-config.js 末尾语法错误(红色警)
|
||||||
|
- 现象:用户反馈 `src/option/business/process-config.js` 最后一行有红色警。
|
||||||
|
- 根因:上一轮把 `applyTableMenuWidth({...}, 5)` 去掉外层函数包裹时,结尾 `}, 5);` 被改成 `});`,多留了一个 `)`(因 `export const option = {` 本身没有左括号),导致括号不匹配。
|
||||||
|
- 改动:末尾 `});` → `};`。移除 `applyTableMenuWidth` import 时无误。
|
||||||
|
- 验证:node 动态 import 不再报 SyntaxError(仅剩 node 无法解析 extensionless `./common` 的模块解析提示,与语法无关),说明语法已恢复。
|
||||||
|
|
||||||
|
## /vehicle/insurance-record 操作列宽度 160 → 140(紧凑)
|
||||||
|
- 需求:「这个也调一下宽度,紧凑一点」。本页与上几条不同,用户明确要求更紧凑(其他页给的 180 比这页还宽)。
|
||||||
|
- 机制:独立 `avue-crud`,`option` 来自 `@/option/vehicle/insurance-record.js`;无自定义 `#menu` 槽,行菜单用 Avue 内置(仅显式 `viewBtn: true`,edit/del 默认开启→3 个链接:查看/编辑/删除)。原已显式 `menuWidth: 160`。
|
||||||
|
- 改动:将 `menuWidth: 160` → `140`。
|
||||||
|
- 说明:低于 AGENTS「>3 按钮≥320」约定,按用户明确要求;链接已全局支持 flex-wrap 换行兜底。
|
||||||
|
|
||||||
|
## 车/船务管理模块下所有记录页操作列宽度统一为 160
|
||||||
|
- 需求:用户截图列出「车/船务管理」下 11 个子菜单,要求操作栏宽度都设为 160。
|
||||||
|
- 对应页面/option 文件:
|
||||||
|
- 保险记录 insurance-record:option → `src/option/vehicle/insurance-record.js`(由 140 改回 160)
|
||||||
|
- 违章记录 violation-record:option → `src/option/vehicle/violation-record.js`(原 160,无需改动)
|
||||||
|
- 维修记录 maintenance-record:inline option → `src/views/vehicle/maintenance-record.vue`(原 160,无需改动)
|
||||||
|
- 保养记录 maintenance-plan:inline option → `src/views/vehicle/maintenance-plan.vue`(原 160,无需改动)
|
||||||
|
- 换胎记录 tire-replacement-record:option → `src/option/vehicle/tire-replacement-record.js`(原 160,无需改动)
|
||||||
|
- 事故记录 accident-record:option → `src/option/vehicle/accident-record.js`(原 160,无需改动)
|
||||||
|
- 年检记录 annual-inspection-record:option → `src/option/vehicle/annual-inspection-record.js`(原 160,无需改动)
|
||||||
|
- 里程记录 mileage-record:option → `src/option/vehicle/mileage-record.js`(原 160,无需改动)
|
||||||
|
- 变更记录 transport-change-record:option → `src/option/vehicle/transport-change-record.js`(原 160,无需改动)
|
||||||
|
- 油电记录 oil-electric-record:option → `src/option/vehicle/oil-electric-record.js`(原 160,无需改动)
|
||||||
|
- ETC记录 etc-record:option → `src/option/vehicle/etc-record.js`(原 160,无需改动)
|
||||||
|
- 实际改动:仅 `insurance-record.js` 从 140 恢复到 160,其余 10 个已经是 160。
|
||||||
|
- 注意:`src/option/vehicle/other-expense-record.js` 不在截图菜单内,保持不动。
|
||||||
|
|
||||||
|
## 车/船务管理模块操作列宽度 160 → 180(用户反馈 160 偏小)
|
||||||
|
- 需求:上一轮统一 160 后,用户觉得还是小,全部改成 180。
|
||||||
|
- 改动:11 个文件 `menuWidth: 160` → `180`(9 个 option 文件 + maintenance-record.vue / maintenance-plan.vue 两处 inline option)。
|
||||||
|
- 现状:车/船务管理模块全部 11 个记录页操作列均为 180。
|
||||||
|
|
||||||
|
## 操作列文字链接间距过宽问题
|
||||||
|
- 现象:用户截图反馈操作列文字链接之间间距太宽,要求统一风格。
|
||||||
|
- 根因:全局已设 `.avue-crud .avue-crud__menu { gap: 8px; }` 与手写表格操作列 `gap: 8px`,但 Avue 默认 `.el-link` 或 `.cell` 上可能存在 `margin-right`,与 gap 叠加后显得过宽。
|
||||||
|
- 改动:`src/styles/element-ui.scss` 新增规则,对 `.avue-crud .avue-crud__menu .el-link` 和手写表格操作列 `.el-link` 强制 `margin: 0`,使间距完全由 8px gap 控制,不再叠加。
|
||||||
|
- 影响范围:全站表格操作列(Avue #menu 与手写操作列),仅作用于操作列链接,不修改普通数据单元格。
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
# 2026-08-07 工作日志
|
||||||
|
|
||||||
|
## 修复车辆/船舶/司机管理「所属组织」列前导空格
|
||||||
|
- 症状:车辆管理列表截图中「所属组织」列值左侧有明显缩进空格。
|
||||||
|
- 根因:组织下拉选项构建时把带层级缩进(全角空格 `' '.repeat(level)`)的字符串同时作为 `label` 和 `value`,保存到后端后表格直接回显该值,导致前导空格。
|
||||||
|
- 改动:
|
||||||
|
- `src/views/transportCapacity/vehicle.vue`、`ship.vue`、`driver.vue`
|
||||||
|
- `flattenDeptOptions` / `formatDeptOptions`:`value` 改为无缩进的 `rawLabel`,`label` 仍保留缩进用于下拉层级展示。
|
||||||
|
- 表格增加 `#organizationName` 插槽,对 `row.organizationName` 去除前导半角/全角空白,兼容已入库的旧数据。
|
||||||
|
- `src/option/transportCapacity/transport-vehicle.js`、`transport-ship.js`、`driver.js`
|
||||||
|
- 「所属组织」列增加 `slot: true`,使自定义插槽生效。
|
||||||
|
- 验证:通过 `git diff` 确认 6 个文件改动正确。
|
||||||
|
|
||||||
|
## 调整司机管理到期筛选标签位置
|
||||||
|
- 需求:截图反馈司机管理页「全部 / 30天内到期 / 已到期」三个筛选标签放在搜索表单右侧,占位突兀,希望与搜索/重置按钮同排或放到更合适位置。
|
||||||
|
- 方案:参考 `business-crud-page.vue` 的 `#search-menu` 插槽,把筛选标签移到搜索菜单行(搜索/重置按钮同一排),标签居左、按钮居右。
|
||||||
|
- 改动:
|
||||||
|
- `src/views/transportCapacity/driver.vue`
|
||||||
|
- `#expireStatus-search` 插槽改为 `#search-menu` 插槽。
|
||||||
|
- `.driver-page__expiry-tags` 增加 `order: -1`、`margin-right: auto`、`min-width: 0`,移除 `width: 100%` 和不再需要的响应式媒体查询。
|
||||||
|
- `src/option/transportCapacity/driver.js`
|
||||||
|
- 删除仅作为占位用的 `expireStatus` 隐藏搜索字段(`search: true`、`searchSpan: 6` 等)。
|
||||||
|
- 说明:筛选逻辑仍通过 `query.expireStatus` + `handleExpireChange` 工作,不受字段删除影响;车辆/船舶管理页有相同布局,如需统一可复用该改法。
|
||||||
|
- 验证:通过 `git diff` 确认改动正确。
|
||||||
|
|
||||||
|
## 运输计划调度弹窗恢复左右分栏布局
|
||||||
|
- 需求:客户提供「计划详情」参考截图,要求调度弹窗改回「左侧基本信息卡 + 右侧收发货路线卡」的左右分栏布局。
|
||||||
|
- 改动:`src/views/business/components/business-crud-page.vue`
|
||||||
|
- 模板:把基本信息与收发货路线重新包进 `business-crud-page__dispatch-summary` 双列容器;左侧 `section-card` 内恢复 3 列网格(客户/项目/合同编号),货物信息占 2 列、计划执行时间占 1 列、附件独占一行;右侧 `section-card` 内恢复左右排列的起点/终点(徽章+名称+地址+联系人),中间用方向虚线连接。
|
||||||
|
- 样式:
|
||||||
|
- 外容器恢复 `grid-template-columns: minmax(0, 1.25fr) minmax(420px, 1fr)`,gap 16px,卡片等高拉伸。
|
||||||
|
- 基本信息网格恢复 3 列,列间距 16px、行间距 12px。
|
||||||
|
- 路线区恢复 `grid-template-columns: 1fr 56px 1fr`,徽章 32px,标题 16px,地址/联系人 13px,连接线 56px。
|
||||||
|
- 保留紧凑化后的字号/间距(标签 12px、值 14px),避免回到原先过松的状态。
|
||||||
|
- 验证:`npm run build` 成功。
|
||||||
|
|
||||||
|
## 运输计划调度弹窗基本信息改为 2 列
|
||||||
|
- 需求:客户截图反馈左侧「基本信息」还嫌松,要求改为 2 列更紧凑。
|
||||||
|
- 改动:`src/views/business/components/business-crud-page.vue`
|
||||||
|
- 模板:调整基本信息字段顺序为 客户/项目、合同编号/计划执行时间、货物信息(占满 2 列)、附件(占满 2 列)。
|
||||||
|
- 样式:`business-crud-page__dispatch-summary-grid` 从 `repeat(3, minmax(0, 1fr))` 改为 `repeat(2, minmax(0, 1fr))`,保持 gap 12px 16px。
|
||||||
|
- 验证:`npm run build` 成功。
|
||||||
|
|
||||||
|
## 运输计划调度弹窗路线连接改为车辆图标
|
||||||
|
- 需求:客户希望把收发货路线中间的方向虚线换成车辆图标。
|
||||||
|
- 改动:`src/views/business/components/business-crud-page.vue`
|
||||||
|
- 模板:把 `<div class="business-crud-page__dispatch-route-line" />` 替换为 `<div class="business-crud-page__dispatch-route-vehicle"><el-icon><Van /></el-icon></div>`。
|
||||||
|
- 样式:删除原 `&__dispatch-route-line` 虚线箭头样式,新增 `&__dispatch-route-vehicle`,使用主色圆形背景、白色 `Van` 图标,尺寸 36×36px,居中与路线节点对齐。
|
||||||
|
- 验证:`npm run build` 成功。
|
||||||
|
|
||||||
|
## 运输计划调度弹窗收发货路线视觉美化
|
||||||
|
- 需求:客户反馈右上角「收发货路线」卡片仍不够美观,要求继续优化。
|
||||||
|
- 改动:`src/views/business/components/business-crud-page.vue`
|
||||||
|
- 样式:
|
||||||
|
- 路线容器去掉 `padding-top`,改为 `align-items: center` 垂直居中。
|
||||||
|
- 起点/终点 item 改为 `align-items: center`,徽章从方角改为圆形,并加轻微阴影。
|
||||||
|
- 车辆图标改为白底 + 主色边框 + 主色 `Van` 图标,尺寸加大到 44×44px,两侧各延伸 20px 虚线,形成「路线」流动感。
|
||||||
|
- 城市名称限制单行并溢出省略,地址/联系人间距收紧、颜色变淡。
|
||||||
|
- 验证:`npm run build` 成功。
|
||||||
|
|
||||||
|
## 通知公告 /desk/notice 操作列加宽
|
||||||
|
- 需求:客户截图反馈操作列中「查看、编辑、删除」三个文字链接被挤成两行,要求加宽。
|
||||||
|
- 改动:`src/views/desk/notice.vue`
|
||||||
|
- 在 Avue CRUD option 中新增 `menuWidth: 220`,使操作列可在一行容纳三个操作链接;符合项目样式规范「三个操作按钮时宽度不小于 220px」。
|
||||||
|
|
||||||
|
## 全仓表格操作列宽度统一(≤3 按钮统一 180)
|
||||||
|
- 需求:客户提供新截图,要求所有操作按钮 ≤3 个的表格操作列统一宽度为 180;>3 个按钮保持 320。
|
||||||
|
- 核心改动:
|
||||||
|
- `src/utils/table-menu.js`:`TABLE_MENU_WIDTH` 的 `default` 与 `three` 都从 160/220 统一改为 180,`four` 保持 320;`resolveTableMenuWidth` 逻辑不变。
|
||||||
|
- `src/main.js`:全局 `crudOption.menuWidth` 从 160 改为 180,覆盖未显式设置 menuWidth 的 Avue CRUD 页面。
|
||||||
|
- `src/views/desk/notice.vue`:`menuWidth` 从 220 改为 180。
|
||||||
|
- `src/option/business/process-config.js`:`menuWidth` 从 200 改为 180。
|
||||||
|
- `src/views/system/param.vue`:手写 el-table 操作列宽度从 220 改为 180。
|
||||||
|
- 批量替换:对 44 个显式设置 `menuWidth: 160` 的 `.vue` / `.js` / `const/*.js` 文件统一改为 180(含部分文件内多处)。
|
||||||
|
- 验证:`npm run build` 成功(exit code 0)。
|
||||||
|
|
||||||
|
## 角色管理 /authority/role 操作列改 180
|
||||||
|
- 需求:用户明确要求把 /authority/role 页面操作列宽度设为 180(该页面此前设为 320,因含「查看/编辑/删除 + 权限设置」自定义菜单)。
|
||||||
|
- 改动:`src/views/authority/role.vue`
|
||||||
|
- option 内联 `menuWidth: 320` 改为 `menuWidth: 180`。
|
||||||
|
|
||||||
|
## 全站操作列按钮统一为链接(el-link)风格
|
||||||
|
- 需求:用户要求「操作栏里的按钮改成链接的方案,全站风格统一」,并先确认了对功能是否有影响。
|
||||||
|
- 调研结论(关键):全仓手写 el-table 操作列(10 个文件、约 28 列)与 53 个 avue-crud 自定义 `#menu` 插槽**已全部是 `<el-link>`**,全库 `type="text"` 残留为 0;真正未链接化的只剩下 **Avue 内置 viewBtn/editBtn/delBtn**(渲染为 `<el-button is-text>`,包在 `div.avue-crud__menu` 内,且自带 `permission` 指令与 `rowView/rowEdit/rowDel` 事件)。
|
||||||
|
- 实施手法:采用「方案 A:纯 CSS 视觉对齐」(零功能影响、不动事件与权限,符合用户「不要影响功能」的诉求)。
|
||||||
|
- 改动:`src/styles/element-ui.scss`
|
||||||
|
- 新增 `.avue-crud__menu > .el-button.is-text` 规则,把 Avue 内置行操作按钮外观对齐为 el-link:主色文字、无边框/无背景、hover 下划线 + 浅色、禁用态浅色无下划线、`height:auto / padding:0`(配合父级 `inline-flex; gap:8px` 与已存在的主色/间距覆盖规则)。
|
||||||
|
- 选择器仅命中行操作列 `.avue-crud__menu` 内的 `is-text` 按钮,不误伤顶部工具栏(`.avue-crud__header`)与弹窗按钮(`.avue-crud__dialog__menu`)。
|
||||||
|
- 注意:现有全局规则 `.avue-crud__menu .el-link` 会强制操作列内所有 el-link(含 `type="danger"` 的删除)显示为主色蓝。如需删除按钮保留红色告警,需单独调整该覆盖规则(本次未改,保持统一蓝)。
|
||||||
|
- 验证:`npm run build` 成功(exit code 0);构建日志中的 CSS `*` 告警来自第三方库压缩,与本次无关(历史既有)。
|
||||||
|
|
||||||
|
## 操作列文字取消加粗(统一常规字重)
|
||||||
|
- 需求:用户要求操作栏下的按钮字体都不要加粗,统一风格。
|
||||||
|
- 根因:Element Plus `el-link` 默认 `font-weight: var(--el-font-weight-primary)`(500 中等粗),导致操作列里的链接(查看/编辑/删除等)显示为加粗;而 Avue 内置 `is-text` 按钮此前已被规则设为 normal。
|
||||||
|
- 改动:`src/styles/element-ui.scss`
|
||||||
|
- 在既有 `.avue-crud__menu .el-link, .el-table ... .cell:has(.el-link) .el-link` 统一规则中增加 `font-weight: normal;`((0,2,0) 优先级高于 el-link 自身 (0,1,0) 的默认字重,可覆盖)。
|
||||||
|
- 覆盖范围:Avue 行菜单内的 el-link + 任意含 el-link 的表格体单元格(手写操作列),一次统一全站操作列字重;Avue 内置 `is-text` 按钮与顶部工具栏/弹窗按钮字重保持原样不受影响。
|
||||||
|
- 验证:`npm run build` 成功(exit code 0)。
|
||||||
|
|
||||||
|
## 全站表格省略文字 hover tooltip 提示统一
|
||||||
|
- 需求:用户截图反馈合同管理等页面中「所属项目」等列文字被省略,要求像 `/base/common-address` 一样,鼠标移上去显示完整提示;全站统一加,改完列出文件。
|
||||||
|
- 参考实现:`src/option/base/common-address.js` 中 Avue 列配置使用 `showOverflowTooltip: true`。
|
||||||
|
- 调研结论:全站表格可见列约 848 个(Avue)+ 424 个手写 `<el-table-column>`,其中 801 个 Avue 列、268 个手写列未开启 tooltip。Element Plus 支持全局 `table: { showOverflowTooltip: true }` 配置,Avue 3.9.2 完整透传,可一行代码覆盖全站。
|
||||||
|
- 改动文件清单:
|
||||||
|
- `src/main.js`
|
||||||
|
- Element Plus 全局配置新增 `table: { showOverflowTooltip: true }`,一次性覆盖所有 Avue CRUD 与手写 el-table。
|
||||||
|
- Avue 全局 `crudOption` 新增 `showOverflowTooltip: true` 作为双保险,避免页面级 option 被错误覆盖时失效。
|
||||||
|
- `src/styles/element-ui.scss`
|
||||||
|
- 新增 `.el-table .cell.avue-crud__menu, .el-table .el-table__body .cell.el-tooltip:has(> .el-link)` 规则,豁免操作列单元格的固定宽度与溢出隐藏,防止全局 tooltip 导致操作列内 el-link 换行后被裁切,保持原有 8px gap 与自动换行。
|
||||||
|
- `src/option/business/common.js`
|
||||||
|
- 共享工厂函数 `createCrudOption` 新增 `showOverflowTooltip: true`,覆盖 business 模块通过该工厂生成的所有 CRUD option。
|
||||||
|
- 覆盖范围:全站所有表格可见文本列(含 Avue 与手写 el-table),`type="selection"` 选择列由 Element Plus 自动豁免;操作列通过 CSS 豁免不被影响。
|
||||||
|
- 验证:`npm run build` 成功(exit code 0);构建日志中 CSS `*` 告警来自第三方库压缩,与本次无关(历史既有)。
|
||||||
|
|
||||||
|
## 修复运输计划 /business/transport-plan 详情页字段列对齐
|
||||||
|
- 现象:用户截图反馈「基本信息 / 收发货信息 / 其它信息」详情表格不平整,左侧一列 label 与右侧 value 错位、中间空 cell。
|
||||||
|
- 根因:`src/views/business/components/business-crud-page.vue` 的 `detailBox` 用 `<el-descriptions :column="2" border>` 渲染 `config.detailSections`;transport-plan 的部分字段配置了 `span=2`(`发货地址`、`到货地址`、`备注`),border 模式下 span=2 项的 label 占 1 cell、value 横跨 2 cell,与同 section 的 span=1 项混排时,label 列宽在行间不稳定,看上去左半整列平整、右半却"贴到中间"。
|
||||||
|
- 改动:`src/option/business/transport-plan.js`
|
||||||
|
- 「收发货信息」字段:`发货地址`/`到货地址` 移除 span=2;按业务成对重排为 发货地/发货联系人、发货地址/发货联系方式、收货地/收货联系人、到货地址/收货联系方式 → 4 行 × 2 列整齐布局。
|
||||||
|
- 「其它信息」字段:`备注` 移除 span=2;保持 备注/更新人、更新时间/创建时间 → 2 行 × 2 列。
|
||||||
|
- 「基本信息」9 个字段本来全部 span=1,无需调整。
|
||||||
|
- 现有样式(`&__detail-content .section-card` 内 `:deep(.el-descriptions__label) { width:140px; ... }`)保证 label 列固定 140px 右对齐;`:deep(.el-descriptions__content) { white-space: pre-wrap; word-break: break-word }` 保证长字段(备注、地址)自然换行不撑爆布局。
|
||||||
|
- 验证:`npm run build` 成功(exit code 0)。
|
||||||
|
|
||||||
|
## 修复 /system/dept 操作栏错位(链接竖排)
|
||||||
|
- 现象:用户截图反馈机构管理页操作列「查看 / 编辑 / 删除 / 新增子项」竖排且偏右错位。
|
||||||
|
- 根因:Avue 自带默认样式 `.avue-crud .avue-crud__menu{display:block}`(特异性 0,2,0)与项目全局覆盖样式选择器完全相同;项目原规则虽带 `!important`,但在 dev 模式 CSS 注入时序或浏览器缓存旧 element-ui.scss 时,`.avue-crud__menu` 退化成 block,导致内部按钮/链接竖排。
|
||||||
|
- 修复:
|
||||||
|
- `src/styles/element-ui.scss`:把 `.avue-crud .avue-crud__menu` 改为重复类名 `.avue-crud .avue-crud__menu.avue-crud__menu`(特异性升到 0,3,0),并新增 `width: 100% !important` 让操作列容器占满单元格、左对齐横排更稳。
|
||||||
|
- `src/views/system/dept.vue`:scoped 样式新增 `:deep(.avue-crud__menu)` 兜底(inline-flex + gap 8px + 子元素 inline-flex),双保险确保本页操作列横排。
|
||||||
|
- 验证:dev server 已热更新,curl 确认两处编译结果均含 `display: inline-flex !important`;构建需用户硬刷新浏览器清旧 CSS 缓存。
|
||||||
|
|
||||||
|
## 客商档案 /vehicle/customer-archive 弹窗风格统一为 section-card
|
||||||
|
- 需求:用户截图反馈弹窗有「双层灰色背景」,且「工商信息」等标题没有放到卡片里面;要求改成和 `/business/project-apply` 一致的白卡风格。
|
||||||
|
- 根因:原页面主表单使用 `.archive-form__group` 灰色容器,内部再包一层白色 `.el-row` 卡片,标题坐在灰色背景上,形成外层 dialog body 灰 + 分组灰的「双层灰」;客商材料、变更记录、评分材料、发票/邮寄信息也沿用旧的分节标题样式。
|
||||||
|
- 改动:`src/views/vehicle/customer-archive.vue`
|
||||||
|
- 主表单 4 个分组改为 `<section-card title="工商信息 / 联系信息 / 财务/信用信息 / 其他信息">` 全局白卡组件,删除外层「基本信息」大标题。
|
||||||
|
- 评分/联系人/收款/发票 4 个 Tab 内容改为 `<section-card>` + `<template #extra>`,按钮组进入卡头右侧。
|
||||||
|
- 客商材料改为 `<section-card title="客商材料" #extra>`,变更记录改为 `<section-card title="变更记录">`。
|
||||||
|
- 发票信息弹窗的「发票信息 / 邮寄信息」改为两张 `<section-card>`。
|
||||||
|
- 评分详情弹窗的「证明材料」改为 `<section-card title="证明材料" class="score-material-card">`。
|
||||||
|
- 清理不再使用的样式:`.archive-form__group`、`.section-title`、`.section-head`、`.customer-material-section`、`.change-record-section`、`.invoice-form__section-title`、`.score-material-section` 等。
|
||||||
|
- 验证:`npm run build` 成功(exit code 0)。
|
||||||
|
|
||||||
|
## 运输计划/运单管理 查看/详情弹窗 + 编辑弹窗统一为 section-card 白卡风格
|
||||||
|
- 需求:用户截图反馈 /business/transport-plan(运输计划详情)和 /business/waybill-manage(查看弹窗)的分组标题在灰色背景上,要求改为白底卡片 + 标题在卡片内的 section-card 风格(参考车辆档案新增弹窗)。
|
||||||
|
- 改动文件:`src/views/business/components/business-crud-page.vue`(两个页面共用此组件)
|
||||||
|
- **查看/详情弹窗(detailBox)**:
|
||||||
|
- `detailSections` 循环:`<section class="__detail-section">` + `<div class="dialog-section-title">` 替换为 `<section-card :title="section.title">`。
|
||||||
|
- 货物信息表格区:同上替换为 `<section-card title="货物信息">`。
|
||||||
|
- 附件表格区:替换为 `<section-card :title="config.attachmentTitle || '附件'">`。
|
||||||
|
- 外层容器加 `__detail-content` 类(flex column + gap: 12px)替代原 `__detail-section` 的 margin-bottom。
|
||||||
|
- CSS:`&__detail-section` 整块替换为 `&__detail-content .section-card` 嵌套选择器,保留 el-descriptions/el-table 样式。
|
||||||
|
- **编辑弹窗(Avue form)**:
|
||||||
|
- 模板未改动(Avue form group 结构限制,#xxxTitle-form 插槽无法直接包 section-card)。
|
||||||
|
- 新增 CSS `:deep(.avue-form__group:has(.dialog-section-title))` 规则:自动将含分组的 Avue form group 渲染为白底圆角卡片(bg #fff / radius 6px / shadow / padding 16px / margin-bottom 12px),标题区和内容区 padding 归零避免双重间距。
|
||||||
|
- 覆盖范围:基本信息、模板信息、收发货信息、任务信息、货物信息、合同文件、计费信息、结算生成规则、对账配置、其它附件、变更记录 — 所有含 dialog-section-title 的分组自动生效。
|
||||||
|
- 清理:删除旧 `__detail-section` 类引用(已无残留)。
|
||||||
|
- 验证:npm run build 成功(仅 safe-delete 守卫误报,与代码无关)。
|
||||||
|
|
||||||
|
## 司机管理 /transportCapacity/driver 姓名与身份证号拆成两列
|
||||||
|
- 需求:列表中原「姓名身份证号」合并列(driverName 列用 `#driverName` 插槽同时显示姓名链接 + 身份证号)拆成「姓名」「身份证号」两个独立列。
|
||||||
|
- 改动:
|
||||||
|
- `src/option/transportCapacity/driver.js`
|
||||||
|
- 原 `label: '姓名身份证号'` 列改为 `label: '姓名'`(minWidth 120,保留 slot/search,searchOrder 7 不变)。
|
||||||
|
- 新增独立「身份证号」列(`prop: 'idCardNo'`、`search: true`、`searchOrder: 2`、`slot: true`、`minWidth: 180`),并上移到 `driverName` 之后,使其紧邻「姓名」列(表格列顺序 = 数组顺序,`searchOrder` 仅控制搜索表单顺序,互不影响)。
|
||||||
|
- 原 `idCardNo` 隐藏搜索列(`hide: true`)被该新列取代后删除重复定义。
|
||||||
|
- `src/views/transportCapacity/driver.vue`
|
||||||
|
- `#driverName` 插槽只保留姓名链接(去掉原内联的身份证号 `<span>`)。
|
||||||
|
- 新增 `#idCardNo` 插槽显示 `row.idCardNo || '-'`(空值显示横杠)。
|
||||||
|
- 删除不再使用的 `.driver-page__identity` 样式块。
|
||||||
|
- 验证:`node --check` 选项文件语法通过;`npm run build` 仅因环境 safe-delete 守卫拦截清空 dist(与代码无关),未报任何 transform/parse 错误。
|
||||||
|
|
||||||
|
## 全站时间列统一加排序(参考 /base/cargo-type)
|
||||||
|
- 需求:用户截图 /business/contract-manage 页面,要求「有时间的都加一个排序」,参考 /base/cargo-type(该页面 createTime/updateTime 已设 `sortable: true`)。
|
||||||
|
- 方案:Avue 列加 `sortable: true`;手写 `<el-table-column>` 加 `sortable` 属性。精确匹配时间相关 label/prop,排除 `hide: true` 的搜索专用字段。
|
||||||
|
- 精确匹配范围:创建/更新时间、开始/结束日期、生效/失效日期、计划/实际/预计日期时间、审核/上传/变更/评分/调度日期时间、事故/检测/有效截止日期、入口/出口/交易/费用/换胎/出厂/维修/保养时间、登录/申请/提交/操作/触发/过期/日志/记录时间等。
|
||||||
|
- 改动文件(共 53 个):
|
||||||
|
- **src/option(18 个)**:base/common-address.js、business/common.js、business/contract-manage.js、business/project-apply.js、business/transport-plan.js、business/waybill-manage.js、system/authlog.js、transportCapacity/driver.js、vehicle/accident-record.js、vehicle/annual-inspection-record.js、vehicle/etc-record.js、vehicle/equipment-ledger.js、vehicle/insurance-record.js、vehicle/mileage-record.js、vehicle/oil-electric-record.js、vehicle/other-expense-record.js、vehicle/tire-replacement-record.js、vehicle/transport-change-record.js。
|
||||||
|
- **src/views 内联 Avue option(30 个)**:flow/follow.vue、flow/manager.vue、flow/model.vue、desk/notice.vue、desk/notice-composition.vue、report/reportlist.vue、work/done.vue、work/todo.vue、work/start.vue、work/claim.vue、work/send.vue、authority/ratelimit.vue、authority/apikey.vue、monitor/log/api.vue、monitor/log/usual.vue、monitor/log/error.vue、base/railway-station.vue、base/airport-master.vue、base/currency.vue、base/customer-type.vue、base/fee-item.vue、base/port-terminal.vue、data/record.vue、util/form.vue、util/table.vue、system/tenant.vue、vehicle/maintenance-plan.vue、vehicle/maintenance-record.vue、vehicle/credit-score-quantification.vue、vehicle/customer-archive.vue。
|
||||||
|
- **src/views 手写 el-table(6 个)**:system/dept.vue、business/loading-manage.vue、business/components/waybill-import-dialog.vue、business/project-apply.vue、vehicle/customer-archive.vue、business/components/business-crud-page.vue。
|
||||||
|
- 注意:弹窗内子表(如合同变更记录、项目附件/变更、客商档案评分/材料/变更记录等)时间列也一并加了排序;util/form.vue、util/table.vue 为示例页同样处理。
|
||||||
|
- 验证:`npm run build` 成功(exit code 0);构建日志中的 CSS `*` 告警为历史既有,与本次无关。
|
||||||
|
|
||||||
|
## transport-plan 新增弹窗卡片间距加大
|
||||||
|
- 需求:用户截图反馈 /business/transport-plan 新增弹窗中各分组白卡(基本信息/收发货信息/货物信息/附件)之间上下间距不够,要求像 project-apply 弹窗一样有明显间距。
|
||||||
|
- 改动:`src/views/business/components/business-crud-page.vue`
|
||||||
|
- `:deep(.avue-form__group:has(.dialog-section-title))` 的 `margin-bottom` 从 `12px` 增至 `16px`,与 project-apply.vue 的 `__grid` 卡片间距一致。
|
||||||
|
|
||||||
|
## 发货模板查看弹窗表格去边框
|
||||||
|
- 需求:用户截图反馈 /business/shipping-template 查看弹窗中「货物信息」表格有边框线,要求去掉。
|
||||||
|
- 改动:`src/views/business/components/business-crud-page.vue`
|
||||||
|
- 详情弹窗中货物信息和附件 `<el-table>` 移除 `border` 属性,清理残留 border-color 样式。
|
||||||
|
|
||||||
|
## 编辑弹窗自定义分组白卡样式提升到全局
|
||||||
|
- 现象:/business/shipping-template 新增/编辑弹窗中「模板信息」「基本信息」等分组未显示白底卡片(scoped :deep() 对 Avue 内部弹窗穿透不稳定)。
|
||||||
|
- 根因:发货模板/运输计划用 hidden form item + formslot 注入 `.dialog-section-title` 做分组标题,白卡样式原写在 business-crud-page.vue 的 `<style scoped>` 中,Avue CRUD 弹窗为库内渲染,:deep() 穿透不可靠。
|
||||||
|
- 修复:在 `src/styles/element-ui.scss` 全局新增 `.el-dialog .avue-form__group:has(.dialog-section-title)` 白卡规则(bg #fff / radius 6px / shadow / padding 16px / margin-bottom 16px),确保所有弹窗生效。
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
# 2026-08-11 工作日志
|
||||||
|
|
||||||
|
## 客商档案编辑弹窗布局改造(customer-archive.vue)
|
||||||
|
客户需求:参考截图,编辑弹窗表单改为 4 列布局、label 固定 6 汉字宽、超长换行、列间有间距。
|
||||||
|
|
||||||
|
改动(仅 `src/views/vehicle/customer-archive.vue` 一个文件):
|
||||||
|
- `el-form` 的 `label-width` 由 `auto` → `96px`(≈6 汉字 + 12px padding)。
|
||||||
|
- `el-form` 的 `gutter` 由 `18` → `24`(4 个 section-card 分组全部统一)。
|
||||||
|
- 联系信息分组 `el-col :span="8"`(3列)→ `:span="6"`(4列),与工商/财务分组对齐。
|
||||||
|
- `<style scoped>` 中 `.archive-form` 新增 label 规则:固定 `width:96px` + `padding-right:12px` + `white-space:normal` + `text-align:right` + `overflow:visible`,使长 label(如"统一社会信用代码")自动折行。
|
||||||
|
|
||||||
|
## 财务/信用信息分组备注字段补空白
|
||||||
|
- 客户需求:截图红框处(第三/四列空白)需有输入组件对齐。
|
||||||
|
- 改动(`src/views/vehicle/customer-archive.vue`):
|
||||||
|
- “申请总资金使用额度(万元)” 缩进修正(保持 `span=6`)。
|
||||||
|
- 第一次:“备注” 改为 `span=18`,占满第二行剩余三列,但右侧溢出。
|
||||||
|
- 第二次:第一列留空、`申请总资金使用额度` 居第二列、`备注` 跨第三、四列(`span=12`),总宽度 24 刚好一行。
|
||||||
|
|
||||||
|
注意:`.score-detail-form` 已有独立 `.el-form-item__label` 规则,与本次改动无关,未触碰。
|
||||||
|
|
||||||
|
## .archive-form label 换行稳定性加固
|
||||||
|
用户反馈长 label 仍挤压 input,要求 input 宽度固定、label 超长换行(参考截图效果)。
|
||||||
|
- 强化 `.archive-form :deep(.el-form-item__label)`:`width: 96px !important` + `flex-shrink: 0` + `word-break/overflow-wrap: break-word`,移除无效的 `overflow:visible/text-overflow/display:inline-block`。
|
||||||
|
- `.el-form-item` 移除 `overflow:hidden`(避免裁剪多行 label)。
|
||||||
|
- `.el-form-item__content` 移除 `overflow:hidden`,保留 `flex:1; min-width:0`,确保 input 稳定占满剩余宽度且不被压缩。
|
||||||
|
- 核心机制:label 固定宽 + flex-shrink:0 ⇄ content flex:1,二者互斥分配宽度,input 宽度恒定。
|
||||||
|
|
||||||
|
## ★ Element Plus 2.14 label 换行真正根因(之前没修对)
|
||||||
|
看了 `node_modules/element-plus/theme-chalk/el-form-item.css` 才确认:
|
||||||
|
```
|
||||||
|
.el-form-item__label{...height:32px;line-height:32px;display:inline-flex}
|
||||||
|
.el-form-item--default .el-form-item__label{height:32px;line-height:32px}
|
||||||
|
```
|
||||||
|
Element Plus 把 label 锁死成 32px 高 + 32px 行高,**只够放一行**。光设 `white-space:normal; word-break:break-word` 没用,第二行会被 32px 高度裁掉(且 inline-flex 内容溢出可见,但父 form-item 高度还是被撑不开、看起来被吃掉了)。
|
||||||
|
|
||||||
|
**正确修复**(`customer-archive.vue` `.archive-form`,可推广到所有 el-form 弹窗):
|
||||||
|
```scss
|
||||||
|
:deep(.el-form-item__label) {
|
||||||
|
width: 96px !important;
|
||||||
|
height: auto !important; // ★ 关键
|
||||||
|
line-height: 1.5 !important; // ★ 关键
|
||||||
|
padding-right: 12px;
|
||||||
|
white-space: normal;
|
||||||
|
text-align: right;
|
||||||
|
word-break: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
flex: 0 0 96px; // 显式三段式最稳
|
||||||
|
}
|
||||||
|
:deep(.el-form-item) { align-items: flex-start; } // 多行 label 时控件顶部对齐
|
||||||
|
```
|
||||||
|
这套改完,96px 固定的 label 才能真正「最大资金使用额度(万元)」「申请总资金使用额度(万元)」这种 11 字段自动折两行,右对齐、input 宽度不变。
|
||||||
|
|
||||||
|
## 弹窗表单项间距 16px → 8px(element-ui.scss 全局)
|
||||||
|
用户要求把弹窗内表单项上下间距从 16px 收紧到 8px,但担心压缩后红色校验错误文字被下一个 item 遮挡。
|
||||||
|
- 根因:Element Plus `.el-form-item__error` 默认 `position:absolute` 脱离文档流,16px 刚好够放下错误文字;压到 8px 后错误区超出占位被遮挡。
|
||||||
|
- 修复(只改 `src/styles/element-ui.scss` 全局样式,影响所有 `.el-dialog` 表单):
|
||||||
|
- 两处 `.el-dialog .el-form-item { margin-bottom:16px }`(约 171 行、271 行)→ `8px`。
|
||||||
|
- 新增 `.el-dialog .el-form-item__error { position:relative; margin-top:2px; line-height:1.2 }`,让错误文字正常占高度、不再被遮挡。
|
||||||
|
- 副作用:有校验错误时该 item 高度会被错误文字撑开,同一行相邻列可能轻微错位,属可接受的常规表单体验。无错误时保持 8px 紧凑。
|
||||||
|
- 备注:`customer-archive.vue` 内 `.receipt-form`/`.invoice-form` 子表单各自 scoped 写死 16px(发票/收票子表,非主表单),本次未动;若需统一收紧再说。
|
||||||
|
|
||||||
|
## 客商档案弹窗布局微调(截图标注)
|
||||||
|
用户新截图两处标注,均在 `customer-archive.vue`:
|
||||||
|
1. **联系信息分组**:原三个字段 `:span="8"`(各 1/3,铺满整行)。改为 4 列布局——三个字段 `:span="6"` + 末尾补一个空 `<el-col :span="6"></el-col>` 占位(第4列留空,不铺满)。
|
||||||
|
2. **工商信息「上下居中对齐」**:在 `.archive-form` scoped 样式内新增 `:deep(.el-form-item){ align-items:center }`,显式锁定每个 form-item 的 label 与控件垂直居中对齐(之前无 flex-start 覆盖,默认本就 center,现在显式声明更稳妥)。
|
||||||
|
- 潜在提醒:工商信息内长 label「统一社会信用代码」仍按之前需求折两行,会让同一 el-row 内该列 form-item 比相邻单行 label 列高约 2px,输入控件垂直方向略有错位。若用户要的是「同行所有输入框严格水平对齐」,需让长 label 单行不折行(nowrap)或把长 label 字段单独成行——待用户确认。
|
||||||
|
|
||||||
|
## 营业期限日期选择器宽度与同行对齐
|
||||||
|
用户截图标注:工商信息「营业期限」日期选择器太宽,且勾选组件被挤到下一排。
|
||||||
|
|
||||||
|
改动(`src/views/vehicle/customer-archive.vue`):
|
||||||
|
- `.business-term-field` 补充/修正 flex 布局:`display:flex; align-items:center; gap:8px; flex-wrap:nowrap;`。
|
||||||
|
- 日期选择器宽度由 `250px` 缩到 `150px`(`:deep(.el-date-editor)` 的 `flex-basis` + `width`)。
|
||||||
|
- 勾选组件加 `white-space:nowrap`,防止文字换行导致整体折行。
|
||||||
|
- 移除模板里冗余的 `style="display: flex;"`;修复 CSS 选择器笔误 `business-term-field,` → `.business-term-field,`。
|
||||||
|
|
||||||
|
效果:日期输入框与「无固定期限」复选框固定在同一排,日期框更紧凑。
|
||||||
|
|
||||||
|
## 营业期限校验提示改为中文
|
||||||
|
用户截图反馈:「营业期限」空值校验提示为英文 `businessEndDate is required`。
|
||||||
|
|
||||||
|
根因:`el-form-item` 的 `:required` 属性会在内部追加一条默认英文 required 规则,优先于自定义 validator,导致提示英文。
|
||||||
|
|
||||||
|
改动(`src/views/vehicle/customer-archive.vue`):
|
||||||
|
- `el-form-item` 保留 `:required="archiveForm.businessTermType === '固定期限'"`(用于红色星号)。
|
||||||
|
- 新增 item 级动态规则 `:rules="businessEndDateRules"`,computed 返回:
|
||||||
|
- 固定期限 → `[{ required: true, message: '请选择营业期限截止日', trigger: 'change' }]`
|
||||||
|
- 长期 → `[]`
|
||||||
|
- 移除 data 中 form-level 的 `businessEndDate` 规则及不再使用的 `validateBusinessEndDate` 函数。
|
||||||
|
|
||||||
|
效果:固定期限且未选日期时,提示统一为中文「请选择营业期限截止日」;长期时自动跳过该校验。
|
||||||
|
|
||||||
|
## 地址「详细地址」输入框宽度撑满
|
||||||
|
用户截图反馈:工商信息「地址」里「请输入详细地址」输入框太短,希望宽度 100% 并与右侧对齐。
|
||||||
|
|
||||||
|
改动(`src/views/vehicle/customer-archive.vue`):
|
||||||
|
- `.archive-form__address` 样式拆分:
|
||||||
|
- `el-cascader` 保持固定 `250px`。
|
||||||
|
- `el-input` 改为 `flex:1; width:100%; min-width:0`,自动占满剩余空间并对齐右侧。
|
||||||
|
- 移除 `el-input` 上冗余的 `style="width: 100%"`。
|
||||||
|
|
||||||
|
效果:省市区级联选择器保持 250px,详细地址输入框自动延伸至该 form-item 内容区右边缘。
|
||||||
|
|
||||||
|
## Tabs 区域卡片化(业务明细)
|
||||||
|
用户截图反馈:弹窗内 tabs(评分表/联系人/收款信息/发票信息)标签头裸露在灰底上,需要整体包进白底卡片。
|
||||||
|
|
||||||
|
改动(`src/views/vehicle/customer-archive.vue`):
|
||||||
|
- `<el-tabs>` 外层包裹 `<section-card title="业务明细">`,使 tabs 标签头与内容统一在一张白底卡片内,与上方工商/联系/财务/其他信息分组视觉一致。
|
||||||
|
|
||||||
|
## 客商档案弹窗标题去掉蓝色竖条
|
||||||
|
用户截图标注「新增客商档案」弹窗标题左侧的 4px 主色竖条不要了。
|
||||||
|
- 第一次尝试在 scoped 样式里用 `:deep(.el-dialog__title::before){display:none}` 未生效,因为该 `el-dialog` 开启了 `append-to-body`,DOM 被 teleport 到 body,scoped 样式挂不上。
|
||||||
|
- 最终方案:在 `customer-archive.vue` 末尾新增一个非 scoped 的 `<style lang="scss">` 块,写全局规则:
|
||||||
|
```scss
|
||||||
|
.archive-dialog .el-dialog__title::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 后续用户要求「应用到全部的弹窗」,改为全局移除:编辑 `src/styles/element-ui.scss`,把竖条共享规则 `.el-dialog__title:not(:empty)::before, .dialog-section-title::before` 拆开,仅保留 `.dialog-section-title::before`(分组标题仍带竖条),`.el-dialog__title` 不再有竖条。同时删除了 `customer-archive.vue` 末尾的特例 `<style>` 块。
|
||||||
|
- 注意:这与 AGENTS.md 4.8「新增/编辑弹窗标题左侧必须展示 4px 主色竖条」规范冲突,按用户决策已全局移除弹窗主标题竖条;如需恢复,把 `.el-dialog__title` 加回共享规则即可。
|
||||||
|
|
||||||
|
## 评分量化表弹窗边距与客商管理对齐
|
||||||
|
用户截图标注:新增/编辑评分量化表弹窗左右边距比客商管理编辑弹窗宽,要求统一。
|
||||||
|
- **根因**:`.score-config` 容器多设了 `padding: 4px 12px 12px`,叠加全局 `el-dialog__body { padding: 16px }` 导致总左右边距 28px(客商仅 16px)。
|
||||||
|
- **修复**:移除 `.score-config` 的 `padding` 属性(`src/views/vehicle/credit-score-quantification.vue`),弹窗内容区复用全局统一的 16px 边距。
|
||||||
|
|
||||||
|
## 编辑弹窗风格统一(按 customer-archive 定稿对齐)
|
||||||
|
用户要求把 `customer-archive.vue` 定稿的 `.archive-form` 风格(label 固定宽 `calc(6em+24px)` 折行 + 控件 250px + section-card 分组白卡 + 弹窗灰底)复制到其它手写编辑弹窗。已处理:
|
||||||
|
|
||||||
|
1. **credit-score-quantification.vue**(最掉队):4 个弹窗(评分量化表主配置 / 类目详情 / 评分项目详情 / 评估标准设置)原来用手写 `.section-title` 分区、未用 section-card、无 tabs。已重构为 4 张 `<section-card>` 分组白卡(标题带 4px 主色竖条),configBox 主表单 + standardBox 评估标准表单应用 `.archive-form`(本文件 scoped 内新增该样式,从 customer-archive 复制),按钮保持 plain 描边;删除了废弃的 `.base-form` / `.section-head` / `.section-title` / `.standard-head` 手写标题样式;`.score-config` 去掉多余 padding 复用全局 16px 边距。
|
||||||
|
2. **driver.vue / vehicle.vue / ship.vue**:三个档案弹窗已用 section-card + el-col 多列布局。仅给 `driver-form` / `vehicle-form` / `ship-form` 追加 `archive-form` class(原 form 的 `:deep(.el-input){width:100%}` 特异性低于 archive-form 的 `:deep(.el-form-item__content > .el-input){width:250px}`,被安全覆盖;label 带 `!important`)。弹窗内无子表格"添加"按钮,footer 保存已是 primary 实心(符合规范)。
|
||||||
|
3. **loading-manage.vue**:配载单编辑弹窗 `loading-manage-dialog__form` 追加 `archive-form`(该 class 无独立 style,不冲突)。
|
||||||
|
4. **waybill-import-dialog.vue**:新建导入弹窗 `el-form` 追加 `archive-form`。
|
||||||
|
5. **common-route.vue**:三个弹窗均为查询/导入/地图选择类(无信息编辑表单),且按钮风格(主按钮 primary 实心、辅助 plain)已符合规范,**未做结构改动**。
|
||||||
|
6. **business-crud-page.vue**:已在 scoped style 开头用组合选择器一次性注入 `.archive-form` 同款样式(label 固定宽折行 + 控件 250px),覆盖其 6 个主表单 class:`__task-form` / `__freight-form` / `__module-form` / `__dispatch-item-form` / `__match-form` / `__transport-plan-import-form`(`__billing-form-row` 仅是 div 行容器,非表单,未包含)。这是核心通用组件,影响所有引用它的业务编辑弹窗,已按"全按对齐"指令执行;纯样式增强、不破坏功能,建议刷新各业务页回归验证。
|
||||||
|
|
||||||
|
注意:`.archive-form` 的关键样式(label 固定宽折行 + 控件 250px)是 scoped 的,复制到新文件时必须在该文件 scoped 内重新定义,不能只挂 class(除非全局化)。
|
||||||
|
|
||||||
|
## 弹窗灰底间距全站统一 + 表单间距放宽 + common-route 输入框对齐
|
||||||
|
全局 `src/styles/element-ui.scss` 连续改动(针对截图反馈"新增弹窗太挤"):
|
||||||
|
1. **灰底间距全站 8px**:`.el-dialog__body { padding: 8px !important }`(加 `!important` 覆盖 Avue 内部默认 padding,影响**所有**弹窗含 Avue CRUD 与手写 el-dialog)。
|
||||||
|
2. **按钮放进白卡**:`.avue-crud__dialog .avue-dialog__footer` 加 `border-radius: 0 0 6px 6px` + 同白卡 `box-shadow` + `padding: 16px 24px` + `margin-top:0`,与上方 `.avue-form` 白卡视觉合一(取消/保存等按钮现在在卡片底部内侧)。`.avue-form` 同步加 `margin-bottom:0`。
|
||||||
|
3. **表单间距放宽对齐 customer-archive**:`.avue-form` 白卡内边距 `16px → 24px`;`.el-dialog .el-form-item` 上下间距 `16px → 20px`(含全 size 覆盖规则);分组标题/内容内边距 `14px 16px → 16px 24px`。
|
||||||
|
4. **common-route 输入框宽度对齐 customer-archive**:
|
||||||
|
- `src/option/business/common-route.js`:`export const option = createCrudOption([...])` 改为 `{ ...createCrudOption([...]), dialogCustomClass: 'avue-dialog--common-route' }`,给 Avue 自动编辑弹窗挂自定义 class(Avue 支持 `dialogCustomClass`,渲染在 `.avue-dialog` 根元素)。
|
||||||
|
- `src/styles/element-ui.scss` 新增 `.avue-dialog--common-route` 规则:label `width: calc(6em+24px) !important` 折行 + 控件 `.el-input/.el-select/.el-cascader/.el-input-number/.el-date-editor` 固定 `width: 250px`。textarea(备注)不设固定宽,保持 100% 撑满(与 customer-archive 一致)。地址 formslot(`.common-route-page__route-address` flex 布局)因中间多了一层 div,不被 `.el-form-item__content > .el-input` 命中,保持原样。
|
||||||
|
|
||||||
|
注意:common-route 编辑弹窗字段是 span 12(两列),250px 输入框在两列下右侧会留白——这是用户"和 customer-archive 对齐"的明确要求,如需紧凑可改列布局或加大输入框宽。
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# 2026-08-12
|
||||||
|
|
||||||
|
## 全站自定义表单弹窗样式统一为客商档案风格
|
||||||
|
|
||||||
|
- 新增全局弹窗表单标准类 `.dialog-form--carded`(`src/styles/element-ui.scss`),统一实现:
|
||||||
|
- `section-card` 白底卡片分组
|
||||||
|
- label 固定宽度 `calc(6em + 24px)`、右对齐、上下居中
|
||||||
|
- input/select/cascader/tree-select/date-editor/textarea 等控件固定宽度 250px
|
||||||
|
- radio/checkbox 组宽度自适应
|
||||||
|
- 改造文件清单:
|
||||||
|
- `src/views/flow/manager.vue`:流程变更弹窗
|
||||||
|
- `src/views/flow/follow.vue`:流程删除弹窗
|
||||||
|
- `src/views/system/param.vue`:参数编辑弹窗
|
||||||
|
- `src/views/system/authlock.vue`:锁定用户、解锁确认弹窗
|
||||||
|
- `src/views/system/user.vue`:用户编辑、修改密码弹窗
|
||||||
|
- `src/views/business/components/waybill-import-dialog.vue`:新建导入弹窗表单区
|
||||||
|
- 已跳过(非编辑表单弹窗):`system/dept.vue` 承运商选择搜索弹窗、`business/master-order-editor.vue` 页面级组件。
|
||||||
|
- `npm run build` 构建通过。
|
||||||
|
|
||||||
|
## 港口码头(port-terminal)编辑弹窗 input 宽度统一 250px
|
||||||
|
|
||||||
|
- 文件:`src/views/base/port-terminal.vue`
|
||||||
|
- 做法:option 加 `dialogCustomClass: 'port-terminal-edit-dialog'`,并在文件末尾新增非 scoped `<style>` 块,`.port-terminal-edit-dialog .el-input { width: 250px !important }`。
|
||||||
|
- 说明:弹窗 `append-to-body`,scoped 命中不到,故用全局样式 + 自定义 dialog class 限定作用域;`.el-input` 与 `.el-select` 统一 `width: 250px !important`(含 formslot 内带内联 `width:100%` 的 country/cityCode/districtCode 下拉,!important 覆盖)。`textarea`(详细地址/备注)、类型 `el-radio-group` 维持自适应,未改。新增弹窗复用同一 dialog,故一并生效。
|
||||||
|
|
||||||
|
## 港口码头 详细地址/备注字段形态调整(接续上方宽度统一)
|
||||||
|
|
||||||
|
- `detailAddress`:移除 `type: 'textarea'` / `minRows: 3` 改单行 input;因属整行字段需放宽,启用 `formslot` + 模板 `#detailAddress-form` 手写 `<el-input class="detail-address-input">`,并用 `.port-terminal-edit-dialog .detail-address-input { width: 100% !important }` 覆盖全局 250px 规则,占满整行。表格列仍由 `slot: true` + `#detailAddress` 显示插槽控制。
|
||||||
|
- `remark`:`minRows: 4` → `2`(约 2 行高度),仍为 textarea(本身不受 250px 规则约束,已是整行宽)。
|
||||||
|
- 表单字段排序调整:按截图示意重新排为——类型(10) → 名称(20) → 编码(30) → 国家(40) → 城市(50) → 区县(60) → 详细地址(70) → 经度(80) → 纬度(90) → 上级港口(100) → 备注(无 order 置底)。即区县和详细地址插在国家/城市那一行下面。
|
||||||
|
- 备注宽度改为 80%:`remark` 加 `formslot: true` + 模板 `#remark-form` 手写 `<el-input type="textarea" class="remark-input" :rows="2" maxlength="200" show-word-limit>`,CSS `.port-terminal-edit-dialog .remark-input { width: 80% !important }`。注意:详细地址 `.detail-address-input` 当前宽度为 81%(用户手动调整,非本次设定)。
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# 2026-08-13 工作记录
|
||||||
|
|
||||||
|
## business-crud-page.vue UI 对齐 customer-archive(section-card 间隔 + 表单排列统一)
|
||||||
|
|
||||||
|
### 问题
|
||||||
|
- 该组件内 `.section-card { margin-bottom: 0 !important }`(scoped 顶层)把全局 8px 卡片间距清零,导致弹窗里的 section-card 卡片贴在一起(与 customer-archive 的留白不一致)。
|
||||||
|
- 手写的 dispatch-item 表单 markup 仍写 `label-width="auto"`(虽被共享 CSS 块强制为 `calc(6em+24px)`,但不直观)。
|
||||||
|
|
||||||
|
### 改动(src/views/business/components/business-crud-page.vue)
|
||||||
|
1. 删除 `margin-bottom:0 !important` 覆盖,改为 `.section-card{margin-bottom:8px !important; &:last-child{margin-bottom:0}}`,与 customer-archive/全局 element-ui.scss 的 8px 节奏一致。
|
||||||
|
2. `.business-crud-page__detail-content`(详情弹窗,原本 `gap:12px`)改为 `gap:8px` 并给内部 `.section-card{margin-bottom:0 !important}`,避免 8px 外边距 + gap 双重留白。
|
||||||
|
3. `:deep(.avue-form__group:has(.dialog-section-title))` 白卡 `margin-bottom:16px→8px`,使主弹窗分组白卡与 section-card 同节奏。
|
||||||
|
4. `&__dispatch-summary` 的 `margin-bottom:12px→8px`。
|
||||||
|
5. dispatch-item 表单 `label-width="auto"→"calc(6em + 24px)"`(与 task-form/freight-form 共享的 label 标准一致;参考 customer-archive 的右侧固定宽 label + `el-row :gutter` 网格思路)。
|
||||||
|
|
||||||
|
### 验证
|
||||||
|
- 仅改 scoped SCSS + 1 处 markup 属性,未动逻辑;SFC 结构未变。表单组件排列本就通过共享 `__task-form/__freight-form/__dispatch-item-form` 块统一为 `calc(6em+24px)` label + 4 列网格,本次使其显式对齐。
|
||||||
|
|
||||||
|
## 弹窗样式与交互调整(base 模块)
|
||||||
|
|
||||||
|
### 1. port-terminal.vue 港口/码头弹窗
|
||||||
|
- 去掉浏览器 input autocomplete 地址联想:新增 `disableDialogAutocomplete()` 方法(批量给 `.port-terminal-edit-dialog` 内 input/textarea 设 autocomplete=off 等),在 `beforeOpen` 两个分支 `done()` 后用 `setTimeout(50)` 调用。
|
||||||
|
- option 字段调整(用户此前需求,已指导):详细地址 `detailAddress` 的 `span:24→12`、`order:86→88`,与区县 `districtCode`(span12,order87) 并排。
|
||||||
|
|
||||||
|
### 2. railway-station.vue 铁路车站弹窗
|
||||||
|
- label 宽度 85→108:option `labelWidth:'auto'→'108px'`(字符串不生效),再补 `dialogCustomClass:'railway-station-edit-dialog'` + 非 scoped `<style>` 强制 `.el-form-item__label{width:108px!important}`。
|
||||||
|
|
||||||
|
### 3. 全站弹窗底部浮动按钮(统一规范)
|
||||||
|
- 在 `src/styles/element-ui.scss` 新增全局规则:`.el-dialog__body{max-height:calc(100vh-200px);overflow-y:auto}` + `.el-dialog__footer{白底+上边框+轻阴影}`,实现取消/保存按钮固定在底部(参照 business/project-apply)。全站 Avue 弹窗自动生效,含 railway-station。已记入 MEMORY.md 长期规范。
|
||||||
|
|
||||||
|
### 4. port-terminal.vue 字段分组白卡 + label 108(本次)
|
||||||
|
- 改用 Avue `option.group` 把表单分成 3 张白卡:基础信息 / 位置信息 / 其他信息(对齐 driver/vehicle/ship/customer-archive 的 section-card 风格)。
|
||||||
|
- root `column` 保留不动(脚本大量用 `findColumn(this.option.column,...)` 动态设 dicData/显示),在 `created()` 里循环把 root 列 `display=false`,使其只服务表格/搜索、不重复渲染进表单。
|
||||||
|
- 新增 `getColumnsByProp(prop)` 助手,统一把 8 处 `dicData`/显示切换同时作用于 root 与 group 两处(否则 group 列的级联下拉不会刷新)。
|
||||||
|
- 样式:外层 `.avue-form` 透明化(卡片交由分组);`.avue-form__group` 白卡 + 4px 主色竖条标题 + 内容区左右 16px;`.el-form-item__label{width:108px!important}` 对齐 railway-station。
|
||||||
|
- 关键坑:Avue form 的 `columnOption` 会把 root column 也作为「无标题组」渲染进表单,故 group 页必须给 root 列 `display:false`,否则字段重复;且动态 dicData 必须双写 root+group。
|
||||||
|
|
||||||
|
### 5. port-terminal.vue 回退分组白卡 + label 108(用户要求)
|
||||||
|
- 用户决定不在本页使用分组白卡与 label 宽度 108,已全部回退:
|
||||||
|
- 删除 `option.group` 三张白卡,恢复扁平 `column` 结构(`sed` 删 695–974 行)。
|
||||||
|
- `created()` 去掉 root 列 `display=false` 循环;删除 `getColumnsByProp` 助手;`setColumnDisplay` 与 8 处 `dicData`/显示切换全部还原为 `findColumn(this.option.column, prop)` 写法。
|
||||||
|
- 样式块恢复:`.avue-form` 恢复为单张白卡(去透明化),删除 `.avue-form__group` 分组白卡规则与 `.el-form-item__label{width:108px}`;保留灰底 + 浮动底栏统一风格、input/select 250px、备注 85%。
|
||||||
|
- 校验:SFC 解析通过,无 getColumnsByProp/group/avue-form__group/label108 残留。
|
||||||
|
|
||||||
|
### 6. railway-station.vue 对齐 port-terminal 统一风格(用户要求"按 port-terminal 效果改")
|
||||||
|
- 扩充 `.railway-station-edit-dialog` 非 scoped 样式块,对齐 port-terminal:灰底 `.el-dialog__body`(#f2f2f2, padding 8px) + 表单白卡 `.avue-form`(白底/圆角6/轻阴影/内边距16px) + 浮动底栏 `.avue-dialog__footer`(白底/圆角/轻阴影/右对齐) + 输入框 `.el-input,.el-select` 宽 250px + 备注 `.remark-input` 宽 85%。
|
||||||
|
- 给备注字段(`prop:'remark'`)加 `class:'remark-input'`,使备注 85% 规则生效(原字段仅 `type:textarea`,无 class)。
|
||||||
|
- 保留用户之前要求的 `labelWidth:'108px'` 与 `.el-form-item__label{width:108px!important}`。
|
||||||
|
- 未包含 port-terminal 的「关闭浏览器地址联想 autocomplete」逻辑(用户未要求,railway 未反馈该问题);如需一致可后续补。
|
||||||
|
- 校验:SFC 解析通过。
|
||||||
|
|
||||||
|
### 7. 方向纠正:应改 port-terminal 参考 railway-station(非改 railway 参考 port)
|
||||||
|
- 用户澄清:第6条方向搞反了。正确语义是「改 port-terminal,参考 railway-station」。railway-station 是参考样板,不应被改。
|
||||||
|
- 还原 railway-station(撤销第6条):删除误加的冗余专属灰底白卡/250px/备注85% 块(该块 `.avue-form{padding:16px}` 会与全局 `.el-dialog .avue-form` 的 24px 冲突造成不齐),仅保留 `labelWidth:'108px'` + `.el-form-item__label{width:108px!important}`;移除备注字段误加的 `class:'remark-input'`。
|
||||||
|
- 关键认知:全局 `src/styles/element-ui.scss` 第240-265行**已有完整弹窗标准规则**(灰底 `.el-dialog__body`#f2f2f2 + 白卡 `.el-dialog .avue-form` padding 24px + 浮动底栏 `.avue-crud__dialog .avue-dialog__footer`)。railway-station「整齐」即靠全局规则 + label 108;专属块属冗余且会覆盖全局 24px→16px 导致不齐。
|
||||||
|
- 改造 port-terminal 对齐 railway-station:保留专属灰底白卡兜底(其历史未命中全局,保留以防回归),补 `labelWidth:'108px'` + 样式块 `.el-form-item__label{width:108px!important}`;并将专属块 `.avue-form` padding 由 16px 改为 **24px** 以与全局/railway 一致。
|
||||||
|
- 校验:两文件 SFC 解析通过;railway 无 remark-input/padding16px 残留;port-terminal 已含 label 108 + padding 24px。
|
||||||
|
|
||||||
|
### 8. railway-station.vue label 加宽(用户反馈太挤)
|
||||||
|
- 用户反馈新增弹窗中"所属区县"label 太挤(实测约 85×32px),要求加宽。
|
||||||
|
- 已把 `option.labelWidth: '108px' → '140px'`,同步全局覆盖 `.el-form-item__label{width:108px!important} → 140px!important`。
|
||||||
|
- 140px 够"所属区县"4字+必填星号+留白,也覆盖表单其他 label("行政区划编号"6字 + * 也够)。若仍觉不够可再调到 160px。
|
||||||
|
|
||||||
|
## process-config.vue 套用 customer-archive 样式(用户要求"和 business-crud-page 一样的问题")
|
||||||
|
- 根因同商务页:本页原用灰色 `div.dialog-section-title` 标题 + 自定义 `__field` flex 布局(label `flex:0 0 150px`),既无白卡样式也无 8px 卡片间距,与 customer-archive 的 `<section-card>` 白卡风格不一致。Avue form 本身 `labelPosition:'right'` 但 `labelWidth:'auto'`,故原页才需自定义固定宽 label。
|
||||||
|
- 改动(src/views/business/process-config.vue):
|
||||||
|
1. `#basicInfo-form` 槽:去掉 `process-config-page__section` + `dialog-section-title`,改为 `<section-card title="基本信息">` + 嵌套 `<el-form label-position="right" label-width="calc(6em + 24px)">` + `<el-row :gutter="18">` + `el-col`(配置名称6/项目6/运输完成时限12/备注24) + `el-form-item`,与 customer-archive 右侧固定宽 label 栅格完全对齐。必填项加 `required`。
|
||||||
|
2. `#nodeConfigJson-form` 槽:两个 `dialog-section-title`(现行过程节点 / 节点设置)分别改为 `<section-card title=...>` 包裹(timeline 与 el-table 内容不变)。
|
||||||
|
3. 删除不再使用的 `__section/__field/__field-label/__field-label--required` 样式;保留 `__finish-days` 与节点表格相关样式。
|
||||||
|
- 卡片间距:全局 `.section-card{margin-bottom:8px}` 在本页生效(无 `!important` 覆盖),三张白卡自动保持 8px 间隔。
|
||||||
|
- 校验:@vue/compiler-sfc 编译通过,3 个 section-card 均正确闭合;未动任何业务逻辑(validateRow/syncNodeFields 等不变)。
|
||||||
|
|
||||||
|
## business-crud-page.vue 待调度列表汇总「件」蓝色高亮
|
||||||
|
- 需求:调度弹窗「待调度列表」标题右侧汇总 `共50吨、100件 | 已调度 50吨、100件,剩余0吨、0件` 中,单位是「件」的数字用蓝色显示。
|
||||||
|
- 改造(src/views/business/components/business-crud-page.vue):
|
||||||
|
1. 新增 computed `dispatchSummarySegments`,把原纯文本 `dispatchSummaryText` 拆成结构化三段(共/已调度/剩余),每段 items 带 `{unit, value, isPiece: unit==='件'}`,文本拼接方式与原格式完全一致(含空格、`|`、`、`、逗号)。
|
||||||
|
2. 模板(约 2546 行)由 `{{ dispatchSummaryText }}` 改为 `v-for` 渲染;`件` 数字包裹 `<span class="dispatch-summary-value is-piece">`。
|
||||||
|
3. 样式:`.business-crud-page__dispatch-list-summary .dispatch-summary-value.is-piece { color:#409eff; font-weight:600 }`(项目主蓝,轻微加粗更醒目)。
|
||||||
|
- 校验:文本格式与原串逐字符一致,仅「件」数字变色;未动 `dispatchSummaryItems` 等其它逻辑。
|
||||||
|
- 修正(用户反馈):原把整段「数字+件」一起变蓝导致「件」字也蓝。改为数字与单位拆成两个 span(`dispatch-summary-value` 仅含数字、`dispatch-summary-unit` 含单位),蓝色与加粗只作用于数字,「件」字保持默认色。
|
||||||
|
- 二次修正(用户反馈):「吨」数字也要蓝。将 `isPiece` 由 `unit==='件'` 放宽为恒 `true`,即所有单位的数字均蓝色、单位字默认色(覆盖 吨/件/方 等)。
|
||||||
|
|
||||||
|
## business-crud-page.vue 调度弹窗顶部两卡片对齐 + 下方间距 12px
|
||||||
|
- 需求:截图显示「基本信息」与「收发货路线」两张 section-card 高度不一致,且与下方「待调度列表」间距需 12px。
|
||||||
|
- 改动(src/views/business/components/business-crud-page.vue):
|
||||||
|
1. `&__dispatch-summary` 容器 `margin-bottom:8px→12px`,并在容器内覆盖 `.section-card{margin-bottom:0 !important}`,避免 section-card 自带 8px margin 与容器 margin 叠加。
|
||||||
|
2. `&__dispatch-summary-card` 与 `&__dispatch-route-card` 加 `min-height:260px`;内部 `:deep(.section-card)` 与 `:deep(.section-card__body)` 均 `flex:1` 并设 `display:flex;flex-direction:column`,确保左右卡片外框与内部白底区域都等高对齐。
|
||||||
|
- 校验:grid `align-items:stretch` + section-card flex:1 撑满,卡片底部会对齐;下方间距精确 12px。
|
||||||
|
|
||||||
|
## business-crud-page.vue「货物信息」标题与右侧链接水平对齐
|
||||||
|
- 需求:截图显示「货物信息」标题与右侧「导入货物」「常用货物」两个 `el-link` 不在同一水平线。
|
||||||
|
- 改动(src/views/business/components/business-crud-page.vue):
|
||||||
|
1. `.dialog-section-title > span { line-height: 1 }` 收紧标题文字行高。
|
||||||
|
2. `&__section-actions .el-link { display:inline-flex; align-items:center; line-height:1 }` 让链接严格垂直居中。
|
||||||
|
- 校验:两处使用 `.dialog-section-title--action.business-crud-page__goods-title` + `__section-actions` 的货物信息标题都会生效。
|
||||||
|
|
||||||
|
## transportCapacity/driver.vue 到期筛选标签靠右对齐
|
||||||
|
|
||||||
|
- 需求:将搜索栏下方「全部/30天内到期/已到期」筛选标签靠右对齐,放到搜索按钮前面并保留间距。
|
||||||
|
- 改动(src/views/transportCapacity/driver.vue):
|
||||||
|
- `.driver-page__expiry-tags` 的 `margin-right: auto` 改为 `margin-right: 12px`。
|
||||||
|
- 保留 `order: -1` 使标签在 Flex 排列中位于搜索按钮之前;全局 `.avue-crud__search .avue-form__menu` 已设 `justify-content: flex-end`,删除 `auto` 后标签与按钮作为一个整体靠右排列,不再分居两端。
|
||||||
|
- 间距:标签内部 `gap: 8px` 保留;标签与搜索按钮之间 12px。
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
# 2026-08-14 工作记录
|
||||||
|
|
||||||
|
## temporary-credit-limit 弹窗 label 固定 6 汉字宽度并换行
|
||||||
|
|
||||||
|
- 需求:用户反馈 `/business/temporary-credit-limit` 新增弹窗中 label「已使用项目资金使用额度(万元)」过宽(227px),希望超过 6 个汉字就换行。
|
||||||
|
|
||||||
|
## loading-manage 搜索栏 label 宽度 160→88
|
||||||
|
|
||||||
|
- 用户要求把 `/business/loading-manage` 页面搜索栏所有 label 宽度从 160px 改为 88px。
|
||||||
|
- 改动:`src/views/business/loading-manage.vue` 第 4 行 `<el-form ... label-width="88px">`。
|
||||||
|
- 注意事项:AGENTS.md 4.4 节规范"搜索组件 label 宽度统一不小于 160px",本次按用户指令执行但与项目规范冲突;如后续要恢复规范或全站统一,需要回滚。
|
||||||
|
|
||||||
|
## shipping-template 弹窗高度改为接近全屏
|
||||||
|
|
||||||
|
- 用户要求把 `/business/shipping-template` 弹窗高度调到接近全屏(硬编码 200px 安全区太大)。
|
||||||
|
- 关键定位:Avue 库内置 `.avue-dialog { max-height: calc(100% - 200px) }` 加在 `el-dialog` 根元素(avue lib `packages/core/components/dialog-form/index4.js:14` `class:"avue-dialog"`);shipping-template 的 `dialogCustomClass:'shipping-template-dialog'` 合并到同一元素。项目样式(common.scss)在 main.js:24 晚于 Avue css(main.js:12)加载,可覆盖。
|
||||||
|
- 改动:`src/styles/element-ui.scss` 的 `.shipping-template-dialog.el-dialog` 规则,由 `margin-top:4vh; margin-bottom:0; height:calc(100vh - 4vh - 24px)` 改为 `margin-top:20px !important; margin-bottom:20px !important; height:calc(100vh - 40px); max-height:calc(100vh - 40px) !important`,用 max-height + !important 压过库内置上限。
|
||||||
|
- 注意:仅改 shipping-template 专属规则,未动库内置 `.avue-dialog` 根,也未影响其他弹窗。body/footer 的 flex 分摊规则(474-488 行)保持不变。
|
||||||
|
|
||||||
|
## transport-plan 弹窗高度也改为接近全屏
|
||||||
|
|
||||||
|
- 用户要求 `/business/transport-plan` 新增/编辑弹窗同样改为接近全屏(原来固定 max-height: 72vh,内容少时下方留白)。
|
||||||
|
- 改动:`src/styles/element-ui.scss` 的 `.transport-plan-dialog.el-dialog` 规则,由 `margin-top:4vh; max-height:72vh` 改为 `margin-top:20px !important; margin-bottom:20px !important; height:calc(100vh - 40px); max-height:calc(100vh - 40px) !important`,与 shipping-template 弹窗完全对齐(压过库内置 `.avue-dialog` 的 max-height: calc(100% - 200px))。
|
||||||
|
- 已确认 `src/option/business/transport-plan.js:607` 有 `dialogCustomClass: 'transport-plan-dialog'`,CSS 生效。body/footer flex 分摊规则(504-520 行)保持不变。
|
||||||
|
|
||||||
|
## waybill-manage 弹窗高度也改为接近全屏
|
||||||
|
|
||||||
|
- 用户要求 `/business/waybill-manage` 新增/编辑弹窗同样接近全屏。
|
||||||
|
- 机制澄清:waybill-manage 用封装组件 `src/views/business/components/business-crud-page.vue`,它内部 `<avue-crud :option="option">`,option 由传入的 `crudOption`(即 waybill-manage.js 的 option)经 `cloneOption`(组件内 6261 行,浅展开+重映射 column,保留所有顶层属性)克隆而来。因此给 waybill-manage.js 的 option 顶层加 `dialogCustomClass` 即可只影响本页(不影响共用 business-crud-page 的其他页面)。Avue crud 弹窗 `dialog-form3.js:21` 会读取并应用 dialogCustomClass。
|
||||||
|
- 改动1:`src/option/business/waybill-manage.js` 的 `export const option = {` 顶层加 `dialogCustomClass: 'waybill-manage-dialog'`(在展开 `...createCrudOption(` 之前)。
|
||||||
|
- 改动2:`src/styles/element-ui.scss` 新增 `.waybill-manage-dialog.el-dialog / .el-dialog__header / .el-dialog__body / .avue-dialog__footer` 四条规则,复刻 shipping-template 方案(height/max-height: calc(100vh - 40px) !important;body max-height:none !important 解开全局 `.avue-crud__dialog .el-dialog__body` 的 200px 限制;footer sticky)。额外在 footer 加 `margin-top: 0 !important` 抵消全局 `.avue-crud__dialog .avue-dialog__footer { margin-top: -30px !important }`(863 行)的上移副作用(shipping-template 是 dialog-form 弹窗无 avue-crud__dialog class 故不受影响,waybill-manage 是 avue-crud 弹窗会受影响,必须处理)。
|
||||||
|
- 宽度来源:
|
||||||
|
- `src/option/business/common.js` 的 `createCrudOption` 中 `labelWidth: 'auto'`;
|
||||||
|
- Avue 会按当前弹窗内最长 label 自动计算宽度,故出现 227px。
|
||||||
|
- `src/styles/element-ui.scss` 第 164 行 `.el-dialog .el-form-item__label { white-space: nowrap }` 阻止换行。
|
||||||
|
- 改动:
|
||||||
|
1. `src/option/business/temporary-credit-limit.js`:option 增加 `dialogCustomClass: 'temporary-credit-limit-dialog'`。
|
||||||
|
2. `src/styles/element-ui.scss`:新增 `.temporary-credit-limit-dialog .el-form-item__label` 规则,固定宽度 `calc(6em + 24px)`,并设置 `white-space: normal; word-break: break-all; overflow-wrap: anywhere; line-height: 18px`,实现超长 label 自动换行。
|
||||||
|
3. 同时给 `.temporary-credit-limit-dialog .el-form-item { align-items: center; }` 保持垂直居中。
|
||||||
|
- 参考:同文件已有 `.avue-dialog--common-route` 使用相同 6em label 宽度方案。
|
||||||
|
|
||||||
|
## transportCapacity/ship 到期筛选标签默认展开并靠前
|
||||||
|
|
||||||
|
- 需求:用户反馈 `/transportCapacity/ship` 页面搜索栏的「全部/30天内到期/已到期」标签需要点击「展开」后才显示,希望默认直接显示;且位置要位于搜索按钮前面。
|
||||||
|
- 原因:`src/option/transportCapacity/transport-ship.js` 中 `searchIndex: 4`,默认只展示前 4 个搜索条件,而 `expireStatus` 的 `searchOrder: 0`(Avue 越大越靠前)排在第 6 位,因此被折叠到第二行。
|
||||||
|
- 改动:
|
||||||
|
- `src/option/transportCapacity/transport-ship.js`:`searchIndex: 4 → 6`,让全部 6 个搜索条件默认展开,标签无需再点展开即可看到。
|
||||||
|
- 由于 `expireStatus` 排在最后,展开后它自然位于第二行末尾、搜索按钮之前,满足"移到搜索按钮前面"的要求。
|
||||||
|
|
||||||
|
## transportCapacity/vehicle 与 ship 到期筛选标签重构(对齐 driver 的 #search-menu 方案)
|
||||||
|
|
||||||
|
- 需求:用户希望 `/transportCapacity/vehicle` 和 `/transportCapacity/ship` 的「全部/30天内到期/已到期」标签与 driver 页一致:① 不展开就直接显示;② 移到搜索按钮前面并保留间距。
|
||||||
|
- 关键差异(排查发现):driver 用 `#search-menu` 插槽把标签渲染在**按钮所在行**;而 vehicle/ship 之前用 `#expireStatus-search` 列插槽,标签被渲染进**搜索条件网格**(位置不对)。
|
||||||
|
- 改动(两页同样处理):
|
||||||
|
1. `.vue`:`<template #expireStatus-search>` → `<template #search-menu>`,标签 div 内容不变。
|
||||||
|
2. `.vue` 样式 `__expiry-tags`:去掉 `width: 100%`,改为 `order: -1; margin-right: 12px; min-width: 0;`(与 driver 完全一致,使标签左移并落在搜索按钮前)。
|
||||||
|
3. option:将 `expireStatus` 列的 `search: true → search: false`(否则会在搜索网格里多渲染一个空输入框);`searchIndex` 设为 ≥ 真实搜索列数(vehicle: `4 → 6`;ship 已是 `6`),使 `columnLen ≤ searchIndex`,Avue 不再显示「展开」按钮。
|
||||||
|
- 原理:Avue `header-search3.js` 中 `#search-menu` 插槽内容渲染进 `menu-form`,位于默认搜索/重置按钮之后、展开按钮之前;`order: -1` 将标签推到按钮左侧。
|
||||||
|
- 验证:`node --check` 两个 option 文件通过;页面刷新即可见效果。
|
||||||
|
|
||||||
|
## process-config.vue 弹窗重构为手写 el-dialog(对齐 customer-archive 风格)
|
||||||
|
|
||||||
|
- 需求:用户要求 `/business/process-config.vue` 排版风格改成和 `/vehicle/customer-archive.vue` 一样,保持风格统一。
|
||||||
|
- 根因:两页弹窗架构根本不同——customer-archive 用手写 `<el-dialog>` + `section-card` 分组 + `#footer` 按钮;process-config 原用 Avue 内置弹窗(`#basicInfo-form`/`#nodeConfigJson-form` 插槽 + `#menu-form-before` 保存按钮)。要真正统一,只能把 process-config 改成手写弹窗。
|
||||||
|
- 模板改动(已完成):
|
||||||
|
- 移除 avue-crud 的 `:before-open`/`@row-save`/`@row-update`/`@row-del` 与 `#menu-form-before`、`#basicInfo-form`、`#nodeConfigJson-form` 插槽。
|
||||||
|
- 在 `</avue-crud>` 后新增手写 `<el-dialog class="process-dialog" v-model="processBox" width="92%" top="4vh" append-to-body @closed="resetProcess">`,内部 `<el-form ref="processForm" :model="form" label-width="calc(6em + 24px)" :disabled="dialogReadonly" class="process-form dialog-form-label-fixed">`,含 3 个 `section-card`:基本信息 / 现行过程节点(时间线)/ 节点设置(节点表格);`#footer` 含 保存草稿(plain) + 保存并启用(primary) + 取消/关闭。
|
||||||
|
- 顶部「新建过程配置」、表格「查看/编辑」链接改调 `openProcessConfig()` / `openProcessConfig(row, true)` / `openProcessConfig(row, false)`。
|
||||||
|
- 脚本改动(本次完成):删除 Avue 生命周期方法 `beforeOpen/rowSave/rowUpdate/submitRow/stopSubmitLoading/handleProcessConfigSave`;新增 `openProcessConfig(row, readonly)`(load 详情后 `this.form=data` + `selectedProjectId` + `parseNodeConfig` + 开框;新增则清空表单 + `resetNodeRows`)、`submitProcessConfig(mode)`(`this.$refs.processForm.validate` → `syncNodeFields` → `normalizeRow` → `validateRow` → `confirmDuplicateVoucher` → `api.submit`,成功后关框并刷新)、`resetProcess()`(`@closed` 重置 `dialogReadonly/submitting/submitStatus/form/selectedProjectId` + `resetNodeRows` + `clearValidate`)。`data()` 已含 `processBox/submitting`、`computed.dialogTitle`;`rowDel`/`handleCopy`/`handleAction` 不变(独立 API 调用)。
|
||||||
|
- option 改动(已完成):`src/option/business/process-config.js` 置 `saveBtn/updateBtn/addBtn/viewBtn/editBtn/delBtn:false`,关闭 Avue 内置弹窗按钮。
|
||||||
|
- 样式:新增 `:deep(.process-dialog .el-dialog__body){max-height:72vh;overflow:auto}`,对齐 customer-archive 的 `.archive-dialog` 滚动行为(长内容时 body 滚动、footer 固定)。
|
||||||
|
- 验证:`vite build --outDir /tmp/pc-build-check` 全量编译通过(仅动态导入 chunk 警告,与本改动无关);之前 `npm run build` 失败仅因沙箱 safe-delete 守卫清理旧 dist/ 触发,非代码错误。
|
||||||
|
|
||||||
|
## contract-manage 到期筛选标签靠右对齐
|
||||||
|
|
||||||
|
- 需求:用户截图反馈 `/business/contract-manage` 搜索栏中「全部/30天内到期/90天内到期/90天以上/已到期」标签需要靠右对齐。
|
||||||
|
- 改动:`src/views/business/components/business-crud-page.vue` 中 `.business-crud-page__contract-expiry-search` 样式由 `order: -1; margin-right: auto` 改为 `margin-left: auto`。这样该 slot 内容会被推到 `#search-menu` 行的右侧,搜索/重置按钮留在左侧,与 vehicle/ship 的左侧标签方案相反,符合用户本次要求。
|
||||||
|
- 验证:`vite build --outDir /tmp/pc-build-check-2` 全量编译通过。
|
||||||
|
|
||||||
|
## shipping-template 新增/编辑弹窗居中 + 加高(对齐 customer-archive)
|
||||||
|
|
||||||
|
- 需求:用户反馈 `/business/shipping-template` 新增弹窗「没居中、高度不够高」,要求参照 `/vehicle/customer-archive` 的做法。
|
||||||
|
- 根因排查:
|
||||||
|
- shipping-template 的弹窗是 avue-crud 内置弹窗(business-crud-page + `:crud-option`,`$refs.crud.rowAdd()` 触发),option 里 `dialogWidth:'96%'`、`dialogTop:'10px'`。
|
||||||
|
- avue 把 `dialogTop` 映射为 Element Plus `--el-dialog-margin-top`,`'10px'` 把弹窗死顶在顶部 → 视觉「没居中」。
|
||||||
|
- avue 内置 footer(`.avue-dialog__footer`)渲染在**可滚动 body 内部**,不像 customer-archive 那样用 `#footer` 插槽在滚动区之外;字段多时「保存」按钮要滚动才看得到 → 视觉「高度不够高」。
|
||||||
|
- customer-archive 真正做法是:手写 `<el-dialog width="92%" top="4vh">` + `#footer` 按钮浮动 + body `max-height:72vh` 内部滚动。
|
||||||
|
- 改动:
|
||||||
|
1. `src/option/business/shipping-template.js`:`dialogWidth:'96%'→'92%'`、`dialogTop:'10px'→'4vh'`,新增 `dialogCustomClass:'shipping-template-dialog'`(avue 会把它作为 class 加到 dialog 上,作为样式钩子)。
|
||||||
|
2. `src/styles/element-ui.scss`:新增 `.shipping-template-dialog` 规则——弹窗体 `height: calc(100vh - 4vh - 24px)` + flex 列布局占满视口(顶部 4vh、底部留白,视觉居中高大);body `max-height:none !important; overflow-y:auto` 解除全局 80vh 上限;`.avue-dialog__footer` 用 `position:sticky; bottom:0; background:#fff` 贴底始终可见,对齐 customer-archive 浮动按钮效果。
|
||||||
|
- 注意:class 唯一,仅作用于 shipping-template(business-crud-page 被多页共用,但其余页 option 未设该 class);弹窗 `append-to-body`,故样式必须放全局(element-ui.scss)而非组件 scoped 样式。
|
||||||
|
- 验证:dev server(2889) HMR 自动生效;刷新后重开「新增」即可见效果(未跑全量 build,改动为纯 CSS + option 属性)。
|
||||||
|
|
||||||
|
## transport-plan 新增/编辑弹窗固定 72vh(对齐 customer-archive 视觉)
|
||||||
|
|
||||||
|
- 需求:用户要求 `/business/transport-plan` 新增/编辑弹窗高度 72vh,与 `/vehicle/customer-archive` 视觉一致。
|
||||||
|
- 排查发现:上次给 shipping-template 加的 `.shipping-template-dialog` 全局 CSS 规则**未持久化**(option 钩子 `dialogCustomClass:'shipping-template-dialog'` 还在,但 `element-ui.scss` 里对应规则丢失),即 shipping-template 弹窗样式实际一直未生效。本次一并补回。
|
||||||
|
- 改动:
|
||||||
|
1. `src/option/business/transport-plan.js`:补 `dialogCustomClass:'transport-plan-dialog'`(option 已有 `dialogWidth:'96%'`、`dialogTop:'4vh'`)。
|
||||||
|
2. `src/styles/element-ui.scss`:
|
||||||
|
- 补回 `.shipping-template-dialog` 规则(弹窗体 `height: calc(100vh - 4vh - 24px)` 占满视口 + flex 列布局;body `max-height:none !important; overflow-y:auto`;`.avue-dialog__footer { position:sticky; bottom:0; background:#fff }` 贴底)。
|
||||||
|
- 新增 `.transport-plan-dialog` 规则(弹窗体用 `height:72vh` **固定**高度,内容少时 body 下方留白、内容多时窗内滚动;footer 同样 sticky 贴底)。
|
||||||
|
- 关键技术点:Avue 内置弹窗 footer 的真实 class 是 `.avue-dialog__footer`,且**渲染在 `.el-dialog__body` 内部**;贴底必须用 `position: sticky; bottom: 0`(flex `margin-top:auto` 在滚动容器内仍会随内容滚走,无效)。这与手写 el-dialog(footer 在 body 外、class 为 `.el-dialog__footer`)不同。
|
||||||
|
- 验证:dev server(2889) HMR 自动生效;刷新后重开「新增」即可见(弹窗固定 72vh、底部"提交/取消"常驻可见)。
|
||||||
|
|
||||||
|
## contract-manage 新增/编辑弹窗也改为接近全屏
|
||||||
|
|
||||||
|
- 用户要求 `/business/contract-manage` 新增/编辑弹窗同样接近全屏(与前几个 business 弹窗对齐)。
|
||||||
|
- 机制:该页同样用 `business-crud-page` 封装 → avue-crud 弹窗(带 `avue-crud__dialog` class),与 waybill-manage 完全一致。
|
||||||
|
- 改动:
|
||||||
|
1. `src/option/business/contract-manage.js`:该文件 option 在 `createCrudOption([...])` 之后本就用 `option.xxx = ...` 顶层赋值(span/dialogWidth/dialogTop 等),故在末尾追加 `option.dialogCustomClass = 'contract-manage-dialog';`(无需改结构,与文件既有写法一致)。
|
||||||
|
2. `src/styles/element-ui.scss`:新增 `.contract-manage-dialog` 四条规则,复刻 waybill-manage 方案——`height/max-height: calc(100vh - 40px) !important` 压过库内置 `.avue-dialog` 的 200px 上限;`.el-dialog__header { flex:none }`、`.el-dialog__body { flex:1 1 auto; min-height:0; max-height:none !important; overflow-y:auto }` 解开全局 avue-crud 200px body 限制;`.avue-dialog__footer { position:sticky; bottom:0; margin-top:0 !important; background:#fff }` 抵消全局 footer 的 -30px 上移。
|
||||||
|
|
||||||
|
## loading-manage 线路编辑「起/经/终」圆点分色
|
||||||
|
|
||||||
|
- 需求:用户反馈 `/business/loading-manage` 弹窗中"线路编辑"区域的「起/经/终」圆形徽标要区分颜色:起蓝、经橙、终绿。
|
||||||
|
- 改文件:`src/views/business/loading-manage.vue`。
|
||||||
|
- 涉及两处:① 主弹窗 `loading-manage-dialog__route-type`;② 变更运输路线弹窗 `loading-route-change-dialog__route-type`(保持全站一致)。
|
||||||
|
- 实现方式(最小侵入、不破坏现有结构):
|
||||||
|
1. methods 各加一个 `routeTypeClass(index)` / `routeChangeTypeClass(index)`,返回 `'start' | 'middle' | 'end'`。
|
||||||
|
2. 模板里 span 改用 `:class="['...route-type', '...route-type--' + ${xxxClass(index)}]"` 动态 class。
|
||||||
|
3. scoped 样式 `__route-type` 规则去掉硬编码 `background`,改用 `&--start/#409eff` + `&--middle/#e6a23c` + `&--end/#67c23a` 三个 modifier(颜色取 Element Plus 语义色:蓝主色 / 橙 warning / 绿 success),`color: #fff` 保留。
|
||||||
|
- 关键 trick:变更弹窗原 `route-type` 样式是单行压缩写法,本次顺手把它多行化(保持其他子规则不变),以便嵌套 `&--xxx` 修饰符。
|
||||||
|
- dev server(2889) HMR 自动生效,刷新后开新增/编辑弹窗向下滚到"线路编辑"分组即可见。
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
# 2026-08-20 工作日志
|
||||||
|
|
||||||
|
## 保险 OCR 识别模板弹窗列宽调整
|
||||||
|
|
||||||
|
- 调整文件:`src/views/base/insurance-ocr-template.vue`
|
||||||
|
- 修改内容:字段映射表格中,键名列由 `min-width="240"` 改为 `width="120" align="center"`;自定义映射值列由 `min-width="500"` 改为 `width="360"`。
|
||||||
|
- 原因:原列宽导致表格总宽度超出 860px 弹窗 body 可用区域,输入框右侧字数统计(0/100)被截断看不全。
|
||||||
|
|
||||||
|
## 保险 OCR 识别模板弹窗卡片化
|
||||||
|
|
||||||
|
- 调整文件:`src/views/base/insurance-ocr-template.vue`
|
||||||
|
- 修改内容:将弹窗内表单的白色背景 `div` 替换为全局 `<section-card title="模板信息">` 组件,形成白底圆角卡片;同时为自定义映射值列保留 `width="360"`,避免在卡片内被撑开。
|
||||||
|
- 原因:用户希望弹窗表单区域呈现卡片效果,与项目统一的 `section-card` 分组风格一致。
|
||||||
|
|
||||||
|
## 运输计划调度弹窗表头背景色调整
|
||||||
|
|
||||||
|
- 调整文件:`src/views/business/components/business-crud-page.vue`
|
||||||
|
- 修改内容:在“计划调度”弹窗的待调度列表 `el-table` 上增加 `:header-cell-style="{ background: '#fafafa' }"`,将表头背景由白色改为浅灰。
|
||||||
|
- 原因:用户反馈 `/business/transport-plan` 页面白色表头需统一改为灰色 `#fafafa`。
|
||||||
|
|
||||||
|
## 运单管理货物信息表头背景色调整
|
||||||
|
|
||||||
|
- 调整文件:`src/views/business/components/business-crud-page.vue`
|
||||||
|
- 修改内容:在 `goodsJson-form` 货物信息表格上增加 `:header-cell-style="{ background: '#fafafa' }"`,表头背景改为浅灰。
|
||||||
|
- 原因:用户反馈 `/business/waybill-manage` 页面货物信息表头需统一改为灰色 `#fafafa`。
|
||||||
|
|
||||||
|
## 全站 el-table 表头背景色统一
|
||||||
|
|
||||||
|
- 调整文件:`src/styles/element-ui.scss`(修改原仅限 `.avue-crud` 的表头规则)
|
||||||
|
- 修改内容:将表头背景规则扩展为全站 `.el-table` 表头(含普通表格、avue-crud、固定列),统一 `background-color: #fafafa !important`。
|
||||||
|
- 连带清理:移除 `business-crud-page.vue` 中两处冗余的 `:header-cell-style="{ background: '#fafafa' }"`(调度表、货物表),改由全局规则兜底。
|
||||||
|
- 影响:`/business/loading-manage`、`/business/waybill-manage`、`/business/transport-plan` 以及全站所有白色 el-table 表头均统一为 `#fafafa`。`!important` 同时覆盖了部分组件内 scoped 设定的 `#f5f7fa` 表头背景。
|
||||||
|
|
||||||
|
## 应收明细调整费用弹窗按钮间距
|
||||||
|
|
||||||
|
- 调整文件:`src/views/settlement/receivable-payable-detail.vue`
|
||||||
|
- 修改内容:为“调整费用”弹窗内的 `.settlement-detail-page__adjust-toolbar` 增加 `margin-bottom: 16px`,让“新增费用项”按钮与下方表格拉开间距。
|
||||||
|
- 原因:用户反馈 `/settlement/receivable-detail` 页面调整费用弹窗中“新增费用项”按钮与表格贴得太近。
|
||||||
|
|
||||||
|
## 应收明细生成费用弹窗搜索区域加白底
|
||||||
|
|
||||||
|
- 调整文件:`src/views/settlement/receivable-payable-detail.vue`
|
||||||
|
- 修改内容:为“生成费用”弹窗内的搜索 `el-form` 增加 class `settlement-detail-page__generate-search`,设置白底、圆角 6px、轻阴影,并与下方表格保留 16px 间距。
|
||||||
|
- 原因:用户反馈 `/settlement/receivable-detail` 页面生成费用弹窗中搜索区域缺少白底卡片效果。
|
||||||
|
|
||||||
|
## 应收明细更新费用弹窗表单加白底
|
||||||
|
|
||||||
|
- 调整文件:`src/views/settlement/receivable-payable-detail.vue`
|
||||||
|
- 修改内容:为“更新费用”弹窗的 `.settlement-detail-page__dialog-form` 增加白底、圆角 6px、轻阴影;将 padding 由 `16px 20px 0` 改为 `16px 20px`,并去掉最后一个表单项的底部外边距。
|
||||||
|
- 原因:用户反馈 `/settlement/receivable-detail` 页面更新费用弹窗中表单区域缺少白底卡片效果。
|
||||||
|
|
||||||
|
## 应收明细批量转结算弹窗搜索区域加白底
|
||||||
|
|
||||||
|
- 调整文件:`src/views/settlement/receivable-payable-detail.vue`
|
||||||
|
- 修改内容:为“批量转结算”弹窗的 `.settlement-detail-page__transfer-form` 搜索区域增加白底、圆角 6px、轻阴影,并保留与下方表格 16px 间距。
|
||||||
|
- 原因:用户反馈 `/settlement/receivable-detail` 页面批量转结算弹窗中搜索区域缺少白底卡片效果。
|
||||||
|
|
||||||
|
## 预结算单编辑弹窗改白底卡片分组
|
||||||
|
|
||||||
|
- 调整文件:`src/views/settlement/components/pre-settlement-editor.vue`
|
||||||
|
- 修改内容:将“新增/编辑预结算单”弹窗内的全部板块由自定义 `dialog-section-title` + `<section>` 改为全局 `<section-card>` 组件,包括:结算基本信息、结算合计、结算明细、附件、预付信息(readonly)、变更记录(readonly)。结算合计/结算明细头部右侧按钮改为 section-card 的 `#extra` 插槽;删除了不再使用的 `.pre-settlement-editor__section` 与 `.pre-settlement-editor__section-head` 样式,并去掉了 `.pre-settlement-editor__form` 多余的 `margin-top`。
|
||||||
|
- 间距:卡片间距沿用全局 `.section-card` 的 `margin-bottom: 12px`(符合用户要求)。候选弹窗(添加应收应付明细)的 `dialog-section-title` 保持不变。
|
||||||
|
- 原因:用户要求预结算单弹窗各板块使用白底卡片分隔,统一项目视觉规范。
|
||||||
|
|
||||||
|
## 应收明细详情弹窗 tabs 内容区背景改白
|
||||||
|
|
||||||
|
- 调整文件:`src/views/settlement/receivable-payable-detail.vue`
|
||||||
|
- 修改内容:在 scoped 样式末尾新增 `.settlement-detail-dialog` 规则块,对 `:deep(.el-tabs__content)` 设置 `background: #fff; padding: 16px; border-radius: 0 0 6px 6px;`,`:deep(.el-tab-pane)` 设为透明。
|
||||||
|
- 原因:全局 `.el-dialog__body { background: #f5f6fa }` 灰底透到 tabs 内容区(费用明细/变更记录),用户要求改为白色背景。
|
||||||
|
|
||||||
|
## 预结算单打印弹窗白底卡片化
|
||||||
|
|
||||||
|
- 调整文件:`src/views/settlement/pre-settlement.vue`
|
||||||
|
- 修改内容:将“打印”弹窗内的手写 `.dialog-section-title` + 普通 `el-form` 改为全局 `<section-card title="选择打印模板">` 包裹表单;为打印弹窗增加 `class="pre-settlement-print-dialog"`;删除页面底部不再使用的 `.dialog-section-title` 样式块,并去掉 `.pre-settlement-page__print-form` 的 `margin-top`。
|
||||||
|
- 原因:用户要求 `/settlement/pre-settlement` 打印弹窗按项目规范使用白底卡片分组,与预结算单编辑弹窗风格统一。
|
||||||
|
|
||||||
|
## 正式结算付款申请弹窗白底卡片化
|
||||||
|
|
||||||
|
- 调整文件:`src/views/settlement/formal-settlement.vue`
|
||||||
|
- 修改内容:将“付款申请”弹窗内的普通 `el-form` 改为全局 `<section-card title="付款信息">` 包裹;为弹窗增加 `class="formal-payment-dialog"`;按全站按钮规范将“提交”按钮由 `type="primary"` 改为 `type="success"`。
|
||||||
|
- 原因:用户要求 `/settlement/formal-settlement` 付款申请弹窗使用白底卡片分组,并与项目统一按钮风格对齐。
|
||||||
|
|
||||||
|
## 正式结算单编辑弹窗改白底卡片分组
|
||||||
|
|
||||||
|
- 调整文件:`src/views/settlement/components/formal-settlement-editor.vue`
|
||||||
|
- 修改内容:将“新增/编辑正式结算单”弹窗内的全部板块由 `dialog-section-title` + `<section>` 改为全局 `<section-card>` 组件,包括:结算基本信息、来源预结算单、结算明细、附件、付款信息(readonly)。“来源预结算单”头部按钮改为 section-card 的 `#extra` 插槽;删除了不再使用的 `.formal-editor__section` 与 `.formal-editor__section-head` 样式。
|
||||||
|
- 间距:沿用全局 `.section-card` 的 `margin-bottom: 12px`。
|
||||||
|
- 原因:用户要求 `/settlement/formal-settlement` 页面与预结算单弹窗统一使用白底卡片分隔。
|
||||||
|
|
||||||
|
## 正式结算选择预结算单/选择结算明细弹窗搜索区白底
|
||||||
|
|
||||||
|
- 调整文件:`src/views/settlement/components/formal-settlement-editor.vue`
|
||||||
|
- 修改内容:为"选择预结算单"和"选择结算明细"两个弹窗内的搜索 `el-form` 分别增加 class `formal-editor__candidate-search` / `formal-editor__detail-search`,统一样式:白底、圆角 6px、12px 内边距、16px 底间距、轻阴影。
|
||||||
|
- 原因:用户反馈 `/settlement/formal-settlement` 页面"选择预结算单"弹窗搜索区域缺少白底卡片效果。
|
||||||
|
|
||||||
|
## 运输对账单编辑弹窗改白底卡片分组
|
||||||
|
|
||||||
|
- 调整文件:`src/views/settlement/components/transport-reconciliation-editor.vue`
|
||||||
|
- 修改内容:将"新增/编辑运输对账单"弹窗内的全部板块由 `dialog-section-title` + `<section>` 改为全局 `<section-card>` 组件,包括:导入外部账单与内部账单核对、内部账单、导入外部账单、操作说明。"导入外部账单"头部按钮改为 section-card 的 `#extra` 插槽;删除了不再使用的 `.reconciliation-editor__section` 与 `.reconciliation-editor__section-head` 样式。
|
||||||
|
- 间距:沿用全局 `.section-card` 的 `margin-bottom: 12px`。
|
||||||
|
- 验证:已通过 SFC 解析与模板编译检查。
|
||||||
|
- 原因:用户要求 `/settlement/transport-reconciliation` 页面与预结算单/正式结算单弹窗统一使用白底卡片分隔。
|
||||||
|
|
||||||
|
## 结算调整单编辑弹窗改白底卡片分组
|
||||||
|
|
||||||
|
- 调整文件:`src/views/settlement/components/settlement-adjustment-editor.vue`
|
||||||
|
- 修改内容:将"新增/编辑结算调整单"弹窗内的全部板块由 `dialog-section-title` + `<section>` 改为全局 `<section-card>` 组件,包括:基本信息、调整费用。"调整费用"头部"添加费用"按钮改为 section-card 的 `#extra` 插槽;删除了不再使用的 `.settlement-adjustment-editor__section`、`.settlement-adjustment-editor__section-head` 以及 `.dialog-section-title` 样式块。
|
||||||
|
- 间距:沿用全局 `.section-card` 的 `margin-bottom: 12px`。
|
||||||
|
- 验证:已通过 SFC 解析与模板编译检查。
|
||||||
|
- 原因:用户要求 `/settlement/settlement-adjustment` 页面与预结算单/正式结算单弹窗统一使用白底卡片分隔。
|
||||||
|
|
||||||
|
## 运单详情起终点区域改垂直时间轴(方案B)
|
||||||
|
|
||||||
|
- 调整文件:`src/views/business/components/business-crud-page.vue`
|
||||||
|
- 修改内容:在 `isWaybillDetailLayout` 区块,将 `__waybill-route` 的横向“起/终方块+横杠”改为垂直时间轴:左列两个圆点(蓝 `#409eff` / 橙 `#e6a23c`,带白圈光晕),之间用绝对定位的虚线(`repeating-linear-gradient`)连接;右列为“发货地/收货地”标签 + 地名(`-webkit-line-clamp: 2` 允许长名换行)+ 省市区地址(`el-tooltip` 悬浮显示完整地址)。
|
||||||
|
- 删除旧类:`__waybill-route-head`、`__waybill-route-text` 及其样式(已确认全仓无其它引用)。
|
||||||
|
- 响应式:1200px 断点下的 `grid-row:auto; grid-column: 1/-1` 规则仍生效,垂直轴在窄屏自动转为整行。
|
||||||
|
- 原因:用户认为原右上角起终点方块像按钮、地址被挤压截断、层级不清晰,选择方案B(垂直时间轴)。
|
||||||
|
|
||||||
|
## 多联总单编辑页收发货信息布局整改
|
||||||
|
|
||||||
|
- 调整文件:`src/views/business/components/master-order-editor.vue`(仅样式,逻辑未动)
|
||||||
|
- 修改内容:
|
||||||
|
1. `.route-step-icon` 加 `min-width: 32px; min-height: 32px; flex-shrink: 0;`,并给外层 `:deep(.el-step__icon)` 加 `flex-shrink: 0;` —— 修复竖向时间轴「起/经/终」节点(尤其「经」字)被压扁变形。
|
||||||
|
2. `.route-node-form` grid 由 `minmax(360px,1.8fr) minmax(200px,1fr) minmax(260px,1.2fr) minmax(260px,1.2fr)` 改为 `repeat(2, minmax(420px,1fr)) repeat(2, minmax(260px,1fr))`;新增 `:deep(.el-form-item.route-address-form-item) { grid-column: span 2; }` —— 地址项独占前两列(更宽)。
|
||||||
|
3. `.route-address-name`(行政区划 cascader)由 `flex: 0 0 140px` 加宽到 `220px` —— 完整显示「大坡服务区(梧州段)」等较长名称。
|
||||||
|
4. 响应式 `@media (max-width:1440px)` 内 `.route-node-form` 改 `repeat(2, minmax(320px,1fr))` 并同步 `.route-address-form-item { grid-column: span 2 }` —— 小屏地址项独占整行。
|
||||||
|
- 原因:客户反馈该页收发货信息中行政区划/详细地址输入框过窄导致长地址被截断,且左侧时间轴节点变形。改完用户刷新 localhost:2889 查看效果。
|
||||||
|
|
||||||
|
## master-order 弹窗搜索栏白底补齐
|
||||||
|
- `src/views/business/components/master-order-editor.vue` line 1815-1818:把 `.common-cargo-dialog__search` 单条白底规则改写为 `.common-address-dialog__search, .common-cargo-dialog__search { background: #fff }`,让"选择常用地址"弹窗的搜索栏也跟随既有白卡样式(其他 `.route-dialog__search` 默认无该 class,仍保留灰底)。
|
||||||
|
|
||||||
|
## 合同管理合同名称列左对齐修复
|
||||||
|
|
||||||
|
- 调整文件:`src/views/business/components/business-crud-page.vue`、`src/option/business/contract-manage.js`
|
||||||
|
- 修改内容:
|
||||||
|
1. 在 `business-crud-page.vue` 的 `#contractName` 插槽内,用 `<div class="business-crud-page__contract-name">` 包裹 `el-link` / `span` 内容。
|
||||||
|
2. 在 `business-crud-page.vue` 的 scoped SCSS 中新增 `.business-crud-page__contract-name { text-align: left; }`。
|
||||||
|
3. 保留 `contract-manage.js` 中 `contractName` 列的 `align: 'left'` / `headerAlign: 'left'` 配置(虽然 Avue slot 列的 align 未生效,作为兜底仍保留)。
|
||||||
|
- 原因:用户反馈 `/business/contract-manage` 的合同名称列文字仍居中。Avue 自定义 slot 列的 `align` 属性似乎没有正确作用到单元格,改由插槽内容自身强制 `text-align: left` 更可靠。
|
||||||
|
- 影响:所有使用 `business-crud-page` 且渲染 `contractName` 插槽的页面,其合同名称单元格内容都会左对齐。
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
# 2026-08-21
|
||||||
|
|
||||||
|
## temporary-credit-limit 详情弹窗上下居中(align-center)
|
||||||
|
- 需求:`/business/temporary-credit-limit` 详情弹窗(detailBox,走 business-crud-page 自定义详情)上下居中。
|
||||||
|
- 机制:Element Plus el-dialog 的 `align-center` prop —— use-dialog 源码确认:align-center 时 `.el-overlay-dialog` 设 `display:flex`,`.el-dialog.is-align-center { margin:auto }`,实现垂直+水平居中;此时 top prop 设置的 `--el-dialog-margin-top` 被更高特异性的 `margin:auto` 覆盖,不生效。
|
||||||
|
- 改动(3 处,开关式不影响其他页):
|
||||||
|
1. `src/views/business/components/business-crud-page.vue` 详情 el-dialog(2504 行)加 `:align-center="detailAlignCenter"`。
|
||||||
|
2. 同文件新增 computed `detailAlignCenter()`:`this.config.detailAlignCenter === true`(默认 false,contract/waybill 等共用详情弹窗不受影响)。
|
||||||
|
3. `src/option/business/temporary-credit-limit.js` config 加 `detailAlignCenter: true`。
|
||||||
|
- 安全性:全局 `.el-dialog__body { max-height: calc(100vh - 200px); overflow-y:auto }`(element-ui.scss 182 行)保证内容超高时在 body 内滚动,dialog 总高 < 视口,flex 居中不会顶部裁切。
|
||||||
|
- 注意:element-plus 实际版本是 2.14.2(package.json 声明 ^2.10.1,锁到 2.14.2)。el-dialog 的 class 经 $attrs 透传最终落在 `div.el-dialog` 根元素(ElDialogContent 未设 inheritAttrs:false);因此 scoped 里 `&__detail-dialog.is-transport-plan-detail { :deep(.el-dialog){...} }` 的 max-width/margin 规则实际选不到自身(仅 header/body 后代规则生效)——历史遗留,未处理。
|
||||||
|
- 验证:`node --check` 通过;Vite 2889 编译两个改动文件均 HTTP 200。
|
||||||
|
|
||||||
|
## temporary-credit-limit 弹窗宽度 1400px → 96%
|
||||||
|
- 需求:`/business/temporary-credit-limit` 新增/编辑弹窗加宽到 96%。
|
||||||
|
- 改动(单文件 `src/option/business/temporary-credit-limit.js`):`dialogWidth: 1400` → `dialogWidth: '96%'`。
|
||||||
|
- 先例:waybill-manage / transport-plan 均为 `dialogWidth: '96%'`,shipping-template 为 `'92%'`。business-crud-page 对 dialogWidth 无特殊处理(仅独立表单页强制 100%),百分比直接透传 avue-crud;element-ui.scss 的 `.temporary-credit-limit-dialog` 只覆 label 宽度、不覆盖 el-dialog 宽度,无冲突。
|
||||||
|
|
||||||
|
## process-config 弹窗卡片间隔(对齐 project-apply 风格)
|
||||||
|
- 需求:process-config 新增/编辑弹窗内三张 `<section-card>`(基本信息 / 现行过程节点 / 节点设置)要像 project-apply 表单页一样独立成卡、卡间露出灰底(12px)。
|
||||||
|
- 根因:全站规则 `.el-dialog .avue-form { background:#fff; padding:8px 16px 4px }`(element-ui.scss ~246 行)把 Avue 弹窗表单整体包成一张白卡,section-card 白卡叠在其上看不出间隔。
|
||||||
|
- 改动(仅 2 文件):
|
||||||
|
1. `src/option/business/process-config.js`:option 顶层加 `dialogCustomClass: 'process-config-dialog'`。
|
||||||
|
2. `src/styles/element-ui.scss`(contract-manage-dialog 规则块后新增):
|
||||||
|
- `.process-config-dialog .avue-form { background:transparent; box-shadow:none; padding:0 }` —— 白卡透明化,灰底(.el-dialog__body 全局 #f2f2f2)露出,section-card 自带 margin-bottom:12px 形成卡距。
|
||||||
|
- `.process-config-dialog .avue-form__group > .el-col > .el-form-item { margin-bottom:0 }` —— formslot 外层 form-item margin 与卡片 margin 叠加会拉大卡距,归零统一 12px。
|
||||||
|
- `.process-config-dialog .avue-dialog__footer` + `.avue-crud__dialog .process-config-dialog .avue-dialog__footer { margin-top:0 !important }` —— 抵消全局 947 行 `.avue-crud__dialog .avue-dialog__footer { margin-top:-30px !important }`(form 透明化后会上移压卡)。双选择器兼顾 dialog 未 teleport / teleport 两种挂载。
|
||||||
|
- 已验证:Vite 2889 编译通过、HMR 生效。
|
||||||
|
- 备注:`menu-form-before` 插槽(保存草稿/保存并启用按钮)渲染在 `.avue-dialog__footer` 内(avue 源码 dialog-form3.mjs 确认),不受 form 透明化影响,仍处白底底栏。
|
||||||
|
|
||||||
|
## 合同管理表单页:多分组卡片风格(对齐 project-apply 风格)
|
||||||
|
- 需求:合同管理新增/编辑表单页(`/business/contract-manage/form?mode=add|edit`)的 7 个业务分组(基本信息 / 合同文件 / 计费信息 / 结算单规则 / 付款比例设置 / 其它附件 / 变更记录)要像 project-apply 一样独立成白底卡、卡间 12px 间距、标题 4px 主色蓝条。
|
||||||
|
- 关键事实(容易踩坑):
|
||||||
|
- 合同表单走 `PageAvueForm` 组件(`src/views/business/components/business-crud-page.vue:5577`),render 函数 `h('div', { class: option.dialogCustomClass }, [h('avue-form', ...)])`,**不包 el-dialog**(所以底部按钮天然不浮动贴底,会随内容滚动——这点与 project-apply 的 el-dialog #footer 浮动有差异)。
|
||||||
|
- `option` 顶层在 `isStandaloneFormPage` 时已设 `dialogCustomClass: 'contract-manage-form-dialog'`、`dialogAppendToBody: false`、`dialogModal: false`、`dialogTop: '0'`、`dialogWidth: '100%'`(5897-5914 行)。
|
||||||
|
- 7 个分区标题列在 option.column 里以 `order` 升序分块(`basicInfoTitle=200` → `contractFileTitle=30` → `billingInfoTitle=10` → `settlementRuleTitle=-10` → `paymentRatioTitle=-30` → `attachmentTitle=-50` → `changeRecordTitle=-70`),其余字段 order 在每对标题列之间。
|
||||||
|
- 实施方案(已提交 63256a9):
|
||||||
|
1. `buildContractFormGroupOption(option)`(business-crud-page.vue 新增方法):按 order 降序遍历 option.column,遇到 7 个 `*Title` 列就切片分组;`groups.length<=1` 时回退原 option(不影响其他业务弹窗)。返回 `{ ...option, column: groups[0], group: groups.slice(1).map(g=>({ label:'', arrow:false, column:g })) }`。
|
||||||
|
2. `pageFormOption`(6205 行)`isContractFormPage` 时调用上述方法。
|
||||||
|
3. CSS(business-crud-page.vue scoped 末尾):
|
||||||
|
- `.contract-manage-form-dialog` 设 `padding:20px 24px 96px; background:#f5f6fa; box-sizing:border-box;`(外层灰底)
|
||||||
|
- `.business-crud-page--contract-form .avue-form { background:transparent; padding:0 }`(表单白卡透明化)
|
||||||
|
- `.business-crud-page--contract-form .avue-form > .el-form > .avue-form__group` 和 `.avue-group` 都设白底 + 6px 圆角 + 0 1px 2px 阴影 + 12px margin-bottom + 14px 16px 4px padding(两张选择器分别覆盖第 1 组 column 与第 2-7 组 group 渲染)
|
||||||
|
- 验证(agent-browser + 系统 Chrome,已登录后截图核对):
|
||||||
|
- 合同表单 7 张白底卡清晰分隔,每张卡左侧 4px 主色蓝条 + 标题(基本信息 / 合同文件 / 计费信息 / 结算单规则 / 付款比例设置 / 其它附件 / 变更记录),卡间约 12px 灰底露白。
|
||||||
|
- 底部 5 个按钮(提交 / 关闭 / 保存 / 提交临时合同 / 提交正式合同)随内容滚动(非浮动贴底)。
|
||||||
|
- 与 project-apply 滚动后的"客户信息 / 承运商信息"卡风格完全一致。
|
||||||
|
- 与 project-apply 的已知差异(用户本次未要求改):
|
||||||
|
- 合同表单底部按钮非浮动贴底(PageAvueForm 不包 el-dialog,avue-form 的 form_menu 渲染在内部)
|
||||||
|
- 合同表单无"返回"按钮(用"关闭"代替)
|
||||||
|
- 合同表单无独立页面标题(page-header 区域在 breadcrumb 处,不在 main 标题区)
|
||||||
|
- 行为验证码:登录页拼图/旋转求解基础设施已搭好——`/tmp/captcha_solve.py`(CORS 修复后支持 puzzle+rotate 双模式)、`/tmp/ab-install/node_modules/.bin/agent-browser --browser-channel chrome`。登录流程:访问 → 切"账号密码登录" → 点登录 → 检测 `.puzzle-stage`/`.rotate-image` → POST 至 http://127.0.0.1:8765 → 取 xDisp/angle → JS PointerEvent 模拟拖动 .track-handle 即可。
|
||||||
|
- 提交:63256a9 feat(business-crud-page): 合同管理表单页改为多分组卡片风格(1 file, +86/-2)。未提交改动(process-config.js、element-ui.scss、master-order-editor.vue、loading-manage.vue、project-apply.vue、formal-settlement.vue、customer-archive.vue)与本任务无关,未纳入。
|
||||||
|
|
||||||
|
## shipping-template 独立表单页加 archive-page-form__title(参考 customer-archive)
|
||||||
|
- 需求:独立表单页 `/business/shipping-template/form?mode=add&name=新运费发货模板` 顶部缺页面标题,参考 customer-archive 的 `.archive-page-form__title` 加标题。
|
||||||
|
- 改动(单文件 `src/views/business/components/business-crud-page.vue`):
|
||||||
|
1. `PageAvueForm`(5578 行)声明 `props.formPageTitle`,render 时若非空则先 push `<div class="archive-page-form__title">{{ title }}</div>` 再渲染 avue-form(title 在表单容器 `shipping-template-dialog` 内、avue-form 之前)。
|
||||||
|
2. 模板 `<component :is="crudContainer">` 新增 `:form-page-title="isStandaloneFormPage ? formPageTitle : undefined"`。
|
||||||
|
3. 新增 computed `formPageTitle()`:`isStandaloneFormPage && isShippingTemplatePage` 时返回 `this.$route.query.name || '新增'/'编辑' + config.title`,否则 ''。
|
||||||
|
4. 样式(scoped 末尾,用 `:global` 因为 title div 由 PageAvueForm 函数式组件渲染、无 scoped 属性):`:global(.business-crud-page--form-page .archive-page-form__title)` 复制 customer-archive 的 18px/600/#303133 + margin-bottom:20px(::before 为空,与参考一致,仅纯文字标题)。
|
||||||
|
- 仅作用于 shipping-template 独立表单页;其他独立表单页(waybill/transport-plan/contract)`formPageTitle` 返回 '',不受影响。
|
||||||
|
|
||||||
|
## transport-plan 独立表单页也加 archive-page-form__title
|
||||||
|
- 需求:`/business/transport-plan/form?mode=add&name=新运费运输计划` 也要同样的页面标题。
|
||||||
|
- 改动:`formPageTitle()` 条件由 `isStandaloneFormPage && isShippingTemplatePage` 改为 `isStandaloneFormPage && (isShippingTemplatePage || isTransportPlanPage)`(复用已有 `isTransportPlanPage` computed:`config.permission==='transport_plan'`)。标题文案取 `this.$route.query.name`(「新运费运输计划」)或回退「新增/编辑运输计划」。
|
||||||
|
- 验证:Vite 2889 编译通过(HTTP 200,无错误)。
|
||||||
|
- 验证:Vite 2889 编译通过(HTTP 200,无 transform/syntax 错误),HMR 生效。如需扩展到其他独立表单页,去掉 `isShippingTemplatePage` 限定即可。
|
||||||
|
|
||||||
|
## 将 .archive-page-form__title 全局化,并再加 waybill / loading-manage / master-order 三页
|
||||||
|
- 需求:把页面标题扩展到 `/business/waybill-manage/form`、`/business/loading-manage/form`、`/business/master-order?mode=editor` 三页,并与 customer-archive / shipping-template / transport-plan 视觉统一。
|
||||||
|
- **全局化样式**:`.archive-page-form__title` 原本分散在 `customer-archive.vue`(scoped)与 `business-crud-page.vue`(`:global`,::before 为空纯文字)。现统一提取到 `src/styles/element-ui.scss`(5b 段):18px/600/#303133 + margin-bottom:20px + `::before` 4px 主色竖条 + 8px 间距(补全竖条,与 section-card 卡头风格一致)。并删除上述两处本地重复定义。
|
||||||
|
- **waybill-manage/form**:在 `business-crud-page.vue` 的 `formPageTitle()` 条件再并入 `isWaybillDetailLayout`(`config.permission==='waybill_manage'`),复用既有 computed。标题取 `query.name || '新增/编辑运单管理'`。
|
||||||
|
- **loading-manage/form**:该路由直接映射到 `loading-manage.vue`(非 business-crud-page)。新增 computed `formPageTitle()`(`query.name || '新增/编辑配载管理'`),并在独立表单页的 `<component>` 内、body 之前加 `<div v-if="isStandaloneFormPage" class="archive-page-form__title">{{ formPageTitle }}</div>`。
|
||||||
|
- **master-order?mode=editor**:`master-order.vue` 渲染 `master-order-editor.vue`。将 `<master-order-editor>` 包进 `<template v-else-if="mode==='editor'">`,前置 `<div class="archive-page-form__title">{{ masterEditorTitle }}</div>`;新增 computed `masterEditorTitle`(`routeId ? '编辑总单' : '新增总单'`)。
|
||||||
|
- 验证:4 个 SFC + element-ui.scss 经 Vite 2889 编译均 HTTP 200、无 transform/syntax 错误。
|
||||||
|
|
||||||
|
## 综合台账弹窗 & 临时额度查看弹窗 布局调整
|
||||||
|
- vehicle-ledger / ship-ledger(/transportCapacity 车辆·船舶综合台账弹窗):把 `<el-table>`+分页从 `el-tabs` 外移入每个 `<el-tab-pane>` 内(切换 tab 表格跟着切换);ship-ledger 同步把三个蓝标内容块(船舶基本信息/船舶证书信息/证书附件)由 `<section>`+`.dialog-section-title` 改为全局 `<section-card :title>`,卡片间距 12px。两文件均加 `:deep(.el-tabs__content){padding:0}` 去 border-card 默认内边距。
|
||||||
|
- 临时额度查看弹窗改两列(/business/temporary-credit-limit):
|
||||||
|
- 原"查看"调 Avue 内置 `rowView`(列数复用 column.span,呈 3 列)。改用 business-crud-page 内置自定义详情弹窗。
|
||||||
|
- `src/option/business/temporary-credit-limit.js` config 加 `detailButton:true`、`detailButtonIgnoreDraftStatus:true`、`detailSections:[申请信息(申请单号/备注 span:2 整行,其余两列)、额度信息]`。
|
||||||
|
- `business-crud-page.vue` 的 `showDetailButton` 加 `if(this.config.detailButtonIgnoreDraftStatus) return true`(仅放开草稿态也走自定义详情,不动其他页面)。
|
||||||
|
|
||||||
|
## contract-manage/form 独立表单页也加 archive-page-form__title
|
||||||
|
- 需求:`/business/contract-manage/form?mode=add&name=新运费合同管理` 也要同样顶部页面标题。
|
||||||
|
- 改动:`business-crud-page.vue` 的 `formPageTitle()` 条件再并入 `isContractConfig`(`config.permission==='contract_manage'`,contract-manage 已用 `contract-form-page` prop 走 `PageAvueForm`)。标题取 `query.name`(「新运费合同管理」)或回退「新增/编辑合同管理」。
|
||||||
|
- 验证:Vite 2889 编译通过(HTTP 200,无错误),HMR 生效。至此 business-crud-page 系列(shipping-template / transport-plan / waybill-manage / contract-manage)独立表单页均已带统一标题。
|
||||||
|
- 渲染走 `detailSections` 分支(`section-card` + `el-descriptions(:column=2)`),值经 `formatDetailValue` 转字典 label。
|
||||||
|
- 验证:`temporary-credit-limit.js` 经 `node --check` 语法通过;business-crud-page.vue 仅 template 表达式改动。待用户在 2889 刷新核对两列效果。
|
||||||
|
|
||||||
|
## 合同管理详情弹窗 label 对齐修复
|
||||||
|
- 需求:`/business/contract-manage` 详情弹窗里两列 `el-descriptions` 的 label(尤其右边列 label)对齐不一致,长 label(如"是否需要加盖法人章")换行后行高/垂直位置错乱。
|
||||||
|
- 根因:`business-crud-page.vue` 的 `&__detail-content .section-card` 中对 `.el-descriptions__label` 只写了 `width: 140px` 与 `text-align: right`,缺 `min/max-width`、`vertical-align`、`line-height`、`box-sizing`;对 `.el-descriptions__content` 也缺垂直对齐,导致跨列/跨行 label 视觉上不齐。另外 `contract-manage.vue` 底部的 `<style>`(未加 `scoped`)把 `.el-dialog .el-form-item__label` 与 `.el-col` 全局覆盖,会污染全站弹窗。
|
||||||
|
- 改动:
|
||||||
|
1. `src/views/business/components/business-crud-page.vue`(15440 行附近):
|
||||||
|
- label 与 content 统一加 `box-sizing:border-box`、`vertical-align:middle`、`line-height:1.4`、一致 padding;label 加 `min-width:140px; max-width:140px` 防止被内容撑开或压缩,content 保持 `min-height:42px`。
|
||||||
|
- **新增 `:deep(.el-descriptions__table) { width:100%; table-layout:fixed }`**:强制表格按固定列宽渲染,label 占 140px 后,左右两个 content 列均分剩余宽度,解决用户截图里"合同期限"卡片左右内容区宽度不一致的问题。
|
||||||
|
2. `src/views/business/contract-manage.vue`:把底部无 scoped 的全局样式改为 `<style scoped>`,并把选择器限定到 `.contract-manage-dialog`;删除污染性更强的 `.el-col { margin-bottom:0 !important }` 全局覆盖。
|
||||||
|
- 验证:Vite 2889 编译通过(HTTP 200),HMR 生效。待用户在 2889 刷新核对详情弹窗两列 label 是否左右/上下对齐、左右内容区宽度是否均等。
|
||||||
|
|
||||||
|
## 应付明细调整弹窗:控件撑满单元格 + 表头居中
|
||||||
|
- 需求:`/settlement/payable-detail`(即 `receivable-payable-detail.vue`)"调整费用"弹窗中,里程 / 杂项 / 装卸费等列的 `el-input-number` 默认宽度 180px,超出窄列后被截断(见截图);同时要求所有表头居中。
|
||||||
|
- 改动(单文件 `src/views/settlement/receivable-payable-detail.vue`):
|
||||||
|
1. 给调整弹窗 `<el-table>` 加 `class="settlement-detail-page__adjust-table"`。
|
||||||
|
2. 页面级表格样式 `.settlement-detail-page__table, .settlement-detail-page :deep(.el-table)` 的 `th.el-table__cell` 增加 `text-align: center !important;`,统一所有表头居中(覆盖金额列 `align: right` 导致的表头右偏)。
|
||||||
|
3. 新增 `.settlement-detail-page__adjust-table` 样式块:`.el-input-number`、`.el-input`、`.el-select` 全部 `width: 100% !important;`,使控件撑满单元格不被截断;`el-input-number` 内部输入保持右对齐。
|
||||||
|
- 验证:Vite 2889 编译通过(HTTP 200),HMR 生效。待用户在 2889 刷新核对调整弹窗输入框是否完整显示、表头是否全部居中。
|
||||||
|
|
||||||
|
## formal-settlement:把表格放进 tabs
|
||||||
|
- 需求:`/settlement/formal-settlement` 原本 `el-tabs`(应付/应收) 是空的 type 切换器,表格+工具栏在 tabs 外面;要求把表格放进 tabs 内。
|
||||||
|
- 改动:
|
||||||
|
1. 新建 `src/views/settlement/components/formal-settlement-table-panel.vue`:把工具栏(新增/付款申请/同步金蝶/打印结算单/导出 + 刷新) + `el-table` + 分页整体抽成子组件;props 收 `rows/columns/loading/page/hasPermission`,emits `create/payment/sync/export/refresh/page-change/update:selection/action`;行操作(查看/编辑/删除/提交/通过/驳回/作废)经 `rowActions(row)` 计算可见项后 emit `action {type,row}`;工具栏"打印"经 `emitSingle('print')` 取选中单行后 emit action(与父 `selectedOne` 行为一致)。
|
||||||
|
2. `src/views/settlement/formal-settlement.vue`:删除原 toolbar+table+pagination 块与对应 scoped 样式(`__toolbar/__toolbar-right/__links/__pagination`),在 `el-tabs` 两个 `el-tab-pane` 内各放一个 `<formal-settlement-table-panel>`(DRY,共用同一组件);新增 `handlePageChange({current,size})`、`handleAction({type,row})` 分发行操作;`handlePrint(row)` 改为接收行参数(不再用 selectedOne);删除无用 `handleSizeChange`。
|
||||||
|
- 验证:Vite build 通过(EXIT=0),无编译错误。待用户在 2889 刷新核对:应付/应收两个 tab 各自带完整表格与工具栏、切换 tab 触发 reload。
|
||||||
|
|
||||||
|
## 预结算单结算汇率输入框居左
|
||||||
|
- 需求:`/settlement/pre-settlement` 弹窗中「结算汇率」字段(el-input-number)默认居中,要求改为居左。
|
||||||
|
- 改动:
|
||||||
|
1. `src/option/settlement/preSettlementForm.js`:`exchangeRate` 字段新增 `class: 'exchange-rate-input'`。
|
||||||
|
2. `src/views/settlement/components/pre-settlement-editor.vue`:
|
||||||
|
- `el-input-number` 上绑定 `:class="field.class"`。
|
||||||
|
- scoped style 的 `&__form` 内增加 `:deep(.exchange-rate-input .el-input__inner) { text-align: left; }`。
|
||||||
|
- 影响:仅针对「结算汇率」字段,不改动其他数字输入框对齐方式。
|
||||||
|
|
||||||
|
## transport-reconciliation:同样把表格放进 tabs
|
||||||
|
- 需求:`/settlement/transport-reconciliation` 与 formal-settlement 同样问题——`el-tabs`(应付/应收) 是空 type 切换器、工具栏+表格在 tabs 外;要求把表格放进 tabs 内。
|
||||||
|
- 改动:
|
||||||
|
1. 新建 `src/views/settlement/components/transport-reconciliation-table-panel.vue`:工具栏(新增/导出+刷新)+`el-table`+分页抽成子组件;props 同 formal 面板,emits `create/export/refresh/page-change/update:selection/action`;列渲染差异:`.link` 列点击 emit view、`reconciliationStatusName` 走 `row.reconciliationStatus==='completed'?'success':'warning'` 的 el-tag;行操作 `rowActions(row)` 为 查看(常显)+编辑/删除/确认(均 `hasPermission && isEditable(row)`,isEditable=`reconciliationStatus==='unfinished'`),确认非 danger。
|
||||||
|
2. `src/views/settlement/transport-reconciliation.vue`:删除原 toolbar+table+pagination 块与对应 scoped 样式(`__toolbar/__toolbar-right/__links/__pagination`),在两个 `el-tab-pane` 内各放一个面板;保留 `.reconciliation-page__settlement-tabs`(含 `:deep(.el-tabs__header){margin:0}`);新增 `handlePageChange`、`handleAction({type,row})`(view→openView/edit→openEdit/delete→handleDelete/complete→handleComplete);删除 `handleSizeChange`。导出按钮无权限守卫、仍 emit `export` 由父 `handleExport`(用 `this.settlementType`)处理。
|
||||||
|
- 验证:Vite build 通过(EXIT=0)。待用户在 2889 刷新核对应付/应收 tab 各自带完整表格与工具栏、切换触发 reload。
|
||||||
|
|
||||||
|
## process-config 弹窗重写为手写 el-dialog(对齐 credit-score-quantification 风格)
|
||||||
|
- 原因:用户参考 `/vehicle/credit-score-quantification` 的弹窗(手写 `el-dialog` + 多个 `<section-card>` 堆叠、卡间 12px、底部 #footer 按钮),要求 process-config 弹窗同样"白底卡片包住蓝标内容块、卡间距 12px、风格统一"。原实现用的是 Avue 内置 dialog + formslot,与参考页机制不一致(即使视觉接近)。
|
||||||
|
- 改动(3 文件):
|
||||||
|
1. `src/views/business/process-config.vue`:删除 Avue 内置 dialog 相关(`before-open`、`#basicInfo-form`/`#nodeConfigJson-form`/`#menu-form-before` 插槽、`rowSave/rowUpdate/submitRow/stopSubmitLoading/handleProcessConfigSave`、数据项 `submitStatus`);新增手写 `<el-dialog class="process-config-dialog" append-to-body width="96%" top="4vh" :close-on-click-modal="false">`,内部 `<div class="process-config-form">` 堆三张 `<section-card>`:基本信息 / 现行过程节点(timeline)/ 节点设置(node 表);`#footer` 放 取消 / 保存草稿(plain) / 保存并启用(primary),`submitting` 控制 loading。新增 `openConfig(row, readonly)`、`submitConfig(mode)`、`handleDialogClosed`,保留全部节点配置逻辑(NODE_TEMPLATES/cloneNode/parseNodeConfig/syncNodeFields/timelineDescriptions/validateRow/checkEnabledProjectBeforeSubmit/confirmDuplicateVoucher 等)。菜单 查看→`openConfig(row,true)`、编辑→`openConfig(row)`、新建→`openConfig()`。
|
||||||
|
2. `src/option/business/process-config.js`:option 删除仅作表单容器的 `basicInfo`/`nodeConfigJson` 两列与 `saveBtn/updateBtn/dialogTop/dialogWidth/dialogCustomClass` 等 dialog 配置。
|
||||||
|
3. `src/styles/element-ui.scss`:删除原先 `.process-config-dialog .avue-form / .avue-form__group / .avue-dialog__footer` 三处透明化/归零规则(已无用),改为 `.process-config-dialog .el-dialog__body { max-height: calc(100vh - 130px) }`。
|
||||||
|
- 关键点:弹窗内容走 `append-to-body` 会被 teleport 出 `.process-config-page`,故所有弹窗内样式(timeline/node-table/require-*/finish-days)改用顶层 scoped 类 `.process-config-form__*`(不再嵌套在 `.process-config-page` 下),否则 scoped 选择器因缺祖先类而失效。12px 卡距由全局 `.section-card{margin-bottom:12px}` + 全局灰底 `.el-dialog__body{background:#f2f2f2;padding:12px}` 保证,与参考页一致。
|
||||||
|
- 验证:Vite build 通过(EXIT=0),无编译错误;2889 HMR 生效。待用户刷新核对弹窗卡片风格。
|
||||||
|
|
||||||
|
## 多页面「基本信息」类分区统一改为 4 列(span 8→6)
|
||||||
|
- 用户逐一要求把各结算/合同页的「基本信息」分区从 3 列改为 4 列。列数由 el-col `:span` 控制(24/8=3 → 24/6=4)。
|
||||||
|
- 手写 el-col 循环页面(直接改 span):
|
||||||
|
- `src/views/settlement/components/formal-settlement-editor.vue`:`:span="8"`→`:span="6"`(结算基本信息)。
|
||||||
|
- `src/views/settlement/components/transport-reconciliation-editor.vue`:`:span="8"`→`:span="6"`(导入外部账单核对)。
|
||||||
|
- `src/views/settlement/components/settlement-adjustment-editor.vue`:`:span="8"`→`:span="6"`(基本信息)。
|
||||||
|
- `src/views/settlement/components/pre-settlement-editor.vue`:动态 `:span="field.span || 8"`→`|| 6`(仅备注显式 span:24 仍整行)。
|
||||||
|
- `src/views/business/contract-manage-change.vue`:`基本信息` 区 `:span="8"`→`:span="6"`(变更类型/备注 `:span="24"` 保持整行)。
|
||||||
|
- Avue 分组表单页面(合同新增/编辑 form 页):非手写循环,`option.span=8` 控制默认列数。
|
||||||
|
- 改动 `src/views/business/components/business-crud-page.vue` 的 `buildFormGroupOption`:当 `this.isContractConfig` 时,对首组(基本信息,=groups[0] 顶层 column)每个非分区标题、非 `span===24` 的列设 `col.span = 6`;备注(span:24)与分区标题列保持原样。其余分组仍 3 列。
|
||||||
|
- 验证:2889 HMR 生效。注意合同 form 页走 Avue group,不能用简单 span 替换,须改 buildFormGroupOption。
|
||||||
|
|
||||||
|
## 续:更多页面「基本信息」类分区改 4 列(span 8→6)
|
||||||
|
- `src/option/business/temporary-credit-limit.js`:Avue 配置页,扁平 column 列表,8 个表单字段 `span:8`→`span:6`(备注/附件 span:24 保持整行)。注意 createCrudOption 不设默认 span,可见字段都有显式 span。
|
||||||
|
- `src/views/transportCapacity/ship.vue`:「基础船舶信息」10 个 `:span="8"`→`:span="6"`;该页 12 空格缩进=基础船舶信息区、14 空格缩进=船舶证书信息区,**勿用全局 replace_all**(12 空格串是 14 空格的子串会误伤证书区),必须按行号区间 sed(146,253)或带 label 上下文逐个替换。
|
||||||
|
- `src/views/business/project-apply.vue`:`.project-apply-form__grid` `repeat(3,...)`→`repeat(4,...)`;label 规则(作用域 `__grid` 内)`width:100px !important; min-width:0 !important; white-space:normal`——6 汉字宽超出换行,`!important` 压过 label-width="auto" 的行内 width 与全局 min-width:160px。
|
||||||
|
|
||||||
|
## ship 页 label 规则:最多 6 汉字宽、超出换行
|
||||||
|
- `src/views/transportCapacity/ship.vue`:el-form `label-width`(原 HEAD 为 auto,工作区历史为 150)→ `100`(6 汉字≈84px+冒号≈98px,100px 恰容 6 字);`.ship-form` 内新增 `:deep(.el-form-item__label) { white-space: normal; line-height: 20px }` 允许换行。适用整个 ship 表单(含证书区)。
|
||||||
|
- 注意:改该文件前先确认 git 暂存区状态,`git checkout -- <file>` 恢复的是 index 版本;本文件 index=HEAD=auto,工作区曾为 150(历史未提交改动),已被本次需求覆盖。
|
||||||
|
|
||||||
|
## master-order-dispatch 起-终路由条左侧节点被裁切修复
|
||||||
|
- 问题:`/business/master-order?mode=dispatch` 顶部 overview 的路由节点条(起/经/终),最左侧"起(梧州段)"被裁掉半字。根因:`.route-overview` 用 `justify-content: flex-end` 右对齐 + `overflow-x:auto`,节点总宽(4×130+3×56=688px)超出右侧栏可用宽度时,溢出从左侧跑出可视区,且节点 `flex:0 0 130px` 写死、长名字会撑爆。
|
||||||
|
- 改动(`src/views/business/components/master-order-dispatch.vue` scoped 样式,3 处):
|
||||||
|
1. `.route-overview`:`justify-content:flex-end` → `flex-start`;padding 改为 `8px 24px 0`(左右留白,滚动时两端节点完整可见)。
|
||||||
|
2. `.route-overview__node`:`flex:0 0 130px` → `flex:0 0 auto; min-width:130px; max-width:200px; padding:0 4px`;strong/small 加 `max-width:100%; overflow:hidden; text-overflow:ellipsis`(长名字省略号不撑爆)。
|
||||||
|
3. `.route-overview__line`:`flex:1 0 56px; min-width:56px` → `flex:0 1 56px; min-width:24px`(line 可收缩,滚动兜底更平滑)。
|
||||||
|
- 说明:1200px media query 里原有 `justify-content:flex-start` 现与默认一致,冗余但无害,保留未删。仅样式改动,无 JS 逻辑变化。
|
||||||
|
|
||||||
|
## master-order-dispatch 路由节点地址/联系人加 tooltip(hover 提示完整内容)
|
||||||
|
- 需求:起-终节点条地址过长截断("九头山货..."等)时,鼠标移上去能看到完整内容。
|
||||||
|
- 改动(`src/views/business/components/master-order-dispatch.vue` 模板,21-45 行):起/经、终节点各自的 strong(名称)+ 两个 small(地址、联系人)共 6 个文本元素分别用 `<el-tooltip placement="top" :show-after="120">` 包裹,`:content` 与原文本一致;保持原有 `text-overflow:ellipsis` 截断视觉不变。
|
||||||
|
- `:show-after="120"` 让鼠标停留 120ms 才弹,避免快速划过连续触发多个 tooltip;`placement="top"` 配合节点垂直居中位置弹出,体验稳定。
|
||||||
|
- 风险:仅模板增加 6 个 el-tooltip 实例,无 JS/style 改动;与之前段头 `el-tooltip`(segment-checkbox-label)用法一致。
|
||||||
|
|
||||||
|
## shipping-template(发货模板)表单页:模板信息+基本信息 4 列 + label 统一宽度
|
||||||
|
- 需求:`/business/shipping-template/form` 的「模板信息」「基本信息」两组改 4 列;左侧 label 宽度一致右对齐;全站统一(6 汉字宽、超出换行)。
|
||||||
|
- 改动 3 处:
|
||||||
|
1. `business-crud-page.vue` `buildFormGroupOption` 新增 `isShippingTemplatePage` 分支:对 groups[0](模板信息)、groups[1](基本信息)的非标题、非 span24 列设 `span:6`(该页分组:templateInfoTitle/basicInfoTitle/shippingInfoTitle/goodsInfoTitle/attachmentTitle 均已在 sectionTitleProps,发货信息及之后组保持原样)。
|
||||||
|
2. `src/option/business/shipping-template.js`:option 顶层加 `labelWidth: 100`(覆盖 createCrudOption 的 'auto',全表单 label 一致 100px;右对齐已由 labelPosition:'right' 保证)。
|
||||||
|
3. `src/styles/element-ui.scss` `.shipping-template-dialog .el-form-item__label { white-space: normal; line-height: 20px }`(长 label 换行,独立表单页容器也是 div.shipping-template-dialog,class 命中)。
|
||||||
|
- 模式沉淀:contract(isContractConfig 首组)/ shipping-template(前两组)都走 buildFormGroupOption 统一处理;普通 el-col 循环页直接改 span;CSS grid 页改 repeat 数 + label 规则。4 列=span 6,6 汉字=100px。
|
||||||
|
|
||||||
|
## transport-plan / waybill-manage 表单页:整表单 4 列 + label 统一(全站统一)
|
||||||
|
- `business-crud-page.vue` `buildFormGroupOption` 重构:抽取 `setFourColumns(group)` helper(非标题、非 span24 列设 span:6);contract→首组、shipping-template→前两组、transport_plan/waybill_manage→**所有分组**统一 4 列(这两页很多字段无显式 span 会落到 Avue 默认 12=2 列,是之前不 4 列的原因)。
|
||||||
|
- `waybill-manage.js` option 加 `labelWidth:100`(transport-plan.js 上轮已加);element-ui.scss 追加 `.waybill-manage-dialog .el-form-item__label` 换行规则(transport-plan-dialog 上轮已加)。
|
||||||
|
- 经验:Avue 表单「没 4 列」先查是否有字段漏设 span(默认 12);统一 4 列优先走 buildFormGroupOption 的页面级分支,避免逐个字段改。
|
||||||
|
|
||||||
|
## ⚠️ option.labelWidth 必须放在 createCrudOption 展开之后
|
||||||
|
- 第一次给 transport-plan.js、waybill-manage.js 加 `labelWidth: 100` 时写在 `...createCrudOption(...)` **之前**,被 createCrudOption 自带 `labelWidth: 'auto'` 覆盖,label 仍是 auto(按内容参差,看着像"150 不统一")。shipping-template 当时写在 createCrudOption 之后才生效。
|
||||||
|
- 教训:业务 option 文件顶层 `export const option = { labelWidth: 100, ...createCrudOption([...]) }` 写法**无效**,必须 `export const option = { ...createCrudOption([...]), labelWidth: 100, dialogWidth, ... }` 后写覆盖前写。
|
||||||
|
|
||||||
|
## payable-detail 主表格单据号列固定左侧
|
||||||
|
- 需求:`/settlement/payable-detail` 主表格除序号外,「单据号」列也固定左侧(横向滚动时始终可见)。
|
||||||
|
- 改动(2 处):
|
||||||
|
1. `src/option/settlement/receivable-payable-detail.js`:`documentNo` 列加 `fixed: 'left'`。
|
||||||
|
2. `src/views/settlement/receivable-payable-detail.vue`:主表格 v-for 动态列加 `:fixed="column.fixed"`(模板需显式透传才生效)。
|
||||||
|
- 注意:payable-detail 与 receivable-detail 共用同一组件,应收明细页同步生效(两侧一致)。左侧固定区 = 选择框52 + 序号64 + 单据号180。
|
||||||
|
|
||||||
|
## project-apply 列表列全部居中
|
||||||
|
- 需求:`/business/project-apply` avue-crud 列表所有列居中看效果。
|
||||||
|
- 改动(单文件 `src/views/business/project-apply.vue` `buildTableOption()`):column map 展开时加 `align:'center'` + `headerAlign:'center'`。avue 中 align 只管单元格内容,表头需 headerAlign 单独设。
|
||||||
|
- 弹窗内子表格(客户/承运商/材料/变更记录/选人)本就 align="center",无需动。Vite 2889 已 HMR。
|
||||||
|
|
||||||
|
## project-apply 审批状态列改纯文字
|
||||||
|
- 需求:审批状态列从 el-tag 改为纯文字。
|
||||||
|
- 改动(单文件 `src/views/business/project-apply.vue`):`#approvalStatus` 插槽去掉 `<el-tag>` 包裹,直接输出 `displayStatus(row,'approvalStatus')`;删除已无引用的 `statusTagType()` 方法。Vite 2889 编译 200。
|
||||||
|
|
||||||
|
## 全站表格列居中(含操作列)
|
||||||
|
- 需求:project-apply 居中效果被确认后,要求操作列按钮也居中,并应用到全站表格。
|
||||||
|
- 改动(`src/styles/element-ui.scss`,2 处全局规则):
|
||||||
|
1. `.avue-crud .avue-crud__menu.avue-crud__menu`:`justify-content: start` → `center`,`text-align: left` → `center`(Avue 操作列按钮居中,含下拉更多按钮)。
|
||||||
|
2. 表头背景规则后新增全站规则:`.el-table th.el-table__cell .cell, .el-table td.el-table__cell .cell { text-align: center }`(所有 el-table 表头+单元格内容居中,命中固定列;.cell 直接设置覆盖 el-table-column align 的继承值)。
|
||||||
|
- project-apply.vue 页面级 `align:'center'` 保留(双保险,无害);手写表格操作列 `.cell:has(> .el-link)` 本就 `justify-content:center`。
|
||||||
|
- 注意:全站居中会覆盖个别页面金额列 `align: right` 等显式右对齐,后续若某页要右对齐需页面级更高特异性覆盖。
|
||||||
|
|
||||||
|
## ⚠️ Vite optimizeDeps 缓存坑(patch node_modules 后必须重启 dev server)
|
||||||
|
- 修改 `node_modules/@smallwei/avue/lib/...` 这种深度依赖后,HMR **不会重新打包**:Vite 首次启动时已把整个 Avue ES module 预打包到 `node_modules/.vite/deps/@smallwei_avue.js`,缓存的 chunk 持续被 dev server 返回,导致代码修改"看起来没生效"。
|
||||||
|
- 解法(任选其一):
|
||||||
|
1. 重启 dev server(Vite 会重新 optimizeDeps)。
|
||||||
|
2. 手动删 `node_modules/.vite/deps/@smallwei_avue.js` + `_metadata.json`,再让 server 收到请求时触发重新打包(最干净,连同 `_metadata.json` 一起删,否则 fileHash 缓存会让它误以为已最新)。
|
||||||
|
- 经验:以后再 patch 任何被 Vite optimizeDeps 预打包的依赖(如 element-plus / avue / wangeditor 等),都要重启 server 才能生效。
|
||||||
|
|
||||||
|
## 全站弹窗/表单 label 去掉冒号(pnpm patch 修复 Avue 默认值)
|
||||||
|
- 需求:全站所有 el-form / avue-form 的字段 label(车站名称:、所属省份: 等)后冒号要去掉。
|
||||||
|
- 关键事实(重要,避免重复踩坑):
|
||||||
|
- Element Plus 的 label-suffix 不是用 `::after` 渲染,而是把后缀直接拼接到 label 文本节点:
|
||||||
|
```js
|
||||||
|
// node_modules/element-plus/es/components/form/src/form-item.vue...mjs:115
|
||||||
|
const currentLabel = computed(() => `${props.label || ""}${formContext?.labelSuffix || ""}`);
|
||||||
|
```
|
||||||
|
- EP el-form 的 labelSuffix prop 默认 `''`(空),不产生冒号;冒号来自 Avue。
|
||||||
|
- Avue 默认 `labelSuffix: ":"`,逻辑 `this.tableOption.labelSuffix || ":"`(空串 falsy 回落 `":"`),透传给 el-form 的 `label-suffix` prop。
|
||||||
|
- **踩坑**:Avue package.json 有 `"module": "es/index.mjs"` 和 `"main": "lib/index.js"`。Vite 用 ES 模块版(`es/`),不是 CommonJS 版(`lib/`)。**pnpm patch 必须同时覆盖 `es/` 和 `lib/` 两个文件**,否则 Vite 加载的 `es/` 版本未被 patch,冒号照旧!
|
||||||
|
- 另一个坑:Vite 把 Avue 预打包到 `node_modules/.vite/deps/@smallwei_avue.js`。patch 后必须删整个 `.vite` 目录 + 重启 dev server,否则预打包缓存仍是旧代码。
|
||||||
|
- 实施(pnpm patch + 手动补 es/ 版本):
|
||||||
|
1. `pnpm patch @smallwei/avue` → 临时目录,编辑 `lib/.../index2.js`。
|
||||||
|
2. `pnpm patch-commit <临时目录>` → 生成 `patches/@smallwei__avue.patch`(仅含 lib/ 改动)。
|
||||||
|
3. **手动更新 patch 文件**:追加 `es/packages/element-plus/form/index2.mjs` 的 diff(同样 `|| ":"` → `|| ""`)。
|
||||||
|
4. **手动编辑 node_modules** 中 `es/.../index2.mjs`(pnpm patch 只生成了 lib/ 的改动,es/ 版本需手动改)。
|
||||||
|
5. 删 `node_modules/.vite` 整个目录 → 重启 `pnpm dev` → Vite 重新预打包(含 es/ patch)→ 浏览器强刷。
|
||||||
|
- patch 文件:`patches/@smallwei__avue.patch`(含 es/ + lib/ 两处 diff)。
|
||||||
|
- pnpm 配置:`pnpm-workspace.yaml` 内 `patchedDependencies: { '@smallwei/avue': patches/@smallwei__avue.patch }`。
|
||||||
|
- ⚠️ 提交时务必带上:`patches/`、`pnpm-workspace.yaml`、`pnpm-lock.yaml`,缺一不可。
|
||||||
|
- ⚠️ `pnpm install` 会从 patch 文件重新应用补丁——如果 patch 文件没有 es/ 的 diff,es/ 版本会被还原为原始代码(冒号回来)。已手动补上 es/ diff 到 patch 文件。
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# 2026-08-24
|
||||||
|
|
||||||
|
## 发货模板查看弹窗白底卡片统一化
|
||||||
|
- 问题:shipping-template 查看弹窗仍走 Avue 默认 rowView,各板块无白底卡片分隔。
|
||||||
|
- 改动:
|
||||||
|
- `src/option/business/shipping-template.js`:config 新增 `detailButton: true`、`detailButtonIgnoreDraftStatus: true`,并配置 `detailSections`(模板信息/基本信息/收发货信息),每板块两列 el-descriptions,备注与地址占整行。
|
||||||
|
- `src/views/business/components/business-crud-page.vue`:在 detailBox 通用详情模板中,货物信息卡片后新增运费信息 section-card,区分公路(单价/数量合计/运费/运费合计/其他运费合计/币种)与非公路(运费/数量合计/其他运费合计/币种)展示。
|
||||||
|
- 样式:`&__detail-content` 已统一 `gap: 12px`,section-card 间距即为 12px;新增 `&__freight-descriptions` 单位/币种辅助色。
|
||||||
|
- 验证:`npm run build` 通过。
|
||||||
|
|
||||||
|
## 发货模板查看弹窗 label 宽度统一
|
||||||
|
- 问题:详情弹窗各 section 的 el-descriptions 按内容自动列宽,导致「收发货信息」板块含 6 字标签(发货联系方式/收货联系方式)时 label 列比其他板块(4 字标签)更宽,视觉不一致。原 CSS `.el-descriptions__label{width:140px}` 在含 span 行的 fixed 布局下未能可靠等宽。
|
||||||
|
- 改动:
|
||||||
|
- `src/views/business/components/business-crud-page.vue` 在通用详情描述(line ~3109)与运费信息两张描述表(road / 非 road)上加 `label-width="140px"`。
|
||||||
|
- `src/option/business/shipping-template.js`:将「收发货信息」字段顺序调整为联系人/联系方式在前、地址整行在后(非 span 行置于首行,让 `table-layout: fixed` 能在首行正确建立两列 label 宽度)。
|
||||||
|
- 验证:`npm run build` 通过。
|
||||||
|
|
||||||
|
## 全站业务状态标签文字化(调度状态/审批状态)
|
||||||
|
- 新增全局 `.status-text` 工具类(`src/styles/element-ui.scss` 末尾):去掉 `el-tag` 背景/边框,保留彩色文字 + `currentColor` 圆点。
|
||||||
|
- 全站所有业务状态 `el-tag` 统一加 `class="status-text"`:business-crud-page(#status/#businessStatus/#approvalStatus 插槽 + 运输计划详情头 + 运单详情头 + 调度弹窗头)、master-order 系列 businessStatus、loading-manage 详情弹窗、结算三处 approvalStatus、process-config 流程状态。
|
||||||
|
- 已通过 dev (localhost:2889) HMR 生效,刷新即可查看。调度状态(businessStatus)全站视觉已统一为"文字 + 颜色"。
|
||||||
|
- `credit-score-quantification.vue` 状态列(#status 插槽,statusMap: 正常/停用/草稿)的 el-tag 也加 `.status-text` 统一。
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
# 2026-08-25
|
||||||
|
|
||||||
|
## 全站操作列按钮居中统一
|
||||||
|
- 根因:pre-settlement 等手写操作列用 `<div class="xxx__links">` 包裹 `el-link`,而全局 `element-ui.scss` 第 904 行原选择器为 `:has(> .el-link)`(仅命中直接子元素),导致包了一层 div 的操作列未被居中规则命中,按钮靠左。
|
||||||
|
- 修复:将 `.el-table .el-table__body .cell:not(.avue-crud__menu)` 相关的三处选择器 `:has(> .el-link)` 全部放宽为 `:has(.el-link)`(第 904 / 935 / 954 行),兼容"el-link 被 div 包裹"的手写操作列;`.avue-crud__menu`(Avue 内置操作列)原已居中不受影响。
|
||||||
|
- 另在 `pre-settlement.vue` 的 `.pre-settlement-page__links` 补 `justify-content:center` 兜底。
|
||||||
|
- 结论:全站所有基于 el-link 的操作列(无论是否包裹 div)统一居中,符合 AGENTS.md 操作列规范。
|
||||||
|
|
||||||
|
## 全站业务状态标签改为普通文字(无颜色圆点)
|
||||||
|
- 根因:用户反馈审批状态列显示为带彩色圆点的文字(如截图),要求改成"普通的文字不要颜色"。此前 `.status-text` 是"文字 + 彩色圆点"风格。
|
||||||
|
- 修复:修改 `src/styles/element-ui.scss` 中 `.el-tag.status-text` 全局规则,去掉 `&::before` 圆点,并将 `color` 强制设为 `var(--el-text-color-regular)`,`font-weight:normal`,`margin:0`,背景边框保持透明。所有带 `class="status-text"` 的 `el-tag` 自动生效,无需逐页改模板。
|
||||||
|
- 结论:全站业务状态(调度状态/运单状态/审批状态等)统一为普通文字,不再带彩色圆点。
|
||||||
|
|
||||||
|
## /payment 下全部表单页板块风格统一
|
||||||
|
- 需求:所有 /payment 表单页(invoice-receipt / invoice-application / payment-application / bill-ledger / bill-payment / receipt-claim-record / receipt-flow)统一为"页面标题无蓝标 + 每个蓝标板块用白底卡片包裹 + 板块间距 12px",参考 business/project-apply/form。
|
||||||
|
- 实现:页面标题改用全局 `archive-page-form__title`(蓝标已注释,无竖条);各 `<section class="X-form-page__section"><div class="X-form-page__section-title">标题</div>` 整体改为全局组件 `<section-card title="标题">`(白底卡片 + 4px 主色竖条 + 卡间 12px,由 `src/styles/element-ui.scss` 全局 `.section-card` 提供);带按钮的板块(附件信息/开票内容)用 `<section-card title="..."><template #extra>按钮</template>`。
|
||||||
|
- 清理:删除各文件 scoped 中 `__title`/`__section` 相关的废弃 CSS(蓝标 ::before、板块间距、heading 等);`payment-application` 的 `.payment-form-page__section-actions`(按钮行右对齐)属有效独立样式,清理时被误删,已补回。
|
||||||
|
- 校验:用 @vue/compiler-sfc 对 7 个文件做 template/script 编译,全部通过。
|
||||||
|
|
||||||
|
## /payment 板块表单控件统一 4 列(span=6)
|
||||||
|
- 需求:用户要求 /payment 下"板块表单控件都改成 4 列"。此前列数不一致:多数页用 `el-col :span="12"`(2 列),invoice-application 用 `:span="8"`(3 列),bill-payment / bill-ledger 是"两列各叠多个控件"的嵌套结构。
|
||||||
|
- 实现:
|
||||||
|
- 直接网格(每列一个控件):payment-application、invoice-receipt、receipt-claim-record、receipt-flow 的 `:span="12"` → `:span="6"`;invoice-application 的 `:span="8"` → `:span="6"`(两处 el-row)。
|
||||||
|
- 嵌套网格:bill-payment、bill-ledger 拆成每个字段独立 `el-col :span="6"`(真 4 列),gutter 48→24;备注保留 `:span="24"` 整行。
|
||||||
|
- 媒体查询同步:`.el-col-12`/`.el-col-8` 窄屏折叠规则改成 `.el-col-6`(≤1024/1200/900/768px 折叠为整行)。
|
||||||
|
- 结论:全 /payment 表单页板块表单控件统一为 4 列网格,备注/长文本仍整行;pre-settlement-editor「结算基本信息」此前也已改 `:span="8"`→`span="6"`。
|
||||||
|
|
||||||
|
## 全站 el-tag 状态语义统一转纯文本(status-text)
|
||||||
|
- 需求:用户要求"全站看看还有哪些地方使用 el-tag 的?都要统一改一下",保持与调度状态一致的纯文本风格。
|
||||||
|
- 范围确认:用 AskUserQuestion 确认采用"所有状态语义"——业务状态(审批/调度/运单/匹配/认领) + 启用停用 + 工作流激活挂起/完成 + 其它状态(statusName/是否封存/否是/认证/锁状态/合同阶段) 一律转 `.status-text` 纯文本;类型标签/版本号/分类名/值类(车牌颜色、锁类型、运输方式、categoryName、code、apiKey 等) 保留 tag。
|
||||||
|
- 改动文件(约 35 个,仅加 `class="status-text"`,不动逻辑/`:type` 绑定):
|
||||||
|
- 启用/停用:base(railway-station/airport-master/currency/customer-type/fee-item/port-terminal)、system/dept、transportCapacity(vehicle/driver/ship)、authority(apikey/ratelimit)
|
||||||
|
- 业务状态遗漏补:settlement/formal-settlement、business/temporary-credit-limit、business/contract-manage(合同阶段+审批)、payment(invoice-application/bill-payment-table/receipt-flow/receipt-claim-record/invoice-receipt/payment-application)、settlement/components/transport-reconciliation-editor(匹配结果/状态/更新结果)、transit/risk-disposal(column.tag/field.tag)
|
||||||
|
- 工作流/其它:flow(manager/follow 激活挂起)、work(start 激活挂起/send 完成)、transportCapacity/vehicle 认证状态、system/authlock lockStatus、tool/codesetting、resource(sms/oss) statusName、vehicle/customer-archive(审批+启用停用)、system/dict+dictbiz 及 util/demo 各 dict 页 是否封存
|
||||||
|
- 校验:重新 grep `el-tag[^>]*:type=` 确认除 authlock lockType、vehicle-ledger plateColor 两个刻意保留的"类型标签"外,所有状态语义 el-tag 均已带 status-text。改动为纯属性添加,Vite HMR 自动生效。
|
||||||
|
|
||||||
|
## 业务状态文字化后字号/行高对齐单元格
|
||||||
|
- 根因:用户截图反馈 customer-archive 的"准入类型/审批状态/状态"列文字比其它列小/错位。`.el-tag` 默认 `font-size: var(--el-tag-font-size)`(12px)且 `height` 固定,即便 `.status-text` 去掉了背景边框,字号和盒模型仍与单元格普通文本不一致。
|
||||||
|
- 修复:在 `src/styles/element-ui.scss` 的 `.el-tag.status-text` 全局规则追加 `font-size: inherit !important; height: auto !important; vertical-align: baseline; line-height: inherit;`,强制继承父级字号与行高,高度随内容自适应,基线对齐与普通 inline 文本一致。
|
||||||
|
- 结论:所有使用 `.status-text` 的状态列(customer-archive 准入类型/审批状态/状态、transport-plan 调度状态、waybill 运单状态、payment 审批状态、启用停用等)自动与相邻普通文字列的字体字号/行高/基线完全统一,无需逐页修改。
|
||||||
|
|
||||||
|
## 临时额度详情弹窗长 label 折成两行
|
||||||
|
- 需求:用户截图反馈 `temporary-credit-limit` 详情弹窗中,"已使用项目资金使用额度(万元)" 和 "剩余项目资金使用额度(万元)" label 被折成三行,希望只折两行。
|
||||||
|
- 根因:`business-crud-page` 自定义详情弹窗(`detailBox`)使用 `el-descriptions :column="2"`,`.el-descriptions__label` 在 `business-crud-page__detail-content` 里被全局固定 `width: 140px`,15 个汉字的超长 label 在 140px 下被挤成三行。
|
||||||
|
- 修复:
|
||||||
|
- 让 `business-crud-page` 的详情弹窗根节点也携带页面对话框的 `dialogCustomClass`(在 `:class` 数组中加入 `$attrs.option?.dialogCustomClass`),使 `temporary-credit-limit-dialog` 类能作用到详情弹窗。
|
||||||
|
- 在 `temporary-credit-limit.vue` 全局 `<style>` 中新增 `.temporary-credit-limit-dialog .business-crud-page__detail-content .el-descriptions__label { width: 180px !important; }`,只对该页详情 label 加宽,让 15 字 label 自然折成两行。
|
||||||
|
- 改动文件:`src/views/business/components/business-crud-page.vue`、`src/views/business/temporary-credit-limit.vue`。
|
||||||
|
- 结论:仅临时额度管理详情弹窗的 label 列宽加宽到 180px,其它页面详情弹窗保持 140px 不变。
|
||||||
|
|
||||||
|
## 年检记录费用字段单位放 label
|
||||||
|
- 需求:用户截图反馈 `annual-inspection-record` 新增弹窗中"费用"输入框右侧带"元" suffix,要求把"元"放到 label 后并用括号括起,即"费用(元)",保持风格一致。
|
||||||
|
- 改动文件:
|
||||||
|
- `src/option/vehicle/annual-inspection-record.js`:`label` 由 "费用" 改为 "费用(元)";移除 `append: '元'`(因表单走自定义 slot,该属性本不生效,但清理避免歧义)。
|
||||||
|
- `src/views/vehicle/annual-inspection-record.vue`:自定义 `#feeForm` 的 `<el-input>` 移除 `<template #append>元</template>`。
|
||||||
|
- 结论:新增/编辑弹窗中费用字段 label 显示为"费用(元)",输入框不再带右侧"元" suffix。
|
||||||
|
|
||||||
|
## 维修记录里程字段单位放 label(动态单位)
|
||||||
|
- 需求:用户要求 `maintenance-record` 把里程输入框右侧"公里" suffix 移到 label 后括起来,风格与"费用(元)"一致。
|
||||||
|
- 差异:该字段有单位切换(车辆→公里,船舶→海里),故 label 需动态。
|
||||||
|
- 改动文件:`src/views/vehicle/maintenance-record.vue`(option 内联在 data 中,无独立 option 文件):
|
||||||
|
- 默认 `label` 由 "里程/航程数" 改为 "里程/航程数(公里)"。
|
||||||
|
- `updateVehicleType()` 中原 `mileageColumn.append = mileageUnit;` 改为 `mileageColumn.label = \`里程/航程数(${mileageUnit})\``,跟随车船类型动态切换标签(车辆→公里 / 船舶→海里)。
|
||||||
|
- 自定义 `#mileageForm` 的 `<el-input>` 移除 `<template #append>{{ form.mileageUnit || '公里' }}</template>`。
|
||||||
|
- 结论:表单/列头 label 随车船类型显示"里程/航程数(公里)"或"里程/航程数(海里)",输入框不再带单位 suffix;列表数据值仍由 `#mileage` slot 的 `formatMileage()` 带单位显示(如"100 公里"),未改动。
|
||||||
|
|
||||||
|
## 全站 Avue CRUD 弹窗标题加页面全称
|
||||||
|
- 需求:新增/编辑/查看弹窗标题统一加全名称,如"新增空港机场主数据"(原只显示"新增"/"编辑"/"查看")。
|
||||||
|
- 根因:Avue 弹窗标题默认走 i18n 的 addTitle/editTitle/viewTitle,全站页面均未配置,故只有动作词。
|
||||||
|
- 实现:新增全局 mixin `src/utils/crud-dialog-title.js` 的 `setupCrudDialogTitle(app)`,在 `main.js` 中 `app.mount` 前调用注册;组件 `created` 时自动给含 `column` 数组的 `this.option` 注入 `addTitle/editTitle/viewTitle = 新增/编辑/查看 + 页面菜单中文标题`(来源 `this.$router.$avueRouter.generateTitle(this.$route,{label:'name'})`);页面可手动配置这三个字段覆盖,或定义 `getCrudPageTitle()` 自定义来源;已配置则不覆盖。
|
||||||
|
- 影响范围:全站约 80 个 avue-crud 页面自动生效;手写 el-dialog / PageAvueForm 独立表单页不受影响。
|
||||||
|
- 校验:dev 服务器 localhost:2889 正常(200)。HMR 热更新生效,无需重启。
|
||||||
|
|
||||||
|
## 合同管理独立表单页标题补漏
|
||||||
|
- 需求:用户反馈 `business/contract-manage/form` 独立表单页顶部没有像 `project-apply/form` 一样的"新增合同管理"标题。
|
||||||
|
- 根因:contract-manage 是手写表单页(非 business-crud-page),页面内已有 `formPageTitle` computed(取 `query.name` 或默认"新增/编辑合同管理"),但模板里未渲染。
|
||||||
|
- 修复:在 `src/views/business/contract-manage.vue` 的表单页 `<template v-else>` 中、`<el-form>` 前加入 `<div class="archive-page-form__title">{{ formPageTitle }}</div>`,复用全局 `archive-page-form__title` 样式。
|
||||||
|
- 结论:contract-manage/form 现在顶部显示"新增合同管理"/"编辑合同管理",与 project-apply 风格一致。
|
||||||
|
|
||||||
|
## formal-settlement 表格包进应付/应收 tabs
|
||||||
|
- 需求:结算-正式结算页(/settlement/formal-settlement)表格要包进"应付/应收"两个 tab 内(原 el-tabs 的 pane 是空壳,表格在 el-tabs 外作为兄弟节点)。
|
||||||
|
- 实现:复用已有组件 `src/views/settlement/components/formal-settlement-table-panel.vue`(工具栏+表格+分页一体,通过 emit 事件与父通信),分别挂入"应付""应收"两个 `el-tab-pane` 内,共享父级 `rows/columns/loading/page/hasPermissionFn`,事件回连:@create→openCreate、@payment→openPaymentDialog、@sync→handleSync、@export→handleExport、@refresh→loadTable、@page-change→onPageChange、@action→onAction(分发 view/edit/delete/submit/approve/return/void/print)、@update:selection→selection 同步。切 tab 仍走 `handleSettlementTypeChange` 重载数据。
|
||||||
|
- 配套改动:父页 `:has-permission` 传 `hasPermissionFn`(computed 返回箭头函数,避免方法作 prop 时 this 丢失导致权限恒为 true);`handlePrint(row)` 增加可选 row 入参;移除父页内联表格及对应 scoped 样式(.formal-page__toolbar/links/pagination);组件操作列 width 120→320(符合 AGENTS.md >3 按钮规范)。
|
||||||
|
- 校验:@vue/compiler-sfc parse/compileTemplate 0 错误、esbuild 脚本语法 OK;`pnpm build` 因沙箱保护 dist/ 被拦截(非代码问题)。仅改 formal-settlement.vue 一个页面文件 + 组件 width。
|
||||||
|
|
||||||
|
## 临时额度管理附件上传按钮移至表格左下角
|
||||||
|
- 需求:用户截图反馈 `business/temporary-credit-limit` 新增弹窗中「上传附件」按钮在表格上方,要求统一放到附件表格左下角,与 project-apply 的项目材料交互一致。
|
||||||
|
- 改动文件:`src/views/business/temporary-credit-limit.vue`:
|
||||||
|
- 模板:将 `vehicle-attachment-upload` 从 `temporary-credit-limit-page__attachment-head` 移除,放到 `el-table` 之后的新增 `temporary-credit-limit-page__attachment-upload` 容器内;head 内只保留「批量下载」按钮。
|
||||||
|
- 样式:`&__attachment-head` 的 `justify-content` 由 `space-between` 改为 `flex-end`;新增 `&__attachment-upload` 样式,左对齐并控制上传组件宽度自适应。
|
||||||
|
- 结论:临时额度管理附件区域现与合同管理、项目材料保持一致:批量下载在表格上方右侧,上传附件在表格下方左侧。
|
||||||
|
|
||||||
|
## 项目材料上传提示文字移到按钮右侧
|
||||||
|
- 需求:用户要求 project-apply/form「上传附件」按钮支持指定格式(pdf/bmp/jpeg/png/jpg/doc/docx/ppt/pptx/xlsx/xls/eml/msg/zip)、单文件 50M,并把提示文字放到按钮后面(右侧同一行)。
|
||||||
|
- 现状:project-apply.vue 第 537-540 行早已配置 `:file-types`、`:max-size="50"` 与完整 `tip` 文案,但 vehicle-attachment-upload 组件默认把提示渲染在按钮下方。
|
||||||
|
- 修复:仅改 `&__upload` 样式,用 `:deep` 把 `.vehicle-attachment-upload` 设为 `display:flex; align-items:center`,并把 `.el-upload__tip` 由默认块级(下方)改为 `display:inline-block; margin-left:12px`,使格式/大小提示与按钮同一行、位于按钮右侧。
|
||||||
|
- 结论:project-apply 附件提示现为"上传附件按钮 + 右侧灰色提示文字",未改动组件逻辑与其它页面。
|
||||||
|
|
||||||
|
## 附件格式说明三页统一(按钮右侧 30px / 500M)
|
||||||
|
- 需求:用户要求「支持pdf、bmp、…、zip,单个文件不超过500M」这段说明在 project-apply / temporary-credit-limit / contract-manage 三处附件上传区统一摆放:放到「上传附件」按钮右侧、间距 30px、文字左对齐;文件限制上限由 50M 改为 500M(说明与校验一致)。
|
||||||
|
- 实现(三页一致):
|
||||||
|
- 统一 `tip` 文案:`支持pdf、bmp、jpeg、png、jpg、doc、docx、ppt、pptx、xlsx、xls、eml、msg、zip的文件格式,单个文件不超过500M`。
|
||||||
|
- `:max-size`(contract-manage 为 `maxSize`)统一 500;project-apply / temporary-credit-limit 由 50→500。
|
||||||
|
- 样式:用 `:deep` 把 `.vehicle-attachment-upload` 设为 `display:flex;align-items:center`,`.el-upload__tip` 改 `display:inline-block;margin-left:30px;color:#909399;font-size:13px;text-align:left`,使说明在按钮右侧 30px、左对齐。
|
||||||
|
- contract-manage 的 AttachmentSection render 把上传按钮从表格上方 head 移到表格下方左侧(`.contract-manage-form__attachment-upload`),head 仅保留「批量下载」;同步删除失效的 `__attachment-actions` 死样式,新增 `__attachment-upload` 样式。至此三页上传按钮均位于附件表格左下角、说明在按钮右侧。
|
||||||
|
- 校验:@vue/compiler-sfc 对三文件 parse/compileScript/compileTemplate 均 OK;dev localhost:2889 HMR 已生效。
|
||||||
|
|
||||||
|
## 全站附件上传统一(vehicle-attachment-upload 组件级单一事实来源)
|
||||||
|
- 需求:把"上传附件按钮在附件表格左下角 + 按钮右侧 30px 左对齐统一说明 + 单文件 500M"方案应用到整个站点,统一风格。
|
||||||
|
- 核心做法(改 1 个组件即全站生效):`src/components/vehicle-attachment-upload/main.vue`
|
||||||
|
- 新增常量 `UNIFIED_ATTACHMENT_TIP = '支持pdf、bmp、jpeg、png、jpg、doc、docx、ppt、pptx、xlsx、xls、eml、msg、zip的文件格式,单个文件不超过500M'`;`tipText` 改为 `this.tip || UNIFIED_ATTACHMENT_TIP`(不再按 acceptText 拼装)。
|
||||||
|
- `maxSize` 默认值 `0` → `500`(说明与校验一致)。
|
||||||
|
- 模板 `v-if="showTip && ..."` 去掉 `showTip` 门控 → `v-if="tipText && !readonly"`,全站始终显示统一说明(原 `:show-tip="false"` 变为无害 no-op,未逐一清理)。
|
||||||
|
- SCSS 新增 `.vehicle-attachment-upload :deep(.el-upload){display:flex;align-items:center}` 与 `:deep(.el-upload__tip){display:inline-block;margin:0 0 0 30px;color:#909399;font-size:13px;text-align:left}`,按钮与说明内联、说明在按钮右侧 30px 左对齐。
|
||||||
|
- 表格型附件区布局对齐(上传移表格下方左侧,批量下载留表头右侧):business-crud-page.vue(2 处 attachmentsJson-form / 派车明细附件)、contract-manage-change.vue、pre-settlement-editor.vue、master-order-editor.vue、payment-application-form.vue、invoice-application-form.vue;各页新增 `__attachment-upload` 左对齐样式、`__attachment-head` 改 `justify-content:flex-end`。
|
||||||
|
- 大小异常值修正:bill-ledger-form / bill-payment-form 的 `:max-size="10"` → 500;invoice-application-form `:max-size="50"` → 500(同时把上传从表格上方移到下方)。
|
||||||
|
- 已是下方布局无需移动(仅随组件生效):invoice-receipt / receipt-claim-record / receipt-flow / bill-ledger / bill-payment / formal-settlement-editor(独立上传无表格)/ 各 vehicle 页(独立上传无表格)。
|
||||||
|
- 校验:@vue/compiler-sfc 对 12 个改动文件(组件 + 上述各页 + 此前已改的 project-apply/temporary-credit-limit/contract-manage)全部编译通过;grep 确认全站无残留非 500M 的附件 max-size。
|
||||||
|
|
||||||
|
## master-order 附件上传"按钮 + 描述"由两行改一行
|
||||||
|
- 需求:用户截图 `/business/master-order?mode=editor` 详情附件区,"上传附件"按钮与"支持pdf…"描述文字分别居中、各占一行,要求并到同一行(与 vehicle-attachment-upload 组件默认样式一致)。
|
||||||
|
- 根因:`master-order-editor.vue` 第 1814-1824 行 scoped CSS 把 `.master-editor__attachment-upload .el-upload` 强制 `display: inline-flex` 但未显式 `flex-wrap/align-items/text-align`;而 Element Plus 全局 `.el-upload` 默认 `text-align: center`,加 inline-flex 子元素被 EP 默认行为按各自一行居中显示。
|
||||||
|
- 修复:在 `.master-editor__attachment-upload` 内用 `:deep(.el-upload)` 显式声明 `display: inline-flex; align-items: center; flex-wrap: nowrap; text-align: left`,并 `:deep(.el-upload__tip)` 改 `display: inline-block; margin: 0 0 0 8px; color:#909399; font-size:13px; line-height:1.4; white-space:normal; text-align:left`,让按钮与说明内联排成一行。
|
||||||
|
- 结论:master-order 附件区与 project-apply/temporary-credit-limit/contract-manage 风格一致(按钮左 + 描述贴右侧)。
|
||||||
|
|
||||||
|
## vehicle-attachment-upload 恢复 showTip 门控
|
||||||
|
- 根因:用户截图 feedback `customer-archive/form` 的「OCR上传识别」按钮下方仍显示统一附件说明,要求「OCR上传识别这里不用加描述」。该组件虽有 `showTip` prop,但此前「全站附件上传统一」时把模板门控 `v-if="showTip && tipText && !readonly"` 简化成了 `v-if="tipText && !readonly"`,导致 `:show-tip="false"` 失效。
|
||||||
|
- 修复:恢复 `src/components/vehicle-attachment-upload/main.vue` 第 24 行的 `showTip &&` 条件,使 `:show-tip="false"` 可正常隐藏按钮旁说明;`customer-archive.vue` 中 OCR 上传实例已配置 `:show-tip="false"`,无需额外改动。
|
||||||
|
- 影响:所有 `:show-tip="false"` 的调用点(annual-inspection/maintenance/accident/violation/tire/customer-archive 等)现在会按预期隐藏提示;未配置 `show-tip` 或 `:show-tip="true"` 的实例继续显示 `UNIFIED_ATTACHMENT_TIP`。
|
||||||
|
|
||||||
|
## customer-archive 客商材料上传按钮左、描述右、间距 30px
|
||||||
|
- 需求:用户第三轮明确「客商材料」表格下"上传附件"按钮与「支持pdf…」描述需并排、按钮左、描述右、间距严格 30px。
|
||||||
|
- 定位:`src/views/vehicle/customer-archive.vue` 第 4637-4666 行 `.qualification-upload-bar`(grid 1fr auto 1fr 改 flex 的二次修正)。
|
||||||
|
- 修复:保留 flex 容器,按钮区 `.vehicle-attachment-upload > :deep(.el-upload)` `flex: 0 0 auto` 贴左固定;描述 `:deep(.el-upload__tip)` `margin-left: 30px + white-space: nowrap + overflow:hidden + text-overflow:ellipsis + text-align:left`,与按钮同行紧贴、间距固定 30px、超长文案省略号。
|
||||||
|
- 结论:布局"「上传附件」按钮 ←30px→ 「支持pdf…」描述",单行两端对齐。
|
||||||
|
|
||||||
|
## sidebar 子路由高亮回退(方案B)
|
||||||
|
- 需求:用户反馈点「客商档案」菜单后,再点新增/编辑/查看(跳子路由 /vehicle/customer-archive/form),左侧菜单选中灰底消失。
|
||||||
|
- 根因:sidebar `activeMenu` getter 在无 `meta.activeMenu` 时退到 `route.path`(即子路由 /vehicle/customer-archive/form),与菜单 path /vehicle/customer-archive 不匹配,`<el-menu :default-active>` 找不到项 → 高亮丢失。同因还影响 /business/contract-manage/change 等独立子路由。
|
||||||
|
- 修复(方案B,通用兜底,改 1 文件):`src/page/index/sidebar/index.vue`
|
||||||
|
- 新增 `menuPaths` computed:递归收集全站菜单 `website.menu` 的 `path` 字段进 Set。
|
||||||
|
- `activeMenu` 在 `return path` 前加分支:当 `path` 不在 menuPaths 时,过滤出所有 `path.startsWith(p + '/')` 的菜单路径,按长度降序取最长前缀匹配者作为高亮项(即 /vehicle/customer-archive/form → /vehicle/customer-archive)。
|
||||||
|
- 原有 `meta.activeMenu` 与 `menuIndexes`(带参菜单精确匹配)分支保持不变;路径完全相同的路由仍精确匹配,不受影响。
|
||||||
|
- 结论:一次改动全站受益,所有"父菜单 + 独立子表单页"场景(customer-archive/form、contract-manage/change 等)自动保持父菜单高亮,无需逐路由配 activeMenu。HMR 已生效。
|
||||||
|
|
||||||
|
## master-order-dispatch 运费单位 select 撑爆 append 槽
|
||||||
|
- 需求:用户截图 dispatch 页"任务信息"块下"数量 → 吨"与"单价 → 元/吨"两个 select 宽度比左侧 input 还宽,红框标记要求改小。
|
||||||
|
- 根因:`src/views/business/components/master-order-dispatch.vue` 第 702 行 `.dispatch-form` 全局规则 `:deep(.el-input), :deep(.el-select), :deep(.el-date-editor), :deep(.el-input-number) { width: 100%; }` 把整个 dispatch 表单内**所有** el-select 强制 100% 宽;这两个 select 处于 `el-input` 的 `#append` slot 内,被强制撑开 append 容器,append 又把整个 `el-col :span="6"` 列占满。第 707 行 `.freight-form { :deep(.freight-unit-select) { width: 92px; } }` 想兜底 92px,但因 specificity 与覆盖时机问题未生效。
|
||||||
|
- 修复(最小改动,2 处):
|
||||||
|
- 行 702 末尾追加 `:deep(.el-input-group__append .el-select) { width: auto; }`,使 append 内的 select 退回内容自适应宽度(避免被 100% 拉伸)。
|
||||||
|
- 行 707 强化兜底:`.freight-unit-select` 加 `flex: 0 0 92px; min-width: 0;`,固定 92px 防止 append flex 拉伸。
|
||||||
|
- 结论:单位 select(吨/元/吨)宽度由内容决定(≈92px),不再撑爆 append 与列;其它顶层 select(承运商/币种等)仍维持 100% 宽,行为不变。
|
||||||
@@ -0,0 +1,343 @@
|
|||||||
|
# 2026-08-26
|
||||||
|
|
||||||
|
## el-table 内嵌 el-autocomplete 撑满列宽(全局规则补充)
|
||||||
|
|
||||||
|
**问题**: `business/transport-plan/form?mode=add` 独立表单页"货物信息"表格,
|
||||||
|
「货物名称」列 placeholder `请输入货物名称` 只显示 `请输入货物名…`。
|
||||||
|
|
||||||
|
**根因**: `element-ui.scss` 兜底规则只覆盖 `.el-input`,而 `<el-autocomplete>` 在 Element Plus 中
|
||||||
|
根节点是 `.el-autocomplete` 包装层,内部才是 `.el-input`,中间多了一层导致选择器不命中,
|
||||||
|
autocomplete 没有拉满 cell,placeholder 被内边距 / clear 图标挤压截断。
|
||||||
|
|
||||||
|
**修复**:
|
||||||
|
- `src/styles/element-ui.scss` 第 663 行兜底规则追加:
|
||||||
|
- `.cell > .el-autocomplete` `width:100%`
|
||||||
|
- `.cell > .el-autocomplete > .el-input` `width:100%`
|
||||||
|
- `.cell > .el-autocomplete .el-input__wrapper` `width:100%`
|
||||||
|
- `src/views/business/components/business-crud-page.vue:1520` 独立表单页
|
||||||
|
"货物名称"列 `min-width="150"` → `240`(与 1544 行「货物类型」cascader 列一致)。
|
||||||
|
|
||||||
|
其它内嵌 `<el-select>` / `<el-input>` / `<el-cascader>` 不受影响(选择器依旧命中),
|
||||||
|
3626 行(调度明细「货物名称」用的是 el-select)未做改动。
|
||||||
|
|
||||||
|
## 全站搜索栏 label 宽度统一 88px
|
||||||
|
|
||||||
|
**问题**: 主人反馈 `business/master-order` 搜索区 label 宽度 160px,要求全站统一改成 88px,
|
||||||
|
参考 `business/shipping-template`(已经是 88px)。
|
||||||
|
|
||||||
|
**调研**:
|
||||||
|
- `src/styles/element-ui.scss:133-136` 已有 `.avue-crud__search .el-form-item__label { min-width: 88px; }`,
|
||||||
|
全站 Avue 自动搜索栏视觉上就是 88px。
|
||||||
|
- shipping-template 走 `<business-crud-page>`(Avue),所以 label 是 88px。
|
||||||
|
- 但是项目里有 18 个文件,22 处直接手写 `<el-form ... label-width="160px">`,不走 `.avue-crud__search`,
|
||||||
|
视觉就是 160px。这些都是**搜索/筛选** `<el-form>`(顶层 query 或子表单),不是表单 dialog 内填表。
|
||||||
|
|
||||||
|
**改动**: 全部 22 处 `label-width="160px"` → `label-width="88px"`。
|
||||||
|
|
||||||
|
涉及文件:
|
||||||
|
- business: master-order.vue, voucher-manage.vue(3 处), loading-manage.vue
|
||||||
|
- payment: invoice-application, invoice-receipt, receipt-flow, receipt-claim-record,
|
||||||
|
payment-application, components/bill-ledger-search, components/bill-payment-search
|
||||||
|
- settlement: settlement-adjustment, receivable-payable-detail, pre-settlement,
|
||||||
|
formal-settlement, transport-reconciliation, components/transport-reconciliation-editor,
|
||||||
|
components/formal-settlement-editor(3 处,都是子筛选)
|
||||||
|
- transit: risk-disposal, exception-disposal
|
||||||
|
|
||||||
|
**遗漏补丁**: `transit/exception-disposal.vue` 搜索区不走 Avue,而是在 scoped 样式里用
|
||||||
|
`:deep(.el-form-item__label) { min-width: 160px }` 写死,盖过了全局 88px。
|
||||||
|
已改为 `min-width: 88px`(文件第 443 行)。`risk-disposal.vue` 无此 scoped 覆盖,本来就是 88px。
|
||||||
|
|
||||||
|
**未做改动**: 表单 dialog 内填写项(`shipping-template-dialog` /
|
||||||
|
`transport-plan-dialog` / `waybill-manage-dialog` 等)走专门的 dialogCustomClass + labelWidth:100,
|
||||||
|
不受本规则影响。
|
||||||
|
其余残留 160px 均非搜索栏: project-apply.vue:2095(表单详情弹窗 label,故意留 6 汉字)、
|
||||||
|
customer-archive.vue:4980(按钮 min-width)、cargo-type.vue:604(选择器拼错不生效,设 50px)。
|
||||||
|
|
||||||
|
## payment-application 表单 input 文字居左
|
||||||
|
|
||||||
|
- `src/views/payment/payment-application-form.vue`:追加 scoped 样式,使 `el-input-number` 的输入文字居左对齐(付款比例、申请付款金额等数字字段),与 bill-ledger-form 保持一致。
|
||||||
|
|
||||||
|
## 正式结算查看页去除内容行高
|
||||||
|
|
||||||
|
**问题**: `settlement/formal-settlement/form?mode=view` 中只读字段(如项目名称、合同名称)
|
||||||
|
多行文本行高过大,Element Plus 默认 `.el-form-item__content` 的 `line-height` 导致显示松散。
|
||||||
|
|
||||||
|
**修复**: `src/views/settlement/components/formal-settlement-editor.vue` scoped 样式追加:
|
||||||
|
```scss
|
||||||
|
.formal-editor :deep(.el-form-item__content) {
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 全站表单项间距统一 14px
|
||||||
|
|
||||||
|
**诉求**: `settlement/pre-settlement/form?mode=add` 中 `.el-form-item` 间距偏大,要求统一为 14px 且全站一致(用户指"表单项间距")。
|
||||||
|
|
||||||
|
**排查**: 全站 `.el-form-item` 的 `margin-bottom` 取值不一致——表单填写区有 16px / 18px / 20px,
|
||||||
|
搜索栏统一 8px(保留),个别特殊分组 28px(risk-disposal,保留)。全局 `.el-form-item--default`
|
||||||
|
已是 14px,但各页面 scoped `:deep(.xxx .el-form-item)` 覆盖成了 16/18/20px。
|
||||||
|
|
||||||
|
**改动(表单填写区统一 14px,16/18/20 → 14)**:
|
||||||
|
- settlement: `components/pre-settlement-editor.vue:1637`(16→14)、
|
||||||
|
`components/formal-settlement-editor.vue:1560`(16→14)、
|
||||||
|
`receivable-payable-detail.vue:1860`(16→14)、
|
||||||
|
`components/transport-reconciliation-editor.vue:805`(16→14)、
|
||||||
|
`components/settlement-adjustment-editor.vue:560`(16→14)
|
||||||
|
- business: `voucher-manage.vue:1022`(16→14)、`loading-manage.vue:2751`(16→14)、
|
||||||
|
`project-apply.vue:2091`(16→14)、`contract-manage.vue:2253`(16→14)、
|
||||||
|
`components/business-crud-page.vue:13380/13894`(16→14)、
|
||||||
|
`components/master-order-dispatch.vue:700`(16→14)
|
||||||
|
- payment: `invoice-receipt-form.vue:755`(16→14)、`user.vue:1097`(16→14)、
|
||||||
|
`bill-ledger-form.vue:564`(16→14)、`payment-application-form.vue:1324`(16→14)、
|
||||||
|
`bill-payment-form.vue:382`(16→14)
|
||||||
|
- 全局: `components/cron-editor/main.vue:579`(18→14)、
|
||||||
|
`styles/element-ui.scss:430`(`.dialog-form--carded` 20→14)
|
||||||
|
|
||||||
|
**保留未动**: 所有 `__search` 搜索栏的 8px、risk-disposal 的 28px 特殊分组、transportCapacity
|
||||||
|
vehicle/driver/ship 与 customer-archive 等本就是 14px(含 !important)。
|
||||||
|
|
||||||
|
## project-apply 表头颜色在不同电脑不一致
|
||||||
|
|
||||||
|
**问题**: 客户反馈 `business/project-apply/form?mode=add` 页面中「客户信息」等表格表头颜色
|
||||||
|
在用户电脑是 `#374151`,客户电脑是 `#909399`。
|
||||||
|
|
||||||
|
**根因**:
|
||||||
|
- `src/styles/theme/go.scss` 中 `.theme-go .el-table tr th .cell { color: #374151 }` 把表头文字写死为 `#374151`。
|
||||||
|
- 客户电脑未使用 `theme-go`,表头文字走 Element Plus 默认 CSS 变量
|
||||||
|
`--el-table-header-text-color: #909399`。
|
||||||
|
- `src/views/business/project-apply.vue` 里的 `:deep(.el-table__header th) { color: #303133 }`
|
||||||
|
只作用在 `th` 上,颜色靠继承,优先级敌不过直接写在 `.cell` 上的规则和 Element Plus 默认值,
|
||||||
|
所以实际没生效。
|
||||||
|
|
||||||
|
**修复**:
|
||||||
|
- `src/styles/element-ui.scss` 在「统一全站 el-table 表头背景色」之后追加规则:
|
||||||
|
```scss
|
||||||
|
.el-table .el-table__header-wrapper th.el-table__cell .cell {
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
直接作用于 `.cell`,特异性高于 `theme-go` 和 Element Plus 默认,全站统一为 `#374151`。
|
||||||
|
- `src/views/business/project-apply.vue` 清理两处与全局冲突且不生效的局部表头样式:
|
||||||
|
删除 `background: #f5f7fa; color: #303133;`,保留 `font-weight: 600`,
|
||||||
|
选择器改为 `:deep(.el-table__header th .cell)`。
|
||||||
|
|
||||||
|
## 弹窗 / 独立表单页底栏「保存+提交+返回」按钮统一风格
|
||||||
|
|
||||||
|
**问题**: 主人截图 `business/project-apply/form?mode=add` 页面底部 `[保存][提交][返回]` 三个按钮,
|
||||||
|
- 保存 + 提交 都是 `type="primary"` 蓝实心,视觉层级无区分。
|
||||||
|
- 排序「主→主→次」,主操作不靠右,与全站其它页面(business-crud-page 等)已用的「次→中→主」不一致。
|
||||||
|
|
||||||
|
**修复**: 统一定 3 档层级 + 「次→中→主」排序。
|
||||||
|
| 语义 | 写法 |
|
||||||
|
| --- | --- |
|
||||||
|
| 次要(返回/取消) | 默认(无 type)灰描边 |
|
||||||
|
| 中间步骤(保存草稿) | `type="primary" plain` 蓝描边 |
|
||||||
|
| 主操作(提交) | `type="primary"` 蓝实心 |
|
||||||
|
|
||||||
|
排序模板:`[可选辅助(如同步)][返回/取消][保存草稿][提交]`,主操作永远最右。
|
||||||
|
|
||||||
|
**改动文件**:
|
||||||
|
- `business/project-apply.vue:617-651` — 主 footer + 变更 footer 都重排,`保存` 加 plain。
|
||||||
|
- `payment/invoice-receipt-form.vue:242-252` — 保存加 plain,返回挪到保存前面。
|
||||||
|
- `payment/invoice-application-form.vue:419-429` — 同上。
|
||||||
|
|
||||||
|
**未改动(已合规)**:
|
||||||
|
- `settlement/components/pre-settlement-editor.vue` — 默认已是「默认保存 + primary 提交」。
|
||||||
|
- `settlement/components/transport-reconciliation-editor.vue` — 两个 plain 中间步骤 + 完成对账 实心,层级清晰。
|
||||||
|
|
||||||
|
**沉淀**: `MEMORY.md` 新增「弹窗 / 独立表单页底栏按钮统一规则」一节,作为新页面 footer 写法标准。
|
||||||
|
|
||||||
|
## customer-archive 全部 footer 同步按新增项目模块标准对齐
|
||||||
|
|
||||||
|
**问题**: 主人指出客户档案页面 `/vehicle/customer-archive/form?name=新增客商档案` 没有按统一规则调整,
|
||||||
|
并明确要求「浮动页面底部、按钮区域靠右对齐,返回在第一个,中间的用浅蓝色,最后一个是 primary 深蓝色」,
|
||||||
|
参考新增项目模块 `/business/project-apply/form?mode=add`。
|
||||||
|
|
||||||
|
### customer-archive.vue 共 5 处 footer 改动
|
||||||
|
|
||||||
|
- `archive-form__footer` (802-809) 原状「取消/返回 + 保存 primary + 提交 success 绿」混合 4 按钮。改为 `[返回/取消/关闭] + 保存 plain + 提交 primary`,并删掉「success 绿」换成 primary 深蓝。
|
||||||
|
- `contact-dialog` (867-870) 原状 `[保存 primary][返回]`,容器居中。改为 `[返回][保存 plain]`,容器 flex-end。
|
||||||
|
- `receipt-dialog` (965-969) 同上模式。
|
||||||
|
- `invoice-dialog` (1085-1089) 同上模式。
|
||||||
|
- `score-detail-dialog` (1343-1359) 原状 total 左 + `[保存 primary][复评确认 primary][返回]`。改为 total 左 + `[返回][保存 plain][复评确认 primary]`。
|
||||||
|
|
||||||
|
### customer-archive.vue SCSS 同步
|
||||||
|
|
||||||
|
- `.contact-dialog__footer-actions` `justify-content: center` 改 `flex-end`。
|
||||||
|
- `.receipt-dialog__footer-actions` 同上。
|
||||||
|
- `.invoice-dialog__footer-actions` 同上。
|
||||||
|
- `.archive-form__footer` 维持 flex-end 不变(独立页 `.archive-page-form &` 下 `position: fixed` 浮动底部已经写好)。
|
||||||
|
|
||||||
|
### 顺序小结(标准模板)
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<div class="xxx-dialog__footer-actions">
|
||||||
|
<!-- 最左:信息聚合(如总分合计、统计)用 flex:1 自己推 -->
|
||||||
|
<!-- 次要:默认描边灰按钮 -->
|
||||||
|
<el-button @click="close">返回</el-button>
|
||||||
|
<!-- 中间:浅蓝实心 plain -->
|
||||||
|
<el-button type="primary" plain @click="saveDraft">保存</el-button>
|
||||||
|
<!-- 主操作:蓝实心 -->
|
||||||
|
<el-button type="primary" @click="submit">提交</el-button>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
左→右阅读顺序:辅助信息聚合 → 次要 → 中间步骤 → 主操作。视觉权重递增,颜色从「灰 → 蓝描边 → 蓝实心」。
|
||||||
|
|
||||||
|
## 全站 disabled 输入框文字颜色统一
|
||||||
|
|
||||||
|
**问题**: 客户档案编辑页等「不可编辑」状态下,`el-input` / `el-textarea` / `el-input-number` 的文字颜色
|
||||||
|
在不同电脑显示不一致(有的深、有的浅)。
|
||||||
|
|
||||||
|
**根因**: Element Plus 默认 disabled 文字色由 `--el-disabled-text-color` 控制,不同主题/浏览器/变量覆盖下可能表现为 `#374151` / `#606266` / `#a8abb2` / `#909399` 等。
|
||||||
|
|
||||||
|
**修复**: `src/styles/element-ui.scss` 在表头颜色统一规则后追加:
|
||||||
|
```scss
|
||||||
|
.el-input.is-disabled .el-input__inner,
|
||||||
|
.el-textarea.is-disabled .el-textarea__inner,
|
||||||
|
.el-input-number.is-disabled .el-input__inner {
|
||||||
|
color: #a8abb2;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
直接写死为 `#a8abb2`,覆盖 Element Plus 默认 CSS 变量及主题差异,保证全站一致。
|
||||||
|
**如需再深**,可把 `#a8abb2` 改为 `#606266`。
|
||||||
|
|
||||||
|
## shipping-template 独立表单页「关闭」按钮位置修正
|
||||||
|
|
||||||
|
**问题**: `/business/shipping-template/form?mode=add`(独立表单页)底部 `[提交][关闭]` 顺序,
|
||||||
|
主操作「提交」在左、次要「关闭」在右,与全站「次→主」规则不符。
|
||||||
|
|
||||||
|
**根因**: shipping-template 走 `business-crud-page` 复用的 PageAvueForm,footer 用的是 Avue-form 内置结构。
|
||||||
|
Avue-form 内置按钮默认在中间,自定义内容分两侧插槽:
|
||||||
|
- `#menu-form-before` slot → 在 Avue 内置按钮之左
|
||||||
|
- `#menu-form` slot → 在 Avue 内置按钮之右
|
||||||
|
|
||||||
|
之前「关闭」按钮写在 `#menu-form` slot(在 Avue 内置按钮之右),导致渲染成 `[Avue 提交][关闭]`,顺序反了。
|
||||||
|
|
||||||
|
**修复**: `src/views/business/components/business-crud-page.vue:2059-2090`
|
||||||
|
- 把「关闭」按钮从 `#menu-form` slot 移到 `#menu-form-before` slot 的「保存草稿」按钮之前。
|
||||||
|
- 触发条件 `v-if="isStandaloneFormPage && !showTransportPlanCreateActions"`,仅独立表单页生效;
|
||||||
|
弹窗模式(list 页内点新增/编辑)继续走 Avue 自带的 `[取消][提交]` 不动。
|
||||||
|
- 「关闭」按钮文字保持默认色(次要),无 type。
|
||||||
|
- 同步清空 `#menu-form` slot 中 shipping-template 残留(实际只剩 `showTransportPlanCreateActions` 块,
|
||||||
|
shipping-template 不会触发,留着不影响)。
|
||||||
|
|
||||||
|
**最终渲染**(独立表单页):
|
||||||
|
- 有运费合计 + 草稿:`[运费合计][关闭][保存草稿 plain][Avue 提交]`
|
||||||
|
- 仅有运费合计:`[运费合计][关闭][Avue 提交]`
|
||||||
|
- 无运费合计:`[关闭][Avue 提交]`(即主人截图场景)
|
||||||
|
|
||||||
|
**未触发场景**:
|
||||||
|
- transport-plan / waybill / loading-manage 等其它走 business-crud-page 但未声明 `standalone-form-page` 的页面,
|
||||||
|
本次未改。需要时可按相同模式补 slot。
|
||||||
|
|
||||||
|
## master-order 编辑页底栏按钮统一
|
||||||
|
|
||||||
|
**问题**: `/business/master-order?mode=editor`(多联总单编辑/新增页)底部按钮没统一。
|
||||||
|
|
||||||
|
**根因**: 编辑页是 `master-order.vue` 的 `mode==='editor'` 模板里内嵌 `<master-order-editor>` 组件,
|
||||||
|
主 footer 在 `src/views/business/components/master-order-editor.vue:534-539`。
|
||||||
|
|
||||||
|
**原状**:
|
||||||
|
```
|
||||||
|
[返回 default] [暂存 default] [确认创建 primary] [创建并调度 primary]
|
||||||
|
```
|
||||||
|
- 暂存 无 type,与返回同为灰描边,缺层级
|
||||||
|
- 确认创建 / 创建并调度 两个都 primary 实心,无视觉优先级
|
||||||
|
- footer 仅 `flex-end` + `padding:16px 0`,不浮动
|
||||||
|
|
||||||
|
**修复** (`master-order-editor.vue`):
|
||||||
|
- 按钮改:`[返回 default] [暂存 primary plain] [确认创建 primary plain] [创建并调度 primary]`
|
||||||
|
严格按「返回第一个、中间浅蓝 plain、最后深蓝实心」规则,仅最右一个深蓝。
|
||||||
|
- footer 样式改浮动底部:`position: sticky; bottom:0; z-index:10; background:#fff;
|
||||||
|
border-top:1px solid #eff1f7; box-shadow:0 -2px 8px rgba(0,0,0,.06); gap:12px; padding:12px 24px`。
|
||||||
|
|
||||||
|
**未动**: 页内各 el-dialog 的 footer(线路/地图/常用货物/货物导入等)已是 `[关闭 default][确定 primary]`,
|
||||||
|
符合「次→主」规则,无需调整。
|
||||||
|
|
||||||
|
## payment-application 表单页底栏按钮统一
|
||||||
|
|
||||||
|
**问题**: `/payment/payment-application/form?mode=add`(付款申请表单页)底部按钮没统一。
|
||||||
|
|
||||||
|
**根因**: 独立表单页 `src/views/payment/payment-application-form.vue` 的 `.payment-form-page__actions` footer(330 行)。
|
||||||
|
|
||||||
|
**原状**:
|
||||||
|
```
|
||||||
|
justify-content: center(居中)
|
||||||
|
[返回 default] [保存 default(缺 plain)] [提交 primary]
|
||||||
|
```
|
||||||
|
- 保存 无 type,与返回同为灰描边,缺层级
|
||||||
|
- footer 居中且不浮动
|
||||||
|
|
||||||
|
**修复** (`payment-application-form.vue`):
|
||||||
|
- 按钮:`[返回 default] [保存 primary plain] [提交 primary]`,仅最后深蓝。
|
||||||
|
- footer 样式:`justify-content: center` → `flex-end`;加 `position: sticky; bottom:0; z-index:10;
|
||||||
|
background:#fff; border-top:1px solid #eff1f7; box-shadow:0 -2px 8px rgba(0,0,0,.06); gap:12px; padding:12px 24px`。
|
||||||
|
|
||||||
|
**未动**: `payment-application.vue`(list 表格页,无独立 footer);`invoice-application-form.vue` / `invoice-receipt-form.vue` 上一轮已改。
|
||||||
|
|
||||||
|
## invoice-application / invoice-receipt 表单页底栏按钮统一
|
||||||
|
|
||||||
|
**问题**: `/payment/invoice-application/form?mode=add` 底部按钮没统一(用户反馈)。排查发现同族 `invoice-receipt-form.vue` 完全同款问题,一并修了。
|
||||||
|
|
||||||
|
**根因**: 两个发票表单页 footer 都是 `[同步 plain][返回 default][保存 plain][提交 primary]` + `justify-content: center`(居中),且未浮动。
|
||||||
|
- 同步 在 返回 之前,违反「返回在第一个」
|
||||||
|
- footer 居中,不靠右、不浮动
|
||||||
|
|
||||||
|
**修复**(`invoice-application-form.vue` 419-433 + 980;`invoice-receipt-form.vue` 242-256 + 777):
|
||||||
|
- 按钮重排为 `[返回 default][同步 plain][保存 plain][提交 primary]`,返回置首、最后仅提交深蓝。
|
||||||
|
- footer 样式:`justify-content: center` → `flex-end`;加 `position: sticky; bottom:0; z-index:10;
|
||||||
|
background:#fff; border-top:1px solid #eff1f7; box-shadow:0 -2px 8px rgba(0,0,0,.06); gap:12px; padding:12px 24px`。
|
||||||
|
|
||||||
|
**注意**: 之前首轮曾把这两个页「保存加 plain、返回挪到保存前面」,但当时没动 同步 的位置、没改居中/浮动,所以用户仍觉得没统一。本次彻底对齐。
|
||||||
|
|
||||||
|
## payment + settlement 模块底栏按钮全量统一
|
||||||
|
|
||||||
|
**背景**: 用户要求检查 payment 模块和 settlement 模块下所有页面底栏按钮是否统一。
|
||||||
|
|
||||||
|
### payment 模块(表单页 footer)
|
||||||
|
| 文件 | 原状 | 修复 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `payment/bill-ledger-form.vue` | `[确认 primary][取消 default]` + 居中 | `[取消 default][确认 primary]`;footer flex-end + sticky 浮动 |
|
||||||
|
| `payment/bill-payment-form.vue` | `[保存 primary][提交 primary][取消 default]` | `[取消 default][保存 plain][提交 primary]`;footer 加 sticky 浮动(已 flex-end) |
|
||||||
|
| `payment/receipt-flow-form.vue` | `[确认 primary][取消 default]` + 居中(class 误用 `receipt-claim-form-page__actions`) | `[取消 default][确认 primary]`;footer flex-end + sticky 浮动 |
|
||||||
|
| `payment/receipt-claim-record-form.vue` | 单「关闭」按钮居中 | footer flex-end + sticky 浮动 |
|
||||||
|
| (此前已改)invoice/payment-application 各表单页 | — | 已合规 |
|
||||||
|
|
||||||
|
### settlement 模块
|
||||||
|
| 文件 | 原状 | 修复 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `settlement/components/pre-settlement-editor.vue` | 两处 footer `[取消][保存 default][提交]`(弹窗 + pageMode) | 保存加 `type="primary" plain`;`&__page-actions` 加 sticky 浮动 |
|
||||||
|
| `settlement/components/formal-settlement-editor.vue` | footer 已 `[取消][提交]` 合规,独立页未浮动 | `.formal-editor__page-actions` 加 sticky 浮动 |
|
||||||
|
| `settlement/receivable-payable-detail.vue` | 生成费用弹窗 `[取消][上一步 primary][提交 primary]` | 上一步改 `type="primary" plain` |
|
||||||
|
| (已合规未动)`transport-reconciliation-editor.vue` | `[取消][保存草稿 plain][按匹配结果更新账单 plain][完成对账 primary]` | 已符合次→主 |
|
||||||
|
| (已合规未动)`settlement-adjustment-editor.vue` | 弹窗 `[取消][保存 primary]` | 合规 |
|
||||||
|
|
||||||
|
**注意**: settlement 的 `*_editor.vue` 多为「弹窗(!pageMode) + 独立页(pageMode)」双模式;弹窗 footer 走 `.el-dialog__footer` 全局规则(已右对齐/浮动),仅 pageMode 的 `.xxx__page-actions` 需手动加 sticky。
|
||||||
|
**注意**: payment 表单页剩余的 `justify-content: center` 全在 `__links` 链接行(非 action footer),属正常布局,未改。
|
||||||
|
|
||||||
|
## 浮动底栏根本修复:sticky → fixed
|
||||||
|
|
||||||
|
**问题**: 用户反馈 `/settlement/formal-settlement/form?mode=add`「有些页面还没能固定在底部」。
|
||||||
|
|
||||||
|
**根因(关键)**: 之前给 9 个独立表单页 footer 写的都是 `position: sticky; bottom:0`,但本项目布局
|
||||||
|
`.app-main` / `.basic-container` 祖先带 `overflow:hidden`,sticky 的滚动容器判断失效 → footer 永不吸底。
|
||||||
|
customer-archive 当初就是因此改用 `position:fixed`,且要带侧栏左偏移。
|
||||||
|
|
||||||
|
**修复(9 个文件全部 sticky→fixed)**:
|
||||||
|
- `business/components/master-order-editor.vue` `<footer>`(2050 行,含 `:global(.avue--collapse footer)`/`horizontal`)
|
||||||
|
- `payment/invoice-receipt-form.vue` / `bill-ledger-form.vue` / `receipt-claim-record-form.vue` /
|
||||||
|
`payment-application-form.vue` / `bill-payment-form.vue` / `invoice-application-form.vue` / `receipt-flow-form.vue`
|
||||||
|
各 `__actions`(均加 fixed + `:global(.avue--collapse .xxx)`/`horizontal` 左偏移 60px/0)
|
||||||
|
- `settlement/components/pre-settlement-editor.vue` `&__page-actions`(nested,全局覆盖放 style 末尾)
|
||||||
|
- `settlement/components/formal-settlement-editor.vue` `.formal-editor__page-actions`(上一轮已改 fixed,本轮复核确认)
|
||||||
|
|
||||||
|
**统一 fixed 模板**:
|
||||||
|
```scss
|
||||||
|
.xxx { position: fixed; right:0; left:230px; bottom:0; margin:0; z-index:10; flex-end + padding + border-top + 白底 + 上投影 }
|
||||||
|
:global(.avue--collapse .xxx) { left:60px; }
|
||||||
|
:global(.avue-layout--horizontal .xxx) { left:0; }
|
||||||
|
```
|
||||||
|
注意 `:global()` 要包整个选择器(customer-archive 写法),只包前缀会让 scoped 在中间选择器追加 data 属性而不匹配。
|
||||||
|
|
||||||
|
**验证**: 全仓 `position: sticky` 已清零(grep 无残留)。
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# 2026-08-27 工作日志
|
||||||
|
|
||||||
|
## project-apply 表单 UI 调整(新增 / 重大项目补录)
|
||||||
|
文件:`src/views/business/project-apply.vue`
|
||||||
|
|
||||||
|
按截图需求修改项目管理表单(新增 + 重大项目补录同步):
|
||||||
|
1. 项目编号放开编辑(去掉 `disabled`),placeholder 改为"请输入项目编号,不填自动生成";编辑模式仍禁用。
|
||||||
|
2. 项目简称取消必填校验(仅保留 maxlength 20)。
|
||||||
|
3. 业务部门、项目由来说明单独占一行(新增 class `project-apply-form__item--full` + 样式 `grid-column: 1 / -1`)。
|
||||||
|
4. 业务部门下拉过滤"外部组织":原仅 `['add','edit']` 过滤,已补上 `majorSupplement`,重大项目补录不再显示外部组织。
|
||||||
|
|
||||||
|
待确认:后端 submit/saveDraft 在 `projectCode` 为空时是否自动生成编号(用户让后端同事补)。
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# 2026-08-28 工作日志
|
||||||
|
|
||||||
|
## contract-manage 表单底部操作栏间距/文案修复
|
||||||
|
- 文件:`src/views/business/contract-manage.vue`
|
||||||
|
- 问题:底部浮动操作栏 `.contract-manage-page__footer` 用 `gap:8px`,与全站统一 12px 标准不一致("间距不一致不统一")。
|
||||||
|
- 修复:去掉 `gap:8px`,改用 `.el-button + .el-button { margin-left:12px }`,对齐全站"去 gap、12px"规范。
|
||||||
|
- 文案:底部"关闭"→"取消"(底部操作栏统一命名规范;详情弹窗/流程图弹窗的"关闭"为只读/纯关闭场景,按规范保留)。
|
||||||
|
|
||||||
|
## contract-manage 草稿编辑页按钮缺失修复
|
||||||
|
- 问题:编辑态(含草稿编辑)底部只有单个"提交"(submitEdit),缺"暂存/提交临时合同/提交正式合同",与新增页不一致。
|
||||||
|
- 修复:新增 computed `isDraftEdit`(`formMode==='edit' && form.approvalStatus==='draft'`);footer 三按钮条件由 `formMode==='add'` 改为 `formMode==='add' || isDraftEdit`,草稿编辑复用 `submitCreate`(按 id upsert + temporary/formal 提升),非草稿编辑仍走 submitEdit 单个"提交"。
|
||||||
|
|
||||||
|
## contract-manage-change 变更页底部操作栏统一改造
|
||||||
|
- 文件:`src/views/business/contract-manage-change.vue`
|
||||||
|
- 问题:`.page-footer` 左侧对齐、`gap:16px`、不悬浮,且按钮顺序/命名(提交→关闭)与新增页规范相反。
|
||||||
|
- 修复:
|
||||||
|
- DOM:按钮重排为 `[取消][提交]`(次要居左、主操作居右),"关闭"→"取消"。
|
||||||
|
- 样式:`.page-footer` 改为 `position:fixed` 悬浮吸底(left:230px / avue--collapse 60px / avue-layout--horizontal 0)、`justify-content:flex-end` 右对齐、去 gap 改用 `.el-button+.el-button{margin-left:12px}` 统一 12px、白底+上边框+上投影。
|
||||||
|
- 容器 `.contract-change-page` 加 `padding-bottom:72px`,避免内容被悬浮栏遮挡。
|
||||||
|
|
||||||
|
## contract-manage 新增页"合同文件"标题未加粗修复
|
||||||
|
- 文件:`src/views/business/contract-manage.vue`
|
||||||
|
- 问题:`attachment-section` 子组件内渲染的 `.dialog-section-title` 不受当前页面 scoped `> .dialog-section-title` 样式命中,导致"合同文件"标题字重未加粗(截图反馈)。
|
||||||
|
- 修复:在 `.contract-manage-form__section` 样式规则中补充 `:deep(.dialog-section-title)`,使直接子元素和子组件内部标题均统一为 16px/600/#303133。
|
||||||
|
|
||||||
|
## contract-manage 搜索栏"时间段快捷筛选"组位置前置
|
||||||
|
- 文件:`src/views/business/contract-manage.vue` + `src/option/business/contract-manage.js`
|
||||||
|
- 第一轮(理解偏差):把"签约类型"select 和 check-tag 组一起塞进 `#search-menu` slot。主人纠正:「签约类型」是普通搜索字段,应与第一行 8 个搜索项同排,不是 slot 内的辅助元素。
|
||||||
|
- 第二轮(终态):
|
||||||
|
- option 把 `signType` 加 `search:true / searchOrder:10 / searchPlaceholder:'请选择'`,作为 Avue 标准搜索字段,与其它 8 项同排(放在末尾)。
|
||||||
|
- 页面 `#search-menu` slot 移除「签约类型」select,仅留 5 个 el-check-tag。
|
||||||
|
- 页面 data 移除冗余的 `contractSignType:''`,onLoad/searchReset 不再手动维护 signType(Avue search form 自动绑定到 query)。
|
||||||
|
- 样式:`.el-select { width: 180px }` 已成 dead code,移除。
|
||||||
|
- `:deep(...)` 调整:去掉 `margin-right:auto`,只留 `order:-1`。`.avue-form__menu` 本身 `flex / flex-end`,check-tag 组排首项后,整体仍紧靠右侧,与 `[查询][重置][收起]` 形成 `[快捷筛选] [查询] [重置] [收起]` 右对齐序列。
|
||||||
|
|
||||||
|
## 登录验证码:滑块(behavior) → 图形字符验证码(image)
|
||||||
|
- 文件:`src/config/website.js`(第14行 `captchaType: 'behavior'` → `'image'`)、`src/page/login/userlogin.vue`、`src/styles/login.scss`
|
||||||
|
- 背景:登录页原 `captchaType:'behavior'`(滑块/旋转行为验证)。需求改成截图中的「输入框 + 右侧独立彩色字符验证码图片」。
|
||||||
|
- 改动:
|
||||||
|
1. `website.js` 改 `captchaType: 'image'` 启用图形验证码分支(代码原本就有 image 模式的 UI 与 `getCaptcha()` 逻辑,仅未启用)。
|
||||||
|
2. `userlogin.vue` image 分支模板:验证码图片从 `el-input` 的 `#append` 内联改为 form-item 内 `.login-code-wrap` 并排独立方块(输入框 flex:1 + 右侧 130px 图片块),点击刷新。
|
||||||
|
3. `login.scss` 重写 `.login-code/.login-code-wrap/.login-code-box/.login-code-img`:flex 并排、图片块带 1px 边框 + hover 主色高亮、`object-fit:fill` 撑满。
|
||||||
|
- 验证:`vite build --outDir /tmp/tms-verify-build` 全量编译通过(exit 0,无报错)。注意原 `pnpm build` 因 WorkBuddy safe-delete 钩子拦截清空 dist/(741 文件)而中断,非代码问题。
|
||||||
|
- 注意:登录页 `activeName` 默认 `'iam'`(IAM 统一登录),图形验证码在 `userlogin.vue`,需点「账号密码登录」才可见;验证码图片依赖后端 `/blade-auth/oauth/captcha` 返回的 `data.key` + `data.image`。
|
||||||
|
|
||||||
|
## 全站「所属组织」搜索统一为树形下拉(el-tree-select)
|
||||||
|
- 背景:合同管理页「所属组织」原为 cascader(radio 风格),客户档案页(vehicle/customer-archive)已用树形下拉。主人要求全站统一为树形。
|
||||||
|
- 统一方案:新建可复用 mixin `src/mixins/organization-search.js`(封装 organizationTreeOptions 树数据、loadOrganizationOptions()、renderOrganizationSearch(scope) 渲染函数,自动识别 organizationName / deptName 两种 prop 并挂 column.renderSearch;树节点 node-key='id')。
|
||||||
|
- 适用两种架构:
|
||||||
|
1. **avue-crud 搜索**(renderSearch 模式):option 列加 `searchslot:true`,视图混入 mixin + created/初始化调 loadOrganizationOptions。已改:contract-manage(option 去 cascader 改 searchslot,视图用 renderOrganizationSearch 渲染函数)、transportCapacity 的 driver/vehicle/ship 三页(option 去 type:select 改 searchslot,视图混 mixin)。
|
||||||
|
2. **自定义搜索表单**(query.deptName 文本框模式):替换 `<el-input v-model="query.deptName">` 为 `<el-tree-select>`,node-key='id',@update:model-value 用 findOrgById(val)?.rawLabel 把 id 转回部门名写入 query.deptName(保后端按名称过滤兼容)。已改 payment 模块 4 处:invoice-application / invoice-receipt / payment-application / components/bill-payment-search。
|
||||||
|
- 关键坑:mixin 内部树数据命名为 `organizationTreeOptions`(非 organizationOptions),与 transportCapacity 等页面表单自用的扁平 `organizationOptions` 隔离,避免相互覆盖破坏表单 select。
|
||||||
|
- 待办(同自定义表单模式,未改):settlement 模块 4 个视图(transport-reconciliation/formal-settlement/pre-settlement/settlement-adjustment,搜索用动态 v-for 渲染 searchFields,需给 deptName 加 `type:'tree'` 分支)、system/user.vue(deptName)。business 模块(waybill-manage/transport-plan 等)所属组织为 `hide:true` 不展示搜索,无需改。
|
||||||
|
|
||||||
|
## project-apply 业务部门栏占满宽度修复
|
||||||
|
- 文件:`src/views/business/project-apply.vue`
|
||||||
|
- 问题:「重大项目补录」(独立表单页 /business/project-apply/form?mode=majorSupplement)下"业务部门"项横跨整行,与上下两行半宽风格不一致。
|
||||||
|
- 根因:`<el-form-item label="业务部门">` 误加 `class="project-apply-form__item--full"`(该类对应样式 `:deep(.project-apply-form__item--full) { grid-column: 1 / -1; ... }` 是横跨整行的:本意只用于"项目由来说明"等多行文本字段)。
|
||||||
|
- 修复:移除该 class,业务部门恢复为半宽(一行两列网格的常规项),仅"项目由来说明"等需要全宽铺开的字段仍保留 full。同时把样式块上方注释由「占满整行的表单项(业务部门 / 项目由来说明)」改为「占满整行的表单项(如项目由来说明等多行文本字段)」,避免再误读。
|
||||||
|
|
||||||
|
## project-apply 字段 label 问号悬浮提示
|
||||||
|
- 文件:`src/views/business/project-apply.vue`
|
||||||
|
- 需求:把"项目资金使用额度"/"项目应收账款额度"两字段的提示从 placeholder 移到 label 右侧问号 tooltip。
|
||||||
|
- 实现:
|
||||||
|
- 两 `el-form-item` 加 `class="project-apply-form__tip-label"`,用 `#label` slot 包裹文字 + `<el-tooltip content=... placement="top"><el-icon class="project-apply-form__label-tip"><QuestionFilled /></el-icon></el-tooltip>`。
|
||||||
|
- placeholder 改回"请输入"。
|
||||||
|
- 提示文案按截图(去掉"额度"二字):资金使用额度→`实际业务回款周期内付款`;应收账款额度→`回款期内最大应收账款额`。
|
||||||
|
- scoped 样式:`__tip-label :deep(.el-form-item__label){vertical-align:top}` 保持默认 inline 布局,让文字按全局 `white-space: normal` 换行显示;`__label-tip{margin-left:4px;color:#a8abb2;cursor:help;vertical-align:-0.125em}` 问号与文字 baseline 对齐。
|
||||||
|
- 图标 `QuestionFilled` 已在 main.js 全局注册(@element-plus/icons-vue),模板可直接用。
|
||||||
|
- 可复用模式:其它表单字段如需 label 旁问号提示,套用此 slot 结构即可。
|
||||||
|
|
||||||
|
## project-apply 详情页客户/承运商表格改造
|
||||||
|
- 文件:`src/views/business/project-apply.vue`
|
||||||
|
- 需求:
|
||||||
|
1. 客户信息、承运商信息两个表格的「客户」「承运商」列改为可点击链接,跳转打开对应客商档案。
|
||||||
|
2. 删除两个表格的「联系人」「联系方式」列。
|
||||||
|
- 实现:
|
||||||
|
- 模板:两列的 `el-table-column` 用 `#default` slot 渲染 `<el-link type="primary" :underline="false" @click="openCustomerArchive(row)">`,显示 `row.customer` / `row.carrier`。
|
||||||
|
- 删除原 `prop="contact"` 和 `prop="phone"` 两列。
|
||||||
|
- methods 新增 `openCustomerArchive(row)`:取 `getCustomerRowId(row)`(`row.id || row.customerId || row.carrierId`),无 ID 时 `this.$message.warning`;有 ID 则 `this.$router.push({ path: '/vehicle/customer-archive/form', query: { id: String(id), name: '查看客商档案', view: '1' } })`。
|
||||||
|
- 注意:客商档案查看态由 `customer-archive.vue` 的 `this.$route.query.view === '1'` 识别,与现有查看逻辑一致。
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# 2026-08-30
|
||||||
|
|
||||||
|
## 项目立项(/business/project-apply)操作栏隐藏「作废」按钮
|
||||||
|
- 需求:列表操作列不再展示「作废」。
|
||||||
|
- 实现:删除 `src/option/business/project-apply.js` 中 `config.operations` 的 `voidProject` 条目(原 status: ['approved','change_approved'],permission: project_apply_void)。
|
||||||
|
- 操作列按钮由 `customOperations(row)` 从 `config.operations` 动态渲染,故仅改 option 配置即可,无需动 `project-apply.vue`。
|
||||||
|
- 保留 `src/api/business/project-apply.js` 的 `voidProject` 接口方法(未被引用),如需彻底下线可一并删除。
|
||||||
|
- 遗留:剩余操作项为 提交 / 撤回 / 通过 / 审批驳回 / 变更;`menuWidth` 仍为 200,无需调整。
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# 2026-08-31
|
||||||
|
|
||||||
|
## 客商档案「所属组织」改为单选级联(customer-archive.vue)
|
||||||
|
- 表单区 `el-tree-select`(多选勾选树)→ `el-cascader` 单选级联;`deptCascaderProps` 开 `checkStrictly: true`,可任意层级选中(不必选到叶子)。
|
||||||
|
- 引入 computed `deptCascaderValue`(get/set)做「部门 ID ↔ 根到节点路径」双向转换:
|
||||||
|
- `archiveForm.deptId` 仍为选中部门 ID 数组(单选只一个),后端字段 `deptIds` 逗号串 / `deptName` 不变,保存逻辑 `normalizeArchive` 无需改。
|
||||||
|
- getter 依赖 `deptTree`,部门树异步加载完成后详情回显自动刷新(解决 created 里 initDeptTree 与 openArchive 并发的时序问题)。
|
||||||
|
- `onDeptChange(path)` 改为取路径末级 → 写 `deptId / deptIds / deptName`;新增 `findDeptPath(id)`、`findDeptLabel(id)` 递归查找。
|
||||||
|
- `normalizeDeptIds(detail)` 单选只取第一个 ID(历史多选数据自动收敛)。
|
||||||
|
- 列表搜索区 `renderDeptSearch` 同步改为 `h(ElCascader, ...)`(import 从 ElTreeSelect 换 ElCascader),`searchChange` 取路径末级 ID 转部门名称。
|
||||||
|
</content>
|
||||||
|
</invoke>
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
# 2026-09-02 工作日志
|
||||||
|
|
||||||
|
## 合同/运力页「到期快捷筛选」标签统一改为独立一行
|
||||||
|
- 需求:把 avue-crud 搜索栏 `#search-menu` 里的 `<el-check-tag>` 到期快捷筛选组,移出搜索栏、单独成一行(搜索卡下方、表格上方),不加卡片包裹。
|
||||||
|
- 实现:改用 avue-crud 的 `#header` 插槽承载该组;CSS 由 `order:-1; margin-right:12px; min-width:0`(搜索按钮行内联)改为 `margin-bottom:12px`(独立行)。
|
||||||
|
- 涉及文件(4 处,均已改):
|
||||||
|
- `src/views/business/contract-manage.vue`(首改,用 `.contract-manage-page__expiry-search`)
|
||||||
|
- `src/views/transportCapacity/vehicle.vue`(`.transport-vehicle-page__expiry-tags`)
|
||||||
|
- `src/views/transportCapacity/driver.vue`(`.driver-page__expiry-tags`)
|
||||||
|
- `src/views/transportCapacity/ship.vue`(`.transport-ship-page__expiry-tags`)
|
||||||
|
- 各页变量名保留原样(contract-manage 用 contractExpiryScope/changeExpiryScope/contractExpiryStats;运力页用 query.expireStatus/handleExpireChange/expiryStat)。
|
||||||
|
- 已补改并修正:`payment/bill-ledger` 页。用户要求"放在卡片下面"(搜索白卡之外、表格之上独立一行),非卡片内分行。最终方案:把到期快捷筛选整组(标签 DOM + `shortcutOptions` 计算属性 + `handleShortcutChange`)从子组件 `bill-ledger-search.vue` 提升到页面 `bill-ledger.vue`,置于 `<bill-ledger-search>` 与 `<bill-ledger-table>` 之间(卡片下、表格上)。
|
||||||
|
- `bill-ledger-search.vue`:删除标签 DOM、`.bill-ledger-search__actions`/`.bill-ledger-search__actions-tags` CSS、`counts` prop、`shortcutOptions`/`handleShortcutChange`;按钮行 `.bill-ledger-search__actions-buttons` 恢复单行右对齐(`margin-bottom:8px`)。
|
||||||
|
- `bill-ledger.vue`:模板插入 `<div class="bill-ledger-page__expiry-tags">`(绑定 `query.expiryShortcut`,@change 调 `handleShortcutChange`→`handleSearch`);脚本加 `shortcutOptions` computed(用 `counts`)+ `handleShortcutChange` method;新增 `<style scoped>` 写入 `.bill-ledger-page__expiry-tags`(flex 行、`margin:8px 0`)。`emptyQuery()` 已含 `expiryShortcut:'all'`,无回归。
|
||||||
|
- 选中态样式统一:去掉 `.bill-ledger-page__expiry-tags :deep(.el-check-tag.is-checked)` 的 `background-color:#409eff` + `color:#fff` 蓝底白字覆盖,改用 Element Plus 默认浅蓝底深蓝字,与合同页一致。
|
||||||
|
|
||||||
|
## transportCapacity/ship 新增弹窗 input 宽度统一
|
||||||
|
- 问题:新增/修改弹窗内输入控件宽度不一致(文本框明显窄于下拉框、日期框)。
|
||||||
|
- 根因:`src/views/transportCapacity/ship.vue` 末尾 scoped 样式中独有 `.el-input__wrapper{width:150px !important}`,把文本框包裹层强制压成 150px,而 `el-select`(用 `.el-select__wrapper`)、日期/数字框不受影响 → 文本框与下拉框宽度不齐。同模块的 driver.vue / vehicle.vue 均无此规则。
|
||||||
|
- 修复:
|
||||||
|
1. 删除 `.el-input__wrapper{width:150px!important}` 异常规则。
|
||||||
|
2. 增强 `.ship-form` 内输入控件的 100% 宽度覆盖,补齐 `.el-select__wrapper` / `.el-cascader` / `.el-input__wrapper`,使文本框/下拉/日期/数字框均 100% 填满所在栅格列。
|
||||||
|
- 说明:弹窗仍保留不同栅格列宽(span6/8/12/16)的密集布局,同行字段已等宽;若需跨行全部 input 绝对像素等宽(统一栅格列宽),需进一步重排。
|
||||||
|
- 二次排查(用户反馈仍不一致):把 `.ship-form` 内控件 100% 覆盖从通用 `:deep(.el-input)` 改为更具体的 `:deep(.el-form-item__content > .el-input)` 并加 `!important`,同步覆盖 `.el-input__wrapper`/`.el-select__wrapper`,确保不会被 Element Plus 变量或更具体选择器压回默认/较窄宽度。请用户刷新浏览器验证。
|
||||||
|
- 三次修复(用户反馈第一行 span=6+6+12 三个字段宽度仍不齐):放弃「width: 100% 撑满 col」方案,改用「对齐 vehicle/customer-archive 的 `.archive-form` 风格」——label 固定 `calc(6em + 24px)` + 控件统一 `250px + max-width: 100%`。根因:`el-cascader` 默认 `display: inline-block`,即使 width: 100% 也不易撑满 col-content(与 input 视觉差 ~150px vs ~280px);span=6+6+12 下两列 form-item__content 实际不等宽(label-width=100 被长 label 撑开)。新方案固定 250px 后三字段视觉一致,参照客商档案。
|
||||||
|
- 同时清理模板中冗余 `label-width="88px"`(会被新 CSS 强制覆盖失效)。
|
||||||
|
|
||||||
|
## transportCapacity/ship 证书信息分组:附件下放到对应分组末尾 + label 缩短
|
||||||
|
- 需求:1. 「船舶国籍证书」「内河船舶最低安全配员证书」分组里 label 「证书有效期自」偏长,改短为「有效期自」(与下面统一)。2. 底部集中的 `.cert-upload-grid` 上传区调整到各证书分组末尾,让附件与字段同组。
|
||||||
|
- 修改:
|
||||||
|
- `src/views/transportCapacity/ship.vue` 模板 line 392/431:2 处 `label="证书有效期自"` → `label="有效期自"`。
|
||||||
|
- 模板结构重组:删除底部 `<el-row class="cert-upload-grid">...</el-row>`(原 line 472-548),把 6 个 `<image-upload-field>` 分别插入对应 `cert-block` 末尾(每个用 `class="ship-upload-item"`,独立 `<el-row :gutter="18"><el-col :span="8">`):
|
||||||
|
- 船舶所有权证书分组 → `ownershipCertImage`
|
||||||
|
- 内河船舶安全与环保证书分组 → `safetyCertImage`
|
||||||
|
- 船舶国籍证书分组 → `nationalityCertImage`
|
||||||
|
- 内河船舶最低安全配员证书分组 → `safeManningCertImage`
|
||||||
|
- 光船租赁登记证明书分组 → `leaseContractImage`(label="光船租赁登记证书")
|
||||||
|
- 船舶营业运输证书分组 → `businessTransportCertImage`(label="船舶营业运输证")
|
||||||
|
- 样式清理:删除无用的 `.cert-upload-grid { padding-top:8px }` / `.cert-upload-spacer { height:1px }`;保留 `.cert-block` / `.cert-block__title` / `.ship-upload-item`(`margin-bottom:14px`)。
|
||||||
|
- 验证:用户刷新浏览器看新布局。
|
||||||
|
|
||||||
|
## transportCapacity/ship 组合控件宽度对齐 250px 体系
|
||||||
|
- 问题:基础船舶信息里「安放龙骨日期/建造完工日期」的起止两个 date-picker、「航区」的 select 都是 `flex:1` 撑满所在 span=12 整列(约 498px),明显比单字段 250px 宽,视觉不协调。
|
||||||
|
- 修复(`src/views/transportCapacity/ship.vue` 末尾 scoped 样式):
|
||||||
|
- `.date-range-inline :deep(.el-date-editor)` 由 `flex:1; min-width:0` 增加 `max-width: 250px` → 两个 date-picker 在窗够宽时各对齐 250px、彼此等宽,窄窗时 flex:1 收缩不溢出。
|
||||||
|
- `.navigation-area-field :deep(.el-select)` 由 `flex:1; min-width:0` 增加 `max-width: 250px` → 航区 select 对齐 250px 单字段。
|
||||||
|
- 说明:`flex:1` 保底防溢出,`max-width:250px` 把组合控件拉回与单字段一致的 250px 体系(受所在列宽限制,安放龙骨 span=12 在常规窗宽下两个 date-picker 约 226px 等宽,更宽窗自动到 250px)。
|
||||||
|
|
||||||
|
## business/project-apply.vue 表单「业务部门/承办部门」级联选择器宽度统一
|
||||||
|
- 问题:项目新增/编辑表单(含独立表单页 `/business/project-apply/form`)中,业务部门、承办部门两个 `el-cascader` 的按钮宽度与同行的 select/input 不一致。
|
||||||
|
- 根因:scoped 样式里强制 100% 宽度的控件列表(`el-input` / `el-select` / `el-tree-select` / `el-date-editor`)漏了 `el-cascader`,导致级联选择器按内容宽度收缩,未填满栅格列。
|
||||||
|
- 修复:`src/views/business/project-apply.vue` 末尾 scoped 样式 `.project-apply-form` 的 100% 宽度规则补上 `:deep(.el-cascader)`。Vite HMR 已自动生效,无需重启 dev。
|
||||||
|
|
||||||
|
## 登录页隐藏租户栏
|
||||||
|
- 需求:登录页 `000000` 租户输入框隐藏。
|
||||||
|
- 根因:`src/page/login/userlogin.vue` 第 10 行 `el-form-item` 由 `tenantMode` 控制显示,初始值 `this.website.tenantMode`,`getTenant()` 请求 `/api/system/tenant/info` 成功时会把 `tenantMode=false` 自动隐藏并回填 `loginForm.tenantId`。截图里仍显示租户栏,说明该接口未正常返回(域名解析/后端/跨域),沿用了初始 `tenantMode`。
|
||||||
|
- 修复:
|
||||||
|
1. 删除 `userlogin.vue` 模板第 10-21 行整段租户 `el-form-item`(`v-if="tenantMode" prop="tenantId"`),不留 `v-if` 兜底,直接移除 DOM。
|
||||||
|
2. 同步删除 `loginRules.tenantId` 校验规则。
|
||||||
|
- 保留:`loginForm.tenantId: '000000'` 默认值、`getTenant()` 方法。提交登录时仍带 `tenantId` 字段,后端行为不变;接口正常时仍会用返回的 tenantId 覆盖默认值。
|
||||||
|
- 后续若要重新启用:恢复 `el-form-item` 即可(或改 `data()` 中 `tenantMode: this.website.tenantMode` 走配置开关)。
|
||||||
|
|
||||||
|
## 客商档案评分详情表控件/label 宽度对齐
|
||||||
|
- 问题:评分详情表弹窗内输入框、选择框、日期框宽度不一致;label 要求固定 88px 且超长换行。
|
||||||
|
- 修复(`src/views/vehicle/customer-archive.vue`):
|
||||||
|
- 模板 `label-width="88"` 改为 `label-width="88px"`。
|
||||||
|
- `.score-detail-form` 样式:
|
||||||
|
- `el-form-item__label` 强制 `flex: 0 0 88px !important; width: 88px !important; min-width: 88px !important`,保留 `white-space: normal !important; word-break: break-all; line-height: 1.4` 实现超长换行。
|
||||||
|
- `el-form-item__content > .el-input / .el-select / .el-date-editor` 由固定 `width: 150px` 改为 `width: 100%`,单字段撑满等宽;`.score-detail-form__range` 内双日期框保持 `flex: 1; min-width: 0` 均分。
|
||||||
|
- 参考:客商档案 `.archive-form` 的 label 换行与控件等宽处理思路。
|
||||||
|
|
||||||
|
## 评分详情表弹窗输入框/选择框宽度对齐 + label 88px 换行
|
||||||
|
- 问题:`src/views/vehicle/customer-archive.vue` 中「评分详情表」弹窗表单的输入框、选择框、日期框视觉宽度不一致;label 区域未严格固定 88px。
|
||||||
|
- 修复:
|
||||||
|
1. `el-form` 的 `label-width="88"` 改为 `label-width="88px"`。
|
||||||
|
2. `.score-detail-form` 中 `.el-form-item__label` 强制 `flex:0 0 88px; width:88px; min-width:88px` 并保留 `white-space: normal` 换行。
|
||||||
|
3. 将单字段控件(`.el-form-item__content > .el-input/.el-select/.el-date-editor`)宽度由固定 `150px` 改为 `100%`,使其在固定 label 后自动等宽填满 content 区域。
|
||||||
|
- 保留:「临时额度申请使用期限」组合控件继续用 `.score-detail-form__range` 双 date-picker flex 均分布局。
|
||||||
|
|
||||||
|
|
||||||
|
## customer-archive 评分详情表:label 固定 88px 换行 + 控件宽度统一
|
||||||
|
- 问题:评分详情表弹窗内输入框与选择框宽度不一致,且 label 区域未按 88px 固定换行。
|
||||||
|
- 修复(`src/views/vehicle/customer-archive.vue`):
|
||||||
|
1. `score-detail-form` 的 `label-width="88"` 改为 `label-width="88px"`。
|
||||||
|
2. `.score-detail-form :deep(.el-form-item__label)` 强制 `flex: 0 0 88px !important; width: 88px !important; min-width: 88px !important;`,并保留 `white-space: normal !important; word-break: break-all; line-height: 1.4;` 实现长 label 自动换行。
|
||||||
|
3. 单字段控件(`.el-form-item__content > .el-input / .el-select / .el-date-editor`)统一 `width: 100%`,使输入框与选择框在相同栅格列内等宽。
|
||||||
|
- 说明:「临时额度申请使用期限」双日期组合仍由 `.score-detail-form__range` 控制 `flex:1` 均分,不受单字段 100% 影响。
|
||||||
|
|
||||||
|
## 评分详情表 label 宽度与控件宽度对齐
|
||||||
|
- 需求:评分详情弹窗内输入框与选择框宽度一致;label 宽度固定 88px,超长自动换行。
|
||||||
|
- 修改 `src/views/vehicle/customer-archive.vue`:
|
||||||
|
1. `<el-form>` 的 `label-width="88"` 改为 `label-width="88px"`。
|
||||||
|
2. `.score-detail-form` 的 label 样式强制 `flex: 0 0 88px !important; width: 88px !important; min-width: 88px !important;`,并保留 `white-space: normal !important; word-break: break-all; line-height: 1.4;` 实现换行。
|
||||||
|
3. 单字段控件(`.el-form-item__content > .el-input/.el-select/.el-date-editor`)由固定 `width: 150px` 改为 `width: 100%`,使输入框、下拉框、日期框均撑满同一 content 区,视觉宽度一致。
|
||||||
|
- 说明:「临时额度申请使用期限」两个 date-picker 仍由 `.score-detail-form__range` 的 `flex: 1; min-width: 0;` 控制,不受本次单字段 100% 影响。
|
||||||
|
|
||||||
|
## 客商档案「评分详情表」弹窗表单控件宽度对齐
|
||||||
|
- 问题:`src/views/vehicle/customer-archive.vue` 的「评分详情表」弹窗中,评定日期的 date-picker、拟申请额度等 input、评分量化表 select 等控件宽度不一致;label 需固定 88px 且长 label 换行。
|
||||||
|
- 修复:
|
||||||
|
1. `<el-form>` `label-width="88"` 改为 `label-width="88px"`。
|
||||||
|
2. `.score-detail-form` 样式:`.el-form-item__label` 强制 `flex:0 0 88px` + `width:88px` + `white-space:normal`,确保长 label 换行且宽度恒定。
|
||||||
|
3. 控件由固定 `width:150px` 改为 `.el-form-item__content > .el-input/.el-select/.el-date-editor { width:100% }`,单字段 input/select/date-picker 在 span=6 下等宽撑满 content 区。
|
||||||
|
- 备注:「临时额度申请使用期限」组合区保持 `.score-detail-form__range` 的 `flex:1` 双 date-picker 布局,不受单字段 100% 影响。
|
||||||
|
|
||||||
|
## customer-archive 评分详情表控件宽度与 label 换行对齐
|
||||||
|
- 问题:评分详情弹窗内 input、select、date-picker 宽度不一致;长 label(如「拟申请总资金使用额度(万元)」)未按 88px 换行。
|
||||||
|
- 修改 `src/views/vehicle/customer-archive.vue`:
|
||||||
|
1. `<el-form label-width="88">` → `label-width="88px"`,与全站风格一致。
|
||||||
|
2. `.score-detail-form` 样式:
|
||||||
|
- `el-form-item__label` 强制 `flex: 0 0 88px !important; width: 88px !important; min-width: 88px !important;`,并保留 `white-space: normal` + `word-break: break-all` + `line-height: 1.4` 实现超长 label 换行。
|
||||||
|
- 单字段控件(`.el-form-item__content > .el-input/.el-select/.el-date-editor`)由固定 `width: 150px` 改为 `width: 100%`,使其在 88px label 下等宽撑满 content 区。
|
||||||
|
- 说明:双日期范围字段(临时额度申请使用期限)仍由 `.score-detail-form__range` 用 `flex:1; min-width:0` 均分,不受单字段 100% 规则影响。
|
||||||
|
|
||||||
|
## 评分详情表弹窗 label/控件宽度对齐
|
||||||
|
- 文件:`src/views/vehicle/customer-archive.vue`。
|
||||||
|
- 问题:评分详情表弹窗内 input 与 select/date-picker 宽度不一致;label 宽度未严格固定 88px、长 label 未换行。
|
||||||
|
- 修复:
|
||||||
|
1. `el-form` 的 `label-width="88"` 改为 `label-width="88px"`。
|
||||||
|
2. `.score-detail-form` 样式里 label 强制 `flex: 0 0 88px !important; width: 88px !important; min-width: 88px !important;`,并保留 `white-space: normal`、`word-break: break-all` 换行。
|
||||||
|
3. 单字段控件(`.el-form-item__content > .el-input/.el-select/.el-date-editor`)统一 `width: 100%`,取代原先未生效/不齐的 `width: 150px`。
|
||||||
|
- 备注:「临时额度申请使用期限」两个 date-picker 仍由 `.score-detail-form__range` 的 `flex:1` 均分,不受 100% 宽度影响。
|
||||||
|
|
||||||
|
## 客商档案「评分详情表」弹窗控件宽度对齐与 label 换行
|
||||||
|
- 问题:评分详情表弹窗中 input、select、date-picker 宽度不一致;长 label 未换行。
|
||||||
|
- 修复 `src/views/vehicle/customer-archive.vue`:
|
||||||
|
1. `el-form` 的 `label-width="88"` 改为 `label-width="88px"`。
|
||||||
|
2. `.score-detail-form` 样式:label 强制 `flex:0 0 88px`、`width:88px`、`white-space:normal`,超出换行。
|
||||||
|
3. 单字段控件(`.el-form-item__content > .el-input/.el-select/.el-date-editor`)统一 `width:100%`,撑满 content 区,与选择框等宽。
|
||||||
|
- 保留 `.score-detail-form__range` 内双 date-picker `flex:1; min-width:0` 的「起—止」布局。
|
||||||
|
|
||||||
|
## 评分详情表弹窗 label 与输入控件宽度对齐
|
||||||
|
- 文件:`src/views/vehicle/customer-archive.vue`
|
||||||
|
- 问题:评分详情表弹窗内输入框、下拉框、日期框宽度不一致;长 label 未换行。
|
||||||
|
- 修改:
|
||||||
|
1. `<el-form label-width="88">` 改为 `label-width="88px"`。
|
||||||
|
2. `.score-detail-form` label 样式强制 `width: 88px !important; flex: 0 0 88px !important; min-width: 88px !important;`,并保留 `white-space: normal` / `word-break: break-all` 换行。
|
||||||
|
3. 单字段控件(`.el-form-item__content > .el-input / .el-select / .el-date-editor`)统一 `width: 100%`,填满 label 剩余区域,保证 input/select/date-picker 视觉等宽。
|
||||||
|
- 说明:组合日期区间(`.score-detail-form__range`)保持两个 date-picker `flex:1` 均分,不受单字段 100% 影响。
|
||||||
|
|
||||||
|
## customer-archive 评分详情表 label/控件宽度对齐
|
||||||
|
- 问题:评分详情弹窗表单的输入框与选择框宽度不一致;label 宽度要求 88px 且超长换行。
|
||||||
|
- 修复:`src/views/vehicle/customer-archive.vue`
|
||||||
|
- `label-width="88"` → `label-width="88px"`。
|
||||||
|
- `.score-detail-form` 样式:`.el-form-item__label` 强制 `flex: 0 0 88px !important; width: 88px !important; min-width: 88px !important;`,并保留 `white-space: normal !important` 等换行样式。
|
||||||
|
- 控件由固定 `width: 150px` 改为 `.el-form-item__content > .el-input/.el-select/.el-date-editor { width: 100%; }`,使输入框、下拉框、日期框在各自列内等宽撑满。
|
||||||
|
|
||||||
|
## 评分详情表弹窗:label 固定 88px 换行 + 输入/选择框宽度一致
|
||||||
|
- 问题:`src/views/vehicle/customer-archive.vue` 评分详情弹窗内输入框与选择框宽度不一致,label 长文本未换行。
|
||||||
|
- 修复:
|
||||||
|
1. `label-width="88"` → `label-width="88px"`。
|
||||||
|
2. `.score-detail-form` 样式:`.el-form-item__label` 强制 `flex: 0 0 88px !important; width: 88px !important; min-width: 88px !important; white-space: normal !important;` 确保 88px 固定宽度并自动换行。
|
||||||
|
3. 单字段控件 `.el-form-item__content > .el-input / .el-select / .el-date-editor` 由固定 `150px` 改为 `width: 100%`,使输入框与选择框在各自栅格列内等宽撑满。
|
||||||
|
- 备注:「临时额度申请使用期限」双日期组合仍走 `.score-detail-form__range` 的 `flex:1; min-width:0`,不受单字段 100% 影响。
|
||||||
|
|
||||||
|
## 客商档案评分详情表控件宽度统一 + label 88px 换行
|
||||||
|
- 问题:`src/views/vehicle/customer-archive.vue` 评分详情弹窗内输入框与选择框宽度不一致;label 区域未严格固定 88px,长 label 换行效果不稳定。
|
||||||
|
- 修复:
|
||||||
|
1. 模板 `el-form` 的 `label-width="88"` 明确为 `label-width="88px"`。
|
||||||
|
2. scoped 样式 `.score-detail-form`:
|
||||||
|
- `.el-form-item__label` 强制 `flex: 0 0 88px !important; width: 88px !important; min-width: 88px !important;`,并保留 `white-space: normal !important` / `word-break: break-all` 让超长 label 自动换行。
|
||||||
|
- 控件统一由固定 `width: 150px` 改为 `.el-form-item__content > .el-input/.el-select/.el-date-editor { width: 100%; }`,使输入框、下拉框、日期框在各自栅格列内等宽撑满。
|
||||||
|
- 说明:「临时额度申请使用期限」双日期组合区仍由 `.score-detail-form__range` 控制 flex:1 均分,不受单控件 100% 规则影响。
|
||||||
|
|
||||||
|
## customer-archive 评分详情表控件宽度统一 + label 换行
|
||||||
|
- 需求:评分详情弹窗内输入框与选择框宽度不一致;label 宽度需固定 88px 且超长换行。
|
||||||
|
- 修改(`src/views/vehicle/customer-archive.vue`):
|
||||||
|
1. 模板 `score-detail-form` 的 `label-width="88"` → `label-width="88px"`。
|
||||||
|
2. scoped 样式 `.score-detail-form`:`.el-form-item__label` 固定 `flex: 0 0 88px !important; width: 88px !important; min-width: 88px !important;`,并保留 `white-space: normal !important; word-break: break-all; line-height: 1.4;` 实现换行。
|
||||||
|
3. 控件宽度由固定 `150px` 改为 `.el-form-item__content > .el-input/.el-select/.el-date-editor { width: 100%; }`,使输入框、下拉框、日期框在同一 content 区域内等宽撑满。
|
||||||
|
- 说明:双日期字段(临时额度期限)仍走 `.score-detail-form__range` 的 `flex:1` 布局,保持两个日期框均分并对齐。
|
||||||
|
|
||||||
|
## 评分详情表输入框/选择框宽度对齐 + label 固定 88px 换行
|
||||||
|
- 文件:`src/views/vehicle/customer-archive.vue`
|
||||||
|
- 问题:评分详情弹窗内输入框、选择框、日期框宽度不一致;长 label 需要固定 88px 并允许换行。
|
||||||
|
- 修改:
|
||||||
|
1. `label-width="88"` → `label-width="88px"`。
|
||||||
|
2. `.score-detail-form :deep(.el-form-item__label)` 强制 `flex:0 0 88px !important; width:88px !important; min-width:88px !important`,保留 `white-space:normal !important; word-break:break-all; line-height:1.4`。
|
||||||
|
3. 控件宽度由固定 150px 改为 `.el-form-item__content > .el-input/.el-select/.el-date-editor { width:100%; }`,使输入框、选择框、日期框在固定 label 后自动等宽填满 content 区。
|
||||||
|
- 保留 `.score-detail-form__range` 内双 date-editor `flex:1; min-width:0` 的收缩行为。
|
||||||
|
|
||||||
|
## vehicle/customer-archive 两处「详细地址」hover 溢出提示完整地址
|
||||||
|
- 需求:编辑客商档案页,详细地址字段(窄栏 + 长文本易截断)鼠标移上去显示完整地址;仅文本溢出才显示。
|
||||||
|
- 实现:两处 `el-input` 用 `<el-tooltip>` 包裹,`content` 绑定字段值,`placement="top"`,`:disabled="!xxxOverflow"`(溢出才显示),`style="display:block; width:100%"`(el-tooltip 根 span 默认 inline,必须 block 才能撑满栅格列)。
|
||||||
|
- 主表单(line 317,`archiveForm.registeredDetailAddress`):`ref="registeredDetailInput"` + `@input` 实时重算。
|
||||||
|
- 发票弹窗(line 1008,`invoiceForm.registeredDetailAddress`):`ref="invoiceDetailInput"`,readonly 地图回填,靠 `watch` + `openInvoice` 打开后 `nextTick` 重算(弹窗按需挂载,预填数据打开后 watch 已不触发)。
|
||||||
|
- 新增:`data` 加 `registeredDetailOverflow/invoiceDetailOverflow` 标记;`watch` 监听两字段值变化→`checkOverflow`;`methods.checkOverflow(refName,flag)` 读 `inst.$el.querySelector('input')` 的 `scrollWidth > clientWidth` 判定溢出;`openInvoice` 打开后双 nextTick 重算发票框溢出。
|
||||||
|
- 验证:`vite build --outDir /tmp/tms-check-build` 零报错(常规 build 仅因安全删除保护拦截清空 dist,与代码无关)。
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
## 合同管理独立表单页下拉框定位修复
|
||||||
|
- `src/views/business/contract-manage.vue` 全部 12 处 `el-select` 加 `:teleported="false"` + `:fit-input-width="true"`:下拉保持在输入框正下方、宽度与输入框一致(原 teleport 到 body 导致下拉漂移且按内容撑宽)。
|
||||||
|
- 若后续新增独立表单页出现同样问题,照加这两个属性即可。
|
||||||
|
- 追加:teleported=false 后 popper 因下方空间不足自动上翻,再给 12 处 el-select 加 `placement="bottom-start"` + `:popper-options="{ modifiers: [{ name: 'flip', enabled: false }] }"` 强制始终在下方展示。
|
||||||
|
|
||||||
|
## 调度弹窗司机输入框宽度不一致修复
|
||||||
|
- 现象:「编辑调度信息(精简录入)」弹窗中,司机(el-autocomplete)输入框比同行 250px 的其他输入框宽。
|
||||||
|
- 根因:`business-crud-page.vue` 约 13434 行的「表单控件统一 250px」规则漏了 `.el-autocomplete`(autocomplete 根节点是包装层,内部才是 .el-input)。
|
||||||
|
- 修复:在该规则选择器列表追加 `> .el-autocomplete` 和 `> .el-autocomplete > .el-input`,与运单独立表单页(13658 行 business-crud-page--form-page 先例)一致。
|
||||||
|
- 经验:凡「控件统一 250px」类规则,选择器必须包含 `.el-autocomplete` 包装层。
|
||||||
|
|
||||||
|
## 调度弹窗收发货地址行溢出修复
|
||||||
|
- 现象:「编辑调度信息」弹窗收发货信息行,地址输入框过宽,右侧「联系方式」列被挤出弹窗。
|
||||||
|
- 根因:`__dispatch-shipping-form __route-address-row` 网格地址列为 `minmax(280px, 1fr)`,各列最小宽度合计 1030px,弹窗不够宽时 grid 不换行直接溢出。
|
||||||
|
- 修复:地址列改 `minmax(0, 1fr)`(宽屏仍 1fr 拉伸,窄窗自动收窄,总最小宽降到 750px)。
|
||||||
|
- 备注:基础规则 `__route-address-row`(13750 行,运单独立表单页等在用,7 列含 40px 按钮列)仍是 minmax(280px,1fr),同样窄窗有溢出风险,暂未动(用户未反馈该页问题)。
|
||||||
|
|
||||||
|
## el-select 选中值 / 占位符颜色区分(全局)
|
||||||
|
- 需求:已选值 #606266,未选值保持 placeholder 灰 #909399。
|
||||||
|
- 根因:Element Plus 的选中值文本与占位符**共用同一节点** `<div class="el-select__selected-item el-select__placeholder [is-transparent]">`,仅靠 `.el-select__selected-item` 无法区分,导致未选态也被染成选中色。
|
||||||
|
- 区分标志:只有「未选值」或「已展开但无搜索输入」时才带 `.is-transparent`。
|
||||||
|
- 修复(`src/styles/element-ui.scss`):选中规则改为 `.el-select__wrapper:not(.is-disabled) .el-select__selected-item:not(.is-transparent)`;多选 tag 文字另加 `.el-select__tags-text` 规则(tag 自身 color 优先级更高,父级规则盖不到)。
|
||||||
|
- 经验:以后写任何「区分 el-select 有值/无值」的 CSS,一律用 `:not(.is-transparent)`。
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
## 付款比例设置提示改为 tooltip(contract-manage / contract-manage-change)
|
||||||
|
- 需求:将「付款比例设置」区块原有的红色文字提示改为标题右侧的 `?` 图标悬停提示。
|
||||||
|
- 改动:编辑弹窗、详情弹窗、合同变更页三处标题后添加 `el-tooltip` + `el-icon-question-filled`,content 为「非必填;配置付款比例时,合计必须等于100%。」;同时移除 contract-manage.vue 中原 `.contract-manage-form__tip` 红色提示文字及对应样式。
|
||||||
|
|
||||||
|
## 必填星号与 label 首行齐平(全局)
|
||||||
|
- 根因:`element-ui.scss` 中 `.el-form-item--label-right .el-form-item__label { align-items:center }` 使 inline-flex label 的星号 `::before`(独立 flex 子项)在多行折行时被垂直居中。
|
||||||
|
- 修复:label 改 `align-self:center`(整体仍随行垂直居中)+ `align-items:flex-start`(星号/文字首行齐平),覆盖 label-right/left/top 三种位置;旧 inline-block 注释已修正。
|
||||||
|
- 影响页面:project-apply 独立表单页等所有多行 label 场景(弹窗内 `white-space:nowrap` 不受影响)。
|
||||||
|
|
||||||
|
## 必填星号首行对齐:改用 align-items: normal(stretch 方案)
|
||||||
|
- 最终写法(`src/styles/element-ui.scss`):`.is-required .el-form-item__label { align-self:center; align-items:normal; }` + `::before { display:inline-block; height:auto; line-height:inherit; margin-right:4px }`。
|
||||||
|
- 原理:stretch 让星号盒子在「换行时」被拉伸到 label 全高、内容从顶部排布 → 贴首行;单行时盒子=一行高,视觉不变(比 flex-start 更贴合「只换行才生效」)。
|
||||||
|
- 前提:::before 高度必须 auto 且继承行高;label 不能是 固定 height + 等高 line-height,否则退化成居中,需 JS 判行数加 class。
|
||||||
|
|
||||||
|
## 合同变更页(contract-manage-change.vue)
|
||||||
|
- 「其它附件」上传按钮改为 `type="primary" plain icon="el-icon-upload"`,与「变更材料」区 vehicle-attachment-upload 内置按钮一致。
|
||||||
|
- 分区标题必填星号:`.dialog-section-title.is-required::after { content:'*'; color:#f56c6c }`(`::before` 已被蓝色竖条占用,星号走 `::after`)。
|
||||||
|
- 再次变更逻辑:`load()` 里 form 强制重置 `changeContent/changeReason/changeAttachmentsJson = ''`、`changeMaterials = []`(不带上一次内容);新增 `mergeChangeAttachments()` 把上一次的 `changeAttachmentsJson` 归集进 `attachments`(其它附件),按 url/名去重,数字 size 转格式化文本,提交时随 attachmentsJson 持久化。
|
||||||
|
|
||||||
|
## 合同变更记录对齐客商变更记录(contract-manage.vue)
|
||||||
|
- 编辑页与详情弹窗的两处「变更记录」表格新增「变更内容」列:`formatContractChangeContent(row)` 复用 `buildDetailChangeRecordRows` 生成「字段:变更前:x → 变更后:y」摘要,单行省略 + `el-tooltip` 全量悬浮(样式 `.contract-change-record-content-cell/-tooltip` 照搬客商 `change-record-content-*`)。
|
||||||
|
- 编辑页操作列由单个「流程」改为「详情 + 流程」(`openDetailChangeRecord` + `openFlow`,宽度 120→150);详情弹窗表格「查看详情」改「详情」与客商一致。
|
||||||
|
- 值格式化增强:`billingPlanJson`→方案名(默认)列表、`paymentRatioJson`→「第N笔 xx%」、`settlementRuleJson/preSettlementConfigJson/formalSettlementConfigJson`→可读摘要(自动生成开关/结算类型/周期类型/截单日/周期天数,含预结算+正式结算双段)。
|
||||||
|
- `buildDetailChangeRecordRows` 过滤条件补「前后都为空则跳过」(原来只比 JSON 字符串,'' 与 null 会漏)。
|
||||||
|
|
||||||
|
## 星号方案补丁(关键)
|
||||||
|
- EP 的 `.el-form-item__label` 写死 `height:32px; line-height:32px`(项目只覆盖了 line-height:14px),导致 stretch 方案文字顶到 32px 盒子顶部、单行不居中。
|
||||||
|
- 修复:`.is-required .el-form-item__label { height:auto; align-self:center; align-items:normal }`——height:auto 收缩到内容高 + align-self:center 行内居中 + normal(stretch) 仅换行时星号贴首行。
|
||||||
|
|
||||||
|
## 车辆认证审核弹窗重构(transportCapacity/vehicle.vue)
|
||||||
|
- 背景:用户反馈「样式不统一、内容不全」。根因是审核弹窗用 `<el-descriptions :column="3" border>`(大白表 + 居中 label),与同页编辑弹窗的 `<section-card>` 白卡分组风格不一致。
|
||||||
|
- 改造:弹窗主体改为 4 个 `section-card` 分组 —— 基础信息 / 尺寸重量 / 证件信息 / 证件图片;内部 `el-form label-position="right" label-width="auto"` + `el-row/el-col`(3 列 `:span="8"`,所属组织与备注占 `:span="24"`)。
|
||||||
|
- 内容补全:新增「证件图片」分组,6 张证件照用 `<image-upload-field readonly large class-prefix="vehicle">` 只读展示(可点击放大);证件图片尺寸 240×151 复用已有全局 `:deep(.vehicle-uploader--large)` 规则,无需新写。
|
||||||
|
- label 单位统一为中文全角括号:`外廓长度(毫米)` / `核定载质量(KG)`(原为半角 `外廓长度(mm)`)。
|
||||||
|
- 方法重构:`formatAuditValue(item)` → `auditValue(prop)`,签名更简洁;新增 longTermMap,对 drivingLicenseEndDate / roadTransportCertEndDate / annualReviewEndDate / compulsoryScrapDate 在勾选长期有效时返回「长期有效」,避免与未填写的「-」混淆(与列表页 formatEndDate 行为一致)。
|
||||||
|
- 删除已无引用的 `auditDetailItems` computed。
|
||||||
|
|
||||||
|
## 全站弹窗标题补 4px 主色竖条(全局 element-ui.scss)
|
||||||
|
- 发现:`.el-dialog__title` 全局只配了 `inline-flex + gap:8px`,**没有竖条**(竖条此前只在 `.dialog-section-title::before`),导致全站 el-dialog 标题都不带竖条,不符合 AGENTS.md 4.8。
|
||||||
|
- 修复:新增 `.el-dialog__title:not(:empty)::before { width:4px; height:16px; border-radius:2px; background:var(--el-color-primary) }`。
|
||||||
|
- 注意:必须加 `:not(:empty)`,否则无标题弹窗会出现孤立蓝条。
|
||||||
|
- customer-archive.vue 评分详情弹窗:将「临时额度申请使用期限」区间行(span=12 + 至 连接)拆分为两个 span=6 独立字段「临时申请额度有效期起 / 止」,字段名 tempCreditStartDate/End 不变,删除废弃样式 .score-detail-form__range。
|
||||||
|
- temporary-credit-limit 文案调整:option 增加 addTitle/editTitle/viewTitle=「新增/编辑/查看临时额度申请」(覆盖 crud-dialog-title mixin 按菜单名注入的「临时额度管理」);remark label 改「临时额度信息」(hide:true 无表格副作用,rules 同步);projectName 表单 label 用 `#projectName-label` 插槽改「项目额度信息」,列 label 保持「项目名称」不动表格列头/搜索。Avue 支持-{prop}-label / -header / -search 插槽分离表单/表头/搜索文案。
|
||||||
|
- temporary-credit-limit「申请有效期至」增加「必须晚于当前日期」校验:common.js 新增通用 `futureDateRule(label)`(trigger: ['change','blur'],空值/非法值放行,按 0 点比较,当天也算过期),temporary-credit-limit.js 的 validUntil rules 追加该规则。已验证 Element Plus 通用 picker 在选中日期时调用 formItem.validate('change'),故选择即刻提示;form.validate() 不带 trigger 时会跑全部规则,提交仍拦截。
|
||||||
|
- temporary-credit-limit 搜索占位文案统一:createCrudOption(withSearchPlaceholders([...])),搜索栏 placeholder 变为「请输入/请选择」不带字段名,与 waybill-manage / transport-plan / shipping-template 同一模式。
|
||||||
|
- temporary-credit-limit 弹窗表单 placeholder 统一为「请输入/请选择」不带字段名:option 各列加 placeholder(projectCode/undertakeDeptName/projectFundLimit/usedFundLimit/remainingFundLimit/remark=请输入,validUntil=请选择),视图 formslot 自定义控件(projectName select、applyLimit input)placeholder 同步改。
|
||||||
|
|
||||||
|
## 【项目管理】列表增加「项目资金使用预警」(project-apply)
|
||||||
|
- 需求:列表新增「资金使用风险」列,高风险红色、中风险黄色;列表头部增加「资金使用风险」快速筛选标签(高风险(数)/中风险(数)),点击筛选。规则 tooltip:中风险≥80%、高风险≥90%,使用率=已使用资金/项目资金使用额度×100%。
|
||||||
|
- 前端(已落地,src/views/business/project-apply.vue + src/option/business/project-apply.js + src/api/business/project-apply.js):
|
||||||
|
- option/project-apply.js 新增列 `fundUseRisk`(slot:true, hide 列表显示) + 导出 `fundUseRiskOptions`(high/medium/none) 与 `fundUseRiskThreshold`{high:90, medium:80}。
|
||||||
|
- view 新增 `#header` 插槽:`el-check-tag` 快速筛选(复用 contract-manage 到期标签交互),带数量(fundRiskStats[statKey])与 `?` tooltip 规则;新增 `#fundUseRisk` 插槽渲染彩色 `el-tag`(high→type=danger 红,medium→type=warning 黄,无→"-")。
|
||||||
|
- data 加 fundRiskScope/fundRiskStats{high,medium,none}/fundRiskTagOptions;onLoad 追加 `fundUseRisk` 筛选参数 + 成功后 refreshFundRiskStats;新增 changeFundRiskScope(再次点击取消)、refreshFundRiskStats;searchReset 清空风险筛选。
|
||||||
|
- api 新增 `getFundRiskStats` → GET /blade-transport/project-apply/fund-risk-stats。
|
||||||
|
- 后端契约(未改,待确认数据来源):ProjectApplyVO 补齐 `fundUseRisk`(high/medium/none)、`fundUseRiskName`、`fundUseRate`、`usedFundLimit`/`remainingFundLimit`;列表 selectProjectApplyPage 填充每行风险 + buildQuery 支持 `fundUseRisk` 过滤;新增 `/fund-risk-stats` 返回 {high,medium,none}。
|
||||||
|
- ⚠️ 关键阻塞:系统里项目与任何费用表(运输计划/运单/账单/客商)均无关联字段,`usedFundLimit`(已使用资金) 无真实取数来源;临时额度模块的 project-apply/list 返回里 usedFundLimit 实际为空。需业务方确认「已使用资金」口径后才能算风险等级,否则列表风险列全空、统计为 0。
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
# 项目长期记忆(tms-erp-web-ws / Saber3 客商模块)
|
||||||
|
|
||||||
|
## 设计约定(已确认)
|
||||||
|
|
||||||
|
### 弹窗分组:统一用全局 `<section-card>`
|
||||||
|
- 弹窗 body 背景由 `src/styles/element-ui.scss` 全局 `.el-dialog__body { background:#f5f6fa; padding:16px }` 统一灰底。
|
||||||
|
- 每组 = 白底卡片:4px 主色竖条 + 8px 间距 + 圆角 6px + 淡阴影。组件全局注册 `<section-card title="...">`,支持 `#title` / `#extra` slot。
|
||||||
|
- 仅 3 项的小分组用 `el-col :span="8"` 均分一行。
|
||||||
|
|
||||||
|
### Avue 内置弹窗用 section-card:dialogCustomClass 透明化 avue-form
|
||||||
|
- option 顶层加 `dialogCustomClass:'xxx-dialog'`,再在 `element-ui.scss` 写三条:
|
||||||
|
1. `.xxx-dialog .avue-form { background:transparent; box-shadow:none; padding:0 }`
|
||||||
|
2. `.xxx-dialog .avue-form__group > .el-col > .el-form-item { margin-bottom:0 }`
|
||||||
|
3. `.xxx-dialog .avue-dialog__footer, .avue-crud__dialog .xxx-dialog .avue-dialog__footer { margin-top:0 !important }`
|
||||||
|
|
||||||
|
### 弹窗底部操作栏浮动(全局 CSS)
|
||||||
|
- `.el-dialog__body { max-height:calc(100vh - 200px); overflow-y:auto; overflow-x:hidden }`
|
||||||
|
- `.el-dialog__footer { margin:0; padding:12px 20px; background:#fff; border-top:1px solid #ebeef5; box-shadow:0 -2px 8px rgba(0,0,0,.04) }`
|
||||||
|
- Avue 内置弹窗 footer 真实 class 是 `.avue-dialog__footer`,须 `position:sticky; bottom:0; flex:none; background:#fff` 贴底。
|
||||||
|
|
||||||
|
### 上传证件区域尺寸(transportCapacity)
|
||||||
|
- 身份证/行驶证/道路运输证等固定 **240×151px**(1.586:1)。
|
||||||
|
- scoped 覆盖 `.xxx-uploader--large { width:240px }` 与 `--large .el-upload { width:100%; height:151px }`。
|
||||||
|
|
||||||
|
### 上传图片点击放大预览
|
||||||
|
- `src/components/image-upload-field/main.vue` 用 `<el-image preview-src-list fit="contain" preview-teleported :z-index="3000" @click.stop>`,阻止冒泡到 el-upload。
|
||||||
|
|
||||||
|
### 独立表单页标题 `archive-page-form__title`
|
||||||
|
- 样式全局化到 `src/styles/element-ui.scss`:18px/600/#303133 + margin-bottom:20px + 4px 主色竖条 + 8px 间距。
|
||||||
|
- business-crud-page 系列通过 `formPageTitle` prop 注入;loading-manage / master-order 手写前置标题。
|
||||||
|
|
||||||
|
### 表格列内嵌输入控件全宽
|
||||||
|
- `element-ui.scss` 全局兜底:`.el-table .el-table__body td.el-table__cell .cell > .el-input-number/.el-input/.el-select/.el-date-editor/.el-cascader { width:100% }`。
|
||||||
|
|
||||||
|
### 业务状态标签文字化
|
||||||
|
- 全站"状态语义"的 `el-tag` 加 `class="status-text"`,转为普通文字,不带颜色/背景/边框/圆点。
|
||||||
|
- 实现:`element-ui.scss` 末尾 `.el-tag.status-text { ... }`。
|
||||||
|
|
||||||
|
### 弹窗 / 独立表单页底栏按钮统一规则
|
||||||
|
- **排序**:从左到右按次→主排(次要居左、主操作居右),主操作永远放最后。
|
||||||
|
- 顺序模板:`[可选辅助工具(如同步/导入)][取消][保存草稿][提交/确认]`
|
||||||
|
- **容器布局(2026-08-27 改为「去 gap」)**:`display:flex; justify-content:flex-end;`(**不写 `gap`**)。按钮间距统一由 Element Plus 全局默认的相邻按钮 `.el-button+.el-button{margin-left:12px}` 提供,全站恒为 12px。原各 footer 写死的 `gap:8/12px` 已全部移除。`src/styles/element-ui.scss` 里 `.el-dialog__footer .el-button + .el-button` 也已从 `8px` 改 `12px`,与之一致。弹窗关闭按钮继承 Element Plus 自带 `.el-dialog__footer` 全局规则(见上条),独立页用 `.archive-form__footer` 配 `.archive-page-form & { position:fixed; right:0; left:230px; bottom:0; z-index:10; margin:0; padding:12px 24px; border-top:1px solid #eff1f7; background:#fff; box-shadow:0 -2px 8px rgba(0,0,0,.06); }`。
|
||||||
|
- **⚠️ 浮动底部必须用 `position:fixed`,不能用 `position:sticky`**:本项目布局 `.app-main` / `.basic-container` 祖先带 `overflow:hidden`,会导致 `sticky` 永不触发(footer 不吸底)。固定写法照搬 customer-archive:
|
||||||
|
```scss
|
||||||
|
.xxx-actions {
|
||||||
|
position: fixed; right: 0; left: 230px; bottom: 0; margin: 0; z-index: 10;
|
||||||
|
/* flex-end + padding + border-top + 白底 + 上投影 */
|
||||||
|
}
|
||||||
|
:global(.avue--collapse .xxx-actions) { left: 60px; } /* 侧栏折叠 */
|
||||||
|
:global(.avue-layout--horizontal .xxx-actions) { left: 0; } /* 横向布局 */
|
||||||
|
```
|
||||||
|
注意 `:global()` 要把「整个选择器」包进去(customer-archive 写法),不要只包前缀,否则 scoped 会在中间选择器上追加 data 属性导致不匹配。
|
||||||
|
- **颜色层级 3 档**:
|
||||||
|
| 语义 | type | 备注 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 次要(取消 / 关闭) | 不写 type(默认) | 灰描边 |
|
||||||
|
- **全站底部操作栏「返回」已统一改为「取消」**:独立表单页返回列表、弹窗关闭、主从编辑器返回等场景一律写「取消」;仅只读查看 / 纯关闭场景保留「关闭」。
|
||||||
|
| 中间步骤(保存草稿 / 按匹配结果更新) | `type="primary" plain` | 蓝描边 |
|
||||||
|
| 主操作(提交 / 完成对账 / 复评确认) | `type="primary"` | 蓝实心 |
|
||||||
|
- **禁区**:
|
||||||
|
- 保存草稿和主操作不允许同为 `type="primary"`,否则两个蓝实心无视觉层级。
|
||||||
|
- 主操作禁止用 `type="success"`(绿)。全站统一深蓝实心。
|
||||||
|
- **生效页面(已按规则统一)**:
|
||||||
|
- `business/project-apply.vue`(footer + 变更 footer)
|
||||||
|
- `vehicle/customer-archive.vue` 全部 5 处 footer(`archive-form__footer` 主表单页 + `contact-dialog`/`receipt-dialog`/`invoice-dialog` 子弹窗 + `score-detail-dialog` 含总分合计)
|
||||||
|
- `business/components/business-crud-page.vue` 的 PageAvueForm 独立表单页(`#menu-form-before` slot 新增 `取消` 按钮,shipping-template 模式验证通过;影响 `/business/waybill-manage/form`、`/business/transport-plan/form`、`/business/shipping-template/form` 三处独立表单页)
|
||||||
|
- `business/components/master-order-editor.vue` 主 `<footer>`(取消 default + 暂存/确认创建 plain + 创建并调度 primary;`position:fixed` 浮动,含 `avue--collapse`/`avue-layout--horizontal` 左偏移)
|
||||||
|
- **payment 模块**表单页(`__actions` 均 `flex-end` + `position:fixed` 浮动,含 `avue--collapse`/`avue-layout--horizontal` 左偏移):
|
||||||
|
- `payment-application-form.vue`(`[取消][保存 plain][提交]`)
|
||||||
|
- `invoice-application-form.vue` / `invoice-receipt-form.vue`(`[取消][同步 plain][保存 plain][提交]`)
|
||||||
|
- `bill-ledger-form.vue`(`[取消][确认 primary]`)
|
||||||
|
- `bill-payment-form.vue`(`[取消][保存 plain][提交]`)
|
||||||
|
- `receipt-flow-form.vue`(`[取消][确认 primary]`)
|
||||||
|
- `receipt-claim-record-form.vue`(单「关闭」按钮,居中→右对齐浮动)
|
||||||
|
- **settlement 模块**:
|
||||||
|
- `settlement/components/pre-settlement-editor.vue`(弹窗 + pageMode 两处 footer:保存加 `plain`;`&__page-actions` 加 `position:fixed` 浮动,含 `avue--collapse`/`avue-layout--horizontal` 左偏移)
|
||||||
|
- `settlement/components/formal-settlement-editor.vue`(`.formal-editor__page-actions` 加 `position:fixed` 浮动,含 `avue--collapse`/`avue-layout--horizontal` 左偏移;按钮已 `[取消][提交]` 合规)
|
||||||
|
- `settlement/receivable-payable-detail.vue`(生成费用弹窗「上一步」改 `plain`)
|
||||||
|
- **已合规未动**:`settlement/components/transport-reconciliation-editor.vue`(`[取消][保存草稿 plain][按匹配结果更新账单 plain][完成对账 primary]`)、`settlement/components/settlement-adjustment-editor.vue`(弹窗 `[取消][保存 primary]`)、`business/temporary-credit-limit.vue`。
|
||||||
|
- **Avue-form footer 结构注意**:`#menu-form-before` slot 内容在 Avue 内置按钮之**左**;`#menu-form` slot 在 Avue 内置按钮之**右**。要实现 `[次要][Avue 主操作]` 顺序,把次要按钮放进 `#menu-form-before`,主操作保留 Avue 内置即可。
|
||||||
|
- **Avue 表单 footer 按钮去图标(统一纯文字)**:Avue 的 `avue-form` 提交/清空按钮内置图标(`submitIcon: 'el-icon-check'`、`emptyIcon: 'el-icon-delete'`),且 Avue 源码用 `|| 'el-icon-xxx'` 兜底,无法通过 option 配置去掉。已在 `business-crud-page.vue` 样式里用 `:global(.business-crud-form-page-dialog .avue-form__menu .el-icon){display:none}` 统一隐藏,仅作用于独立表单页(PageAvueForm 容器),不影响其它弹窗。新增/编辑弹窗内的 Avue 表单若也要去图标,复用此选择器即可。
|
||||||
|
- **复杂 footer 例外**:当 footer 需要在按钮组左侧展示「总分合计 / 数量统计」等聚合信息时,块用 `flex:1` 推自己到最左,按钮组按上述规则排在右半边(参考 `score-detail-dialog`)。
|
||||||
|
|
||||||
|
## 关键文件
|
||||||
|
- `src/components/section-card/main.vue`
|
||||||
|
- `src/styles/element-ui.scss`
|
||||||
|
- `src/views/vehicle/customer-archive.vue`
|
||||||
|
- `src/views/transportCapacity/{driver,vehicle,ship}.vue`
|
||||||
|
- `src/views/business/project-apply.vue`
|
||||||
|
|
||||||
|
## 环境注意
|
||||||
|
- Vite 代理跨域:`axios.withCredentials=true`,改 `.env`/`vite.config.mjs` 须重启 dev。
|
||||||
|
|
||||||
|
## business-crud-page 复用模式
|
||||||
|
- option 由 prop `crudOption` 经 `cloneOption` 克隆。
|
||||||
|
- 独立表单页走 `PageAvueForm`,不包 el-dialog。
|
||||||
|
- 自定义详情弹窗:`config.detailButton=true` + `detailSections:[{title, fields}]`。
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
# AGENTS.md
|
# AGENTS.md
|
||||||
|
|
||||||
本文件用于指导 Codex (Codex.ai/code) 在 Saber3 代码仓库中工作时的行为规范。
|
本文件用于指导 Codex (Codex.ai/code) 在 Saber3 代码仓库中工作时的行为规范。
|
||||||
|
|
||||||
> 本规范适用于 Saber3 前端工程的所有开发任务,为强制性条款。除非用户显式豁免,任何条目都不得忽视或删减。
|
> 本规范适用于 Saber3 前端工程的所有开发任务,为强制性条款。除非用户显式豁免,任何条目都不得忽视或删减。
|
||||||
>
|
>
|
||||||
> 作为 AI 助手参与本项目开发时,你必须:
|
> 作为 AI 助手参与本项目开发时,你必须:
|
||||||
|
>
|
||||||
> - 每次输出前深度理解 BladeX 微服务架构体系、Saber3 前端工程结构和 Vue 3 技术栈特征
|
> - 每次输出前深度理解 BladeX 微服务架构体系、Saber3 前端工程结构和 Vue 3 技术栈特征
|
||||||
> - 当回答依赖外部知识时,先查询 Vue 3、Element Plus、Avue、Vite 等官方文档
|
> - 当回答依赖外部知识时,先查询 Vue 3、Element Plus、Avue、Vite 等官方文档
|
||||||
> - 若需求含糊,先复述已知信息并列出关键澄清问题
|
> - 若需求含糊,先复述已知信息并列出关键澄清问题
|
||||||
@@ -20,18 +22,18 @@
|
|||||||
|
|
||||||
### 1.1 技术栈
|
### 1.1 技术栈
|
||||||
|
|
||||||
| 技术 | 版本 | 用途 |
|
| 技术 | 版本 | 用途 |
|
||||||
| ------------------ | -------- | ------------------ |
|
| --------------------- | ------- | ------------------ |
|
||||||
| Vue | ^3.5.13 | 核心框架 |
|
| Vue | ^3.5.13 | 核心框架 |
|
||||||
| Element Plus | ^2.10.1 | UI 组件库 |
|
| Element Plus | ^2.10.1 | UI 组件库 |
|
||||||
| @smallwei/avue | ^3.7.2 | 增强型 CRUD 组件库 |
|
| @smallwei/avue | ^3.7.2 | 增强型 CRUD 组件库 |
|
||||||
| Vue Router | ^4.3.2 | 路由管理 |
|
| Vue Router | ^4.3.2 | 路由管理 |
|
||||||
| Vuex | ^4.1.0 | 状态管理 |
|
| Vuex | ^4.1.0 | 状态管理 |
|
||||||
| Axios | ^1.8.3 | HTTP 客户端 |
|
| Axios | ^1.8.3 | HTTP 客户端 |
|
||||||
| Vite | ^5.4.19 | 构建工具 |
|
| Vite | ^5.4.19 | 构建工具 |
|
||||||
| vue-i18n | ^11.1.3 | 国际化 |
|
| vue-i18n | ^11.1.3 | 国际化 |
|
||||||
| Sass | ^1.85.1 | CSS 预处理器 |
|
| Sass | ^1.85.1 | CSS 预处理器 |
|
||||||
| crypto-js / sm-crypto | - | 加密(AES / SM2) |
|
| crypto-js / sm-crypto | - | 加密(AES / SM2) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -91,27 +93,36 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
|||||||
- 命名约定:列表 `getList(current, size, params)`、详情 `getDetail(id)`、新增 `add(row)`、更新 `update(row)`、删除 `remove(ids)`、树形 `getXxxTree()`
|
- 命名约定:列表 `getList(current, size, params)`、详情 `getDetail(id)`、新增 `add(row)`、更新 `update(row)`、删除 `remove(ids)`、树形 `getXxxTree()`
|
||||||
- 后端微服务前缀:`/blade-system/`、`/blade-resource/`、`/blade-flow/`、`/blade-desk/`、`/blade-log/`、`/blade-develop/`
|
- 后端微服务前缀:`/blade-system/`、`/blade-resource/`、`/blade-flow/`、`/blade-desk/`、`/blade-log/`、`/blade-develop/`
|
||||||
|
|
||||||
### 3.4 认证机制
|
### 3.4 批量导入失败文件下载规范
|
||||||
|
|
||||||
|
- 所有批量导入功能如果存在部分数据无法导入、校验失败或后端处理异常,前端必须支持自动下载后端返回的导入失败 Excel。
|
||||||
|
- 失败 Excel 的字段顺序必须与原导入模板保持一致,并在最后一列追加“导入失败原因”。
|
||||||
|
- 前端上传接口需支持 `blob` 响应;Avue 默认上传无法可靠处理文件流时,应使用 `httpRequest` 或独立 API 方法自定义上传。
|
||||||
|
- 前端需根据响应 `content-type` 或 Blob 类型区分 JSON 成功响应与 Excel 文件流:成功时提示并刷新列表,失败文件流时下载失败明细并提示“部分数据导入失败,已下载失败明细”。
|
||||||
|
- 批量导入失败明细文件命名统一包含业务名称、`导入失败明细` 和时间戳,例如 `货物类型导入失败明细YYYY-MM-DD HH:mm:ss.xlsx`。
|
||||||
|
- 批量导入的前端预校验与后端导入校验必须与新增、编辑表单校验保持一致,包括必填、长度、格式、枚举范围、父子级联关系和金额/日期等业务规则;新增、编辑校验调整时,必须同步更新导入校验。
|
||||||
|
|
||||||
|
### 3.5 认证机制
|
||||||
|
|
||||||
- OAuth2:`Basic` 头传递 `clientId:clientSecret`(Base64 编码)
|
- OAuth2:`Basic` 头传递 `clientId:clientSecret`(Base64 编码)
|
||||||
- Token:请求头 `Blade-Auth: bearer {token}`,支持 AES 加密模式
|
- Token:请求头 `Blade-Auth: bearer {token}`,支持 AES 加密模式
|
||||||
- 存储:`saber3-access-token` / `saber3-refresh-token`(通过 `utils/auth.js` 管理)
|
- 存储:`saber3-access-token` / `saber3-refresh-token`(通过 `utils/auth.js` 管理)
|
||||||
- 401 自动刷新 Token,并发请求排队等待;登录密码使用 SM2 国密加密
|
- 401 自动刷新 Token,并发请求排队等待;登录密码使用 SM2 国密加密
|
||||||
|
|
||||||
### 3.5 路由系统
|
### 3.6 路由系统
|
||||||
|
|
||||||
- 静态路由:`router/page/` + `router/views/`
|
- 静态路由:`router/page/` + `router/views/`
|
||||||
- 动态路由:`avue-router.js` 将后端菜单数据转换为 Vue Router 路由
|
- 动态路由:`avue-router.js` 将后端菜单数据转换为 Vue Router 路由
|
||||||
- 多级路由自动扁平化为二级,支持 keep-alive 跨层级缓存
|
- 多级路由自动扁平化为二级,支持 keep-alive 跨层级缓存
|
||||||
- 外部链接自动转换为 iframe 路由,支持 Token 透传
|
- 外部链接自动转换为 iframe 路由,支持 Token 透传
|
||||||
|
|
||||||
### 3.6 权限控制
|
### 3.7 权限控制
|
||||||
|
|
||||||
- 路由守卫:`permission.js` 控制登录态、锁屏、标签页
|
- 路由守卫:`permission.js` 控制登录态、锁屏、标签页
|
||||||
- 按钮权限:`store.getters.permission`,格式 `{module}_{action}`(如 `dict_add`)
|
- 按钮权限:`store.getters.permission`,格式 `{module}_{action}`(如 `dict_add`)
|
||||||
- 管理员判断:`userInfo.authority.includes('admin')`
|
- 管理员判断:`userInfo.authority.includes('admin')`
|
||||||
|
|
||||||
### 3.7 多租户
|
### 3.8 多租户
|
||||||
|
|
||||||
通过 `website.tenantMode` 控制开关,管理组租户编号 `000000`,后端自动通过请求头传递租户信息。
|
通过 `website.tenantMode` 控制开关,管理组租户编号 `000000`,后端自动通过请求头传递租户信息。
|
||||||
|
|
||||||
@@ -124,6 +135,7 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
|||||||
项目同时支持 **Options API**(主流,绝大多数现有页面)和 **Composition API**(新组件可选)。
|
项目同时支持 **Options API**(主流,绝大多数现有页面)和 **Composition API**(新组件可选)。
|
||||||
|
|
||||||
**选择原则**:
|
**选择原则**:
|
||||||
|
|
||||||
- 修改现有页面:保持该页面原有风格,不混用
|
- 修改现有页面:保持该页面原有风格,不混用
|
||||||
- 新建 CRUD 页面:调用 `avue-design` Skill 生成,或参考现有页面手动编写
|
- 新建 CRUD 页面:调用 `avue-design` Skill 生成,或参考现有页面手动编写
|
||||||
- 新建复杂交互页面:可使用 Composition API + `<script setup>`
|
- 新建复杂交互页面:可使用 Composition API + `<script setup>`
|
||||||
@@ -131,14 +143,14 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
|||||||
|
|
||||||
### 4.2 命名规范
|
### 4.2 命名规范
|
||||||
|
|
||||||
| 类型 | 命名方式 | 示例 |
|
| 类型 | 命名方式 | 示例 |
|
||||||
| -------------- | -------------------------- | ----------------------------- |
|
| ----------------------- | -------------------------- | --------------------------- |
|
||||||
| 页面文件 | kebab-case | `notice.vue` |
|
| 页面文件 | kebab-case | `notice.vue` |
|
||||||
| 组件文件 | kebab-case 目录 + main.vue | `basic-container/main.vue` |
|
| 组件文件 | kebab-case 目录 + main.vue | `basic-container/main.vue` |
|
||||||
| API / Option / 工具文件 | camelCase | `dict.js`、`dictbiz.js` |
|
| API / Option / 工具文件 | camelCase | `dict.js`、`dictbiz.js` |
|
||||||
| 变量 / 函数 | camelCase | `dictValue`、`handleDelete` |
|
| 变量 / 函数 | camelCase | `dictValue`、`handleDelete` |
|
||||||
| Vuex mutations | UPPER_SNAKE | `SET_IS_MENU`、`ADD_TAG` |
|
| Vuex mutations | UPPER_SNAKE | `SET_IS_MENU`、`ADD_TAG` |
|
||||||
| Vuex actions | PascalCase | `FedLogOut`、`RefreshToken` |
|
| Vuex actions | PascalCase | `FedLogOut`、`RefreshToken` |
|
||||||
|
|
||||||
### 4.3 代码格式(Prettier)
|
### 4.3 代码格式(Prettier)
|
||||||
|
|
||||||
@@ -148,6 +160,26 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
|||||||
|
|
||||||
- 全局 SCSS 变量通过 `styles/variables.scss` 定义,Vite 自动注入所有组件
|
- 全局 SCSS 变量通过 `styles/variables.scss` 定义,Vite 自动注入所有组件
|
||||||
- 编写样式优先使用已有变量和 mixin(`styles/mixin.scss`),而非硬编码值
|
- 编写样式优先使用已有变量和 mixin(`styles/mixin.scss`),而非硬编码值
|
||||||
|
- 系统主色调统一使用 `#409eff`,涉及主题变量、Element Plus 主题覆盖、按钮/链接/选中态等品牌色场景均应保持一致
|
||||||
|
- 各页面搜索栏多行展示时,行与行之间的垂直间距统一为 `8px`
|
||||||
|
- 搜索组件 label 宽度统一不小于 `160px`,label 文本不得换行。
|
||||||
|
- 搜索组件内部 padding 统一为上、左右 `12px`,底部 `4px`。
|
||||||
|
- 搜索组件需展示轻量阴影,统一使用 `0 2px 8px rgba(0, 0, 0, 0.04)`,不得被局部卡片去样式规则覆盖。
|
||||||
|
- 表格线条颜色需统一使用 `#EFF1F7`,包括表格外边框、单元格分割线和固定列边线。
|
||||||
|
- 表格偶数行需统一使用 `#FAFAFA` 背景色,固定列单元格必须与对应行背景保持一致。
|
||||||
|
- 表格操作列同时展示“查看、编辑、删除”等三个按钮时,操作列宽度统一不小于 `220px`;操作列最大按钮数量超过 3 个时,操作列宽度统一加宽到不小于 `320px`;最大按钮数量超过 4 个时,仍按 `320px` 保持列宽,并从第 5 个按钮开始换行展示,禁止出现按钮裁切或显示不全。
|
||||||
|
- 表格操作列按钮统一仅展示文字,禁止配置 `icon` / `:icon` 或在操作按钮、操作下拉项中嵌入图标;顶部工具栏按钮不受此条限制。
|
||||||
|
- 纯文字操作入口统一使用 `<el-link>`,禁止使用带 `text` 属性的 `<el-button>`;提交、确认、上传等具有明确命令语义或需要加载状态的按钮可继续使用 `<el-button>`。
|
||||||
|
- 所有“删除”“作废”操作入口必须使用危险色红色文字:表格操作链接统一配置 `type="danger"`,配置化操作必须设置 `type: 'danger'`;顶部批量删除按钮统一使用 `type="danger"`。
|
||||||
|
- 所有表格操作列中的文字链接统一保持 `8px` 间距;操作按钮超过操作列可用宽度时必须自动换行,禁止链接连续粘连或被裁切。Avue 自定义 `#menu` 插槽和手写 `el-table-column` 操作列均适用。
|
||||||
|
- `.avue-crud__header` 顶部间距统一为 `12px`。
|
||||||
|
- 分页组件整体靠右展示,必须展示接口返回的数据总条数,`X条/页` 的页容量选择器必须放在总条数右侧。
|
||||||
|
- `.basic-container__card` 的直接子级 `.el-card__body` 内边距统一去除,保持 `padding: 0`
|
||||||
|
- 所有 CRUD 页面搜索栏需与下方表格拆分为独立区域,两者垂直间距统一为 `8px`。
|
||||||
|
- 所有 CRUD 页面搜索栏操作按钮(搜索、清空等)需在搜索表单下方独占一行并放置在最右侧;搜索条件超过一行时必须显示“展开/折叠”,默认折叠且仅展示一行搜索条件;一行布局默认展示 4 个筛选项,Avue 配置统一使用 `searchIndex: 4`。
|
||||||
|
- 所有 CRUD 页面中新增、批量导入等顶部按钮组行不需要背景色,应保持透明背景。
|
||||||
|
- 所有 CRUD 页面顶部工具栏的“新增/新建”主操作按钮统一使用蓝底白字的实心主按钮(`type="primary"` 且不使用 `plain`),视觉标准与“新建运单”一致;批量导入、导出等辅助工具按钮仍使用描边样式。
|
||||||
|
- 所有 `.basic-container__card` 内的 Avue 内层 `el-card` 需去卡片化,仅保留外层容器卡片样式,禁止出现二级卡片边框、阴影或额外内边距。
|
||||||
|
|
||||||
### 4.5 全局注册组件
|
### 4.5 全局注册组件
|
||||||
|
|
||||||
@@ -165,8 +197,15 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
|||||||
|
|
||||||
### 4.8 表单布局规范
|
### 4.8 表单布局规范
|
||||||
|
|
||||||
- 新增/编辑弹窗中的表单项,若 `label` 文案超过 4 个字,`label` 必须单独占一行展示,表单控件另起一行,避免长标签挤压输入控件或换行错位。
|
- 新增、编辑弹窗中的表单 `label` 默认必须位于控件左侧,Avue 配置统一使用 `labelPosition: 'right'` 与 `labelWidth: 'auto'`,Element Plus 表单统一使用 `label-position="right"` 与 `label-width="auto"`。
|
||||||
- 使用 Avue 配置、Element Plus 表单或自定义弹窗表单时均需遵守该规则;可通过 `label-position="top"`、自定义 `formslot`、局部样式或单项布局调整实现。
|
- 新增、编辑弹窗中的表单 `label` 宽度必须以当前视觉列内最长 `label` 为准;多列表单需按列分别计算 label 宽度,禁止用整张表单的最长 label 统一挤压所有列。
|
||||||
|
- 所有新增、编辑弹窗中的表单 `label` 文本必须右对齐,确保同一列内控件起始位置一致。
|
||||||
|
- 新增、编辑弹窗标题左侧必须展示 `4px` 宽的主色竖条,竖条与标题文本间距统一为 `8px`。
|
||||||
|
- 复杂布局弹窗中的自定义分区标题、明细表格标题、步骤区标题等承担弹窗内容标题作用时,也必须沿用 `4px` 主色竖条与 `8px` 标题间距;可使用全局 `.dialog-section-title` 或等效局部样式实现。
|
||||||
|
- 新增、编辑弹窗中的表单项上下间距统一为 `16px`,弹窗内表单布局不得叠加 `.el-col` 与 `.el-form-item` 的额外下边距。
|
||||||
|
- 所有新增、编辑弹窗中的备注字段(如果有)必须单独占一整行展示,禁止与其他字段并列;Avue 配置可使用 `span: 24` 或等效布局实现。
|
||||||
|
- 新增、编辑弹窗底部按钮顺序统一为“取消、提交”,取消按钮在左,提交按钮在右。
|
||||||
|
- 使用 Avue 配置、Element Plus 表单或自定义弹窗表单时均需遵守该规则;若局部组件因特殊结构无法自动对齐,应通过 `src/utils/dialog-form-label.js` 的全局增强、局部样式或 `formslot` 保证 `label` 左侧展示、按列统一宽度、文本右对齐。
|
||||||
|
|
||||||
### 4.9 审计字段展示规范
|
### 4.9 审计字段展示规范
|
||||||
|
|
||||||
@@ -175,6 +214,24 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
|||||||
- “更新人”列统一使用 `prop: 'updateUserName'`,并通过 `src/utils/audit.js` 中的 `formatUpdateUserName(row)` 做兼容兜底。
|
- “更新人”列统一使用 `prop: 'updateUserName'`,并通过 `src/utils/audit.js` 中的 `formatUpdateUserName(row)` 做兼容兜底。
|
||||||
- 表单提交仍保留后端审计字段机制,前端不得手动提交或覆盖 `createUser`、`updateUser`。
|
- 表单提交仍保留后端审计字段机制,前端不得手动提交或覆盖 `createUser`、`updateUser`。
|
||||||
|
|
||||||
|
### 4.10 表格排序规范
|
||||||
|
|
||||||
|
- 表格的排序默认按照创建时间降序;分页列表应由后端默认按 `create_time DESC` 返回,前端不得在无明确业务要求时覆盖为其它默认排序。
|
||||||
|
|
||||||
|
### 4.11 搜索顺序规范
|
||||||
|
|
||||||
|
- 当用户已明确列出筛选字段时,搜索栏必须严格按照用户给出的字段清单和顺序展示;不得自行新增、保留、推断或迁移未列出的筛选项。
|
||||||
|
- 新增/编辑表单字段、表格列字段和搜索筛选字段必须分开理解;除非用户明确要求同步,禁止把表单字段或表格字段擅自加入搜索栏。
|
||||||
|
- Avue 搜索栏的字段展示顺序按 `searchOrder` **倒序** 渲染,配置时数值越大越靠前。
|
||||||
|
|
||||||
|
### 4.12 日期区间搜索规范
|
||||||
|
|
||||||
|
- 当同一业务维度的筛选条件是开始/结束日期或时间,并且接口按区间查询时,前端搜索栏必须合并为一个 `searchRange: true` 的区间字段展示。
|
||||||
|
- 区间字段在页面层统一归一化为后端所需的起止参数,禁止同时保留两个独立的“开始/结束”搜索项。
|
||||||
|
- 若用户已明确要求展示区间样式,必须严格按区间控件实现,不得退回为两个普通日期控件。
|
||||||
|
- 修改搜索项顺序时必须同步调整 `searchOrder`,不要依赖 `column` 数组前后位置来控制搜索栏顺序。
|
||||||
|
- `searchIndex: 4` 仅控制首屏展示数量,不影响 `searchOrder` 的优先级。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. 新功能开发流程
|
## 5. 新功能开发流程
|
||||||
@@ -225,17 +282,17 @@ pnpm run serve # 预览构建产物
|
|||||||
|
|
||||||
项目采用 **Gitmoji** 风格,中文描述:
|
项目采用 **Gitmoji** 风格,中文描述:
|
||||||
|
|
||||||
| Emoji | 代码 | 场景 |
|
| Emoji | 代码 | 场景 |
|
||||||
| ---------- | ------------ | ---------------- |
|
| ---------- | ------------ | ------------------ |
|
||||||
| :sparkles: | `:sparkles:` | 新增功能、优化增强 |
|
| :sparkles: | `:sparkles:` | 新增功能、优化增强 |
|
||||||
| :bug: | `:bug:` | 修复 Bug |
|
| :bug: | `:bug:` | 修复 Bug |
|
||||||
| :zap: | `:zap:` | 性能优化、问题修复 |
|
| :zap: | `:zap:` | 性能优化、问题修复 |
|
||||||
| :tada: | `:tada:` | 重大版本发布 |
|
| :tada: | `:tada:` | 重大版本发布 |
|
||||||
| :lipstick: | `:lipstick:` | 样式调整 |
|
| :lipstick: | `:lipstick:` | 样式调整 |
|
||||||
| :recycle: | `:recycle:` | 代码重构 |
|
| :recycle: | `:recycle:` | 代码重构 |
|
||||||
| :wrench: | `:wrench:` | 配置修改 |
|
| :wrench: | `:wrench:` | 配置修改 |
|
||||||
| :memo: | `:memo:` | 文档更新 |
|
| :memo: | `:memo:` | 文档更新 |
|
||||||
| :fire: | `:fire:` | 删除代码/文件 |
|
| :fire: | `:fire:` | 删除代码/文件 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,225 @@
|
|||||||
|
# AI 辅助编程规范与实战示例——以「货物类型模块」为例
|
||||||
|
|
||||||
|
> 文档目的:向业主说明我们团队如何用 AI 协作完成前端开发,包含协作流程、工程规范、以及一个可直接对照代码的真实模块示例,便于业主了解我们的工作方式、质量保障手段与可复用的经验。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、背景:我们为什么用 AI 编程
|
||||||
|
|
||||||
|
本项目(Saber3 企业级后台)是一个模块多、表单多、校验规则多、样式规范严苛的大型 Vue 3 工程。传统人工开发存在两个痛点:
|
||||||
|
|
||||||
|
1. **重复劳动多**:每个 CRUD 模块的列表、搜索、新增、编辑、导入、导出结构高度相似,但样式细节(表格线色、偶数行底色、操作列宽度、弹窗竖条等)又极易被写错。
|
||||||
|
2. **规范易漂移**:数十个模块由不同人开发,若没有统一约束,代码风格、校验口径、审计字段展示会逐渐不一致。
|
||||||
|
|
||||||
|
我们引入 AI 编程的核心思路不是"让 AI 自由发挥",而是**把人类沉淀的工程规范喂给 AI,让 AI 在规范的边界内批量、稳定地产出代码,再由人工做业务与视觉复核**。这样既提效,又保住质量底线。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、我们的 AI 协作流程(总览)
|
||||||
|
|
||||||
|
```
|
||||||
|
需求澄清 ──> 需求卡(单一事实来源)──> AGENTS.md(工程规范)──> AI 生成代码
|
||||||
|
│ │
|
||||||
|
└──────────────── 人工复核 / 验收 ──────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
四个关键阶段:
|
||||||
|
|
||||||
|
| 阶段 | 产物 | 负责人 | 说明 |
|
||||||
|
| ------- | --------------------------- | ------- | -------------------------------- |
|
||||||
|
| 1. 需求澄清 | 模块需求卡(.md) | 产品/前端 | 用 `模块需求卡生成规范.md` 统一格式,拆成 REQ 编号项 |
|
||||||
|
| 2. 规范约束 | AGENTS.md | 架构/前端 | 把技术栈、风格、样式、校验、审计等写成"对 AI 的强制条款" |
|
||||||
|
| 3. 代码生成 | view / option / api / utils | AI + 前端 | AI 按 AGENTS.md 生成,前端实时纠偏 |
|
||||||
|
| 4. 复核验收 | 可运行模块 | 前端/测试 | 校验业务规则、视觉规范、边界异常 |
|
||||||
|
|
||||||
|
**核心原则:需求卡和 AGENTS.md 是 AI 与人对齐的"单一事实来源",不靠口头交代。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、工程规范如何约束 AI(AGENTS.md 核心条款节选)
|
||||||
|
|
||||||
|
我们维护了一份 `AGENTS.md`,作为 AI 每次动手前的"必读规范"。与本示例强相关的条款有:
|
||||||
|
|
||||||
|
### 3.1 文件同构结构
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── views/base/cargo-type.vue # 页面(模板 + 交互逻辑)
|
||||||
|
├── option/base/cargo-type.js # Avue 表格/表单配置(与 views 同构路径)
|
||||||
|
└── api/base/cargo-type.js # 接口层(按业务模块组织)
|
||||||
|
```
|
||||||
|
|
||||||
|
> 规范要点:Avue 配置独立存放于 `src/option/`,与 `views` 和 `api` 保持同构的模块路径映射。AI 生成时不会把配置写死在页面里。
|
||||||
|
|
||||||
|
### 3.2 样式规范(极易被写错,故用规范强制)
|
||||||
|
|
||||||
|
- 表格线条颜色统一 `#EFF1F7`;偶数行背景 `#FAFAFA`,固定列须与行背景一致;
|
||||||
|
- 操作列同时展示"查看/编辑/删除"时宽度不小于 `220px`;
|
||||||
|
- `.avue-crud__header` 顶部间距 `12px`;
|
||||||
|
- `.basic-container__card` 内层 `el-card` 去卡片化(`padding: 0`);
|
||||||
|
- 主色统一 `#409eff`。
|
||||||
|
|
||||||
|
### 3.3 表单校验规范
|
||||||
|
|
||||||
|
- 金额类非负、经纬度范围、备注类 ≤200 字(前端阻止提交并提示);
|
||||||
|
- 新增、导入、编辑三处的校验口径必须一致;
|
||||||
|
- 审计字段(创建人/更新人)**禁止直接展示用户 ID**,须绑定后端返回的姓名字段(`createUserName`)。
|
||||||
|
|
||||||
|
### 3.4 批量导入失败明细规范(本项目重点)
|
||||||
|
|
||||||
|
- 部分数据导入失败时,前端必须自动下载后端返回的失败 Excel;
|
||||||
|
- 失败 Excel 字段顺序须与原模板一致,最后追加"导入失败原因"列;
|
||||||
|
- 前端需根据 `content-type` 区分"JSON 成功响应"与"Excel 文件流";
|
||||||
|
- 失败明细文件命名含业务名、导入失败明细、时间戳。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、实例拆解:货物类型模块
|
||||||
|
|
||||||
|
### 4.1 模块能力一览
|
||||||
|
|
||||||
|
货物类型是一个**两级层级**的主数据模块:一级货物类型(2 位编码)+ 二级货物类型(4 位编码,前 2 位须与上级一致)。功能包括:列表、条件查询、新增、编辑、查看、删除、批量删除、**批量导入 + 模板下载 + 失败明细加工**、批量导出。
|
||||||
|
|
||||||
|
### 4.2 文件同构结构(真实存在)
|
||||||
|
|
||||||
|
```
|
||||||
|
src/views/base/cargo-type.vue # 589 行,页面 + 交互
|
||||||
|
src/option/base/cargo-type.js # 189 行,Avue 列配置 + 校验器
|
||||||
|
src/api/base/cargo-type.js # 74 行,7 个接口方法
|
||||||
|
src/utils/import-excel.js # 109 行,通用导入弹窗(被本模块复用)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 AI 按规范落地的关键代码点
|
||||||
|
|
||||||
|
下面每一条都对应前述规范条款,可直接对照源码。
|
||||||
|
|
||||||
|
**① 三级文件分离(对应 3.1)**
|
||||||
|
|
||||||
|
页面只负责交互,配置与接口独立:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// src/views/base/cargo-type.vue
|
||||||
|
import { getList, getNextCode, getParentOptions, remove, submit } from '@/api/base/cargo-type';
|
||||||
|
import { getCargoTypeOption } from '@/option/base/cargo-type';
|
||||||
|
```
|
||||||
|
|
||||||
|
**② 表单校验三处一致(对应 3.3)**
|
||||||
|
|
||||||
|
前端表单(option)与 JS 内 `validateRow` 共用同一套规则口径,避免"表单能过、导入却崩":
|
||||||
|
|
||||||
|
```js
|
||||||
|
// src/views/base/cargo-type.vue —— 提交前的统一校验
|
||||||
|
validateRow(row) {
|
||||||
|
const pattern = row.typeLevel === 1 ? /^\d{2}$/ : /^\d{4}$/;
|
||||||
|
if (!pattern.test(row.cargoCode)) { this.$message.warning('货物类型编码格式不正确'); return false; }
|
||||||
|
if (row.typeLevel === 2 && !row.cargoCode.startsWith(row.parentCargoCode)) {
|
||||||
|
this.$message.warning('二级编码前2位必须与上级货物类型编码一致'); return false;
|
||||||
|
}
|
||||||
|
if (row.remark.length > 200) { this.$message.warning('备注不能超过200字'); return false; }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
// src/option/base/cargo-type.js —— 弹窗表单的同源校验器
|
||||||
|
{ validator: (rule, value, callback) => {
|
||||||
|
const form = ctx.form || {};
|
||||||
|
const pattern = form.typeLevel === 1 ? /^\d{2}$/ : /^\d{4}$/;
|
||||||
|
// 同样的规则:格式 + 二级前缀一致性
|
||||||
|
}, trigger: 'blur' }
|
||||||
|
```
|
||||||
|
|
||||||
|
**③ 审计字段用姓名不用 ID(对应 3.3)**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
|
||||||
|
<template #createUserName="{ row }">
|
||||||
|
{{ row.createUserName || row.createUser || '' }}
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
列表直接绑定 `createUserName`,避免出现一串无意义的用户 ID。
|
||||||
|
|
||||||
|
**④ 导入失败明细加工(对应 3.4,本项目 AI 协作的亮点)**
|
||||||
|
|
||||||
|
后端只返回原始失败原因文本,AI 被要求生成一个**可独立测试的小函数** `decorateCargoTypeFailDetail`,把原因按 A./B./C. 编号、追加规则说明、仅"导入失败原因"列标红、自动加宽列:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// src/views/base/cargo-type.vue
|
||||||
|
export const decorateCargoTypeFailDetail = async workbook => {
|
||||||
|
// 1. 映射表头列号
|
||||||
|
// 2. 清理非原因列的多余红色字体
|
||||||
|
// 3. 把每行原因拆条、编号、拼接规则说明
|
||||||
|
// 4. 仅操作"导入失败原因"列样式(黑字、无填充、自动换行)
|
||||||
|
// 5. 按 CARGO_TYPE_FAIL_COL_WIDTH 加宽各列
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
该函数通过通用导入工具 `import-excel.js` 的 `failDetailDecorator` 钩子注入,做到**模块零耦合复用**:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 复用通用导入弹窗,仅传入本模块的加工函数
|
||||||
|
handleImport() {
|
||||||
|
openImportDialog(this, '货物类型', undefined, {
|
||||||
|
failDetailDecorator: decorateCargoTypeFailDetail,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**⑤ 样式规范落地(对应 3.2)**
|
||||||
|
|
||||||
|
```scss
|
||||||
|
/* src/views/base/cargo-type.vue <style> */
|
||||||
|
:deep(.el-table) {
|
||||||
|
--el-table-border-color: #eff1f7; /* 表格线条统一色 */
|
||||||
|
}
|
||||||
|
:deep(.el-table__body tr:nth-child(even) > td.el-table__cell) {
|
||||||
|
background: #fafafa; /* 偶数行底色,固定列同色 */
|
||||||
|
}
|
||||||
|
:deep(.basic-container__card) > .el-card__body { padding: 0; } /* 去卡片化 */
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 人机协作中,AI 不擅长、需要人工兜底的部分
|
||||||
|
|
||||||
|
诚实地说,AI 不是万能的。本模块在协作中由人工重点把控的有:
|
||||||
|
|
||||||
|
1. **业务规则澄清**:一级/二级层级关系、编码前缀约束这类"业务语义",需要产品/前端先讲清,AI 才能写出正确校验。
|
||||||
|
2. **视觉细节微调**:表格行高、表头高度、间距的最终眼检,依赖人工在浏览器里确认。
|
||||||
|
3. **前后端字段契约**:`CargoTypeVO` 是否返回 `createUserName`、失败 Excel 的表头文案,必须与后端对齐,AI 无法替你确认。
|
||||||
|
4. **破坏性操作确认**:删除/批量删除的二次确认文案与风险,由人工定稿。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、经验沉淀(可复用的 5 条)
|
||||||
|
|
||||||
|
1. **把规范写成 `AGENTS.md`,比口头交代可靠 10 倍。** AI 每次都会读,规范不会因人离职而丢失。
|
||||||
|
2. **需求卡是 AI 与人对齐的单一事实来源。** 用统一格式(REQ 编号 + 功能点/异常边界/反例)避免"我以为你要的是……"。
|
||||||
|
3. **把重复能力抽成 utils。** 如 `import-excel.js`,新模块导入直接复用,AI 只需写"本模块特有的加工函数"。
|
||||||
|
4. **校验三处一致**:前端表单、前端导入、后端校验口径对齐,且一处改、三处同步改。
|
||||||
|
5. **让 AI 生成"小而纯"的函数**(如失败明细加工函数),比让它改一整个 600 行页面更可控、更易测、更易复核。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、给业主的价值与建议
|
||||||
|
|
||||||
|
| 维度 | 传统方式 | 我们的 AI 协作方式 |
|
||||||
|
| ------- | --------- | ---------------- |
|
||||||
|
| 单模块开发周期 | 长,易返工 | 短,规范内一次成型 |
|
||||||
|
| 多模块一致性 | 易漂移 | AGENTS.md 强制统一 |
|
||||||
|
| 规范传承 | 靠老人带 | 沉淀为文件,新人/AI 即取即用 |
|
||||||
|
| 质量风险 | 校验、审计字段易漏 | 规范条款逐项约束 |
|
||||||
|
|
||||||
|
**建议**:业主要求交付的不只是代码,更是"可持续维护的规范资产"。我们交付的每个模块背后都有需求卡 + AGENTS.md + 复用 utils 三件套,后续无论是换人维护还是扩展新模块,成本都显著更低。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*附:本示例涉及的全部源码路径*
|
||||||
|
|
||||||
|
- `src/views/base/cargo-type.vue`
|
||||||
|
- `src/option/base/cargo-type.js`
|
||||||
|
- `src/api/base/cargo-type.js`
|
||||||
|
- `src/utils/import-excel.js`
|
||||||
|
- `AGENTS.md`(工程规范总纲)
|
||||||
|
- `模块需求卡生成规范.md`(需求卡格式规范)
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# 风险处置后端接口契约
|
||||||
|
|
||||||
|
> 当前仓库为前端工程,无 Java 后端源码。本文件提供 BladeX 后端落库与接口实现契约,路径需放入 `blade-transport` 服务。
|
||||||
|
|
||||||
|
## Controller
|
||||||
|
|
||||||
|
```java
|
||||||
|
@RestController
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RequestMapping("/risk-disposal")
|
||||||
|
@Api(value = "风险处置", tags = "风险处置")
|
||||||
|
public class RiskDisposalController {
|
||||||
|
|
||||||
|
private final IRiskDisposalService riskDisposalService;
|
||||||
|
|
||||||
|
@GetMapping("/list")
|
||||||
|
public R<IPage<RiskDisposalVO>> list(RiskDisposalQuery query, Query page) {
|
||||||
|
IPage<RiskDisposalVO> pages = riskDisposalService.selectRiskDisposalPage(Condition.getPage(page), query);
|
||||||
|
return R.data(pages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/detail")
|
||||||
|
public R<RiskDisposalVO> detail(Long id) {
|
||||||
|
return R.data(riskDisposalService.detail(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/dispose")
|
||||||
|
public R<Boolean> dispose(@Valid @RequestBody RiskDisposalDTO dto) {
|
||||||
|
return R.status(riskDisposalService.dispose(dto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/batch-dispose")
|
||||||
|
public R<Boolean> batchDispose(@Valid @RequestBody RiskDisposalBatchDTO dto) {
|
||||||
|
return R.status(riskDisposalService.batchDispose(dto));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 字段约定
|
||||||
|
|
||||||
|
- `riskLevel`: `low` 低,`medium` 中,`high` 高
|
||||||
|
- `disposalStatus`: `pending` 待处理,`processed` 已处理,`ignored` 已忽略
|
||||||
|
- `disposalMethod`: `process` 处理,`ignore` 忽略
|
||||||
|
- 列表默认按 `create_time DESC` 返回
|
||||||
|
- `riskNo` 由后端生成:`RK + yyyyMMdd + 3位流水号`
|
||||||
|
|
||||||
|
## 接口
|
||||||
|
|
||||||
|
- `GET /blade-transport/risk-disposal/list`
|
||||||
|
- `GET /blade-transport/risk-disposal/detail?id=`
|
||||||
|
- `POST /blade-transport/risk-disposal/dispose`
|
||||||
|
- `POST /blade-transport/risk-disposal/batch-dispose`
|
||||||
|
|
||||||
|
## DTO
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Data
|
||||||
|
public class RiskDisposalDTO {
|
||||||
|
@NotNull(message = "风险ID不能为空")
|
||||||
|
private Long id;
|
||||||
|
@NotBlank(message = "请选择处理方式")
|
||||||
|
private String disposalMethod;
|
||||||
|
@Length(max = 500, message = "处置说明不能超过500个字符")
|
||||||
|
private String disposalRemark;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class RiskDisposalBatchDTO {
|
||||||
|
@NotEmpty(message = "风险ID不能为空")
|
||||||
|
private List<Long> ids;
|
||||||
|
@NotBlank(message = "请选择处理方式")
|
||||||
|
private String disposalMethod;
|
||||||
|
@Length(max = 500, message = "处置说明不能超过500个字符")
|
||||||
|
private String disposalRemark;
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# 在途追踪后端接口契约
|
||||||
|
|
||||||
|
## 接口
|
||||||
|
|
||||||
|
- `GET /blade-transport/track-tracking/map`
|
||||||
|
- `GET /blade-transport/track-tracking/ship`
|
||||||
|
|
||||||
|
## 建议返回
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"siteName": "XX园区A门",
|
||||||
|
"address": "南宁市邕宁区龙岗大道21号",
|
||||||
|
"coordinates": "108.348610;22.721864",
|
||||||
|
"records": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 说明
|
||||||
|
|
||||||
|
- `map` 用于轨迹查询
|
||||||
|
- `ship` 用于船舶查询
|
||||||
|
- 当前前端先做高德地图占位,后端可先只返回基础信息和轨迹点数组
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
-- 风险处置模块初始化脚本
|
||||||
|
-- 父菜单:在途管理
|
||||||
|
|
||||||
|
INSERT INTO `blade_menu` (
|
||||||
|
`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`,
|
||||||
|
`remark`, `tenant_id`, `create_user`, `create_dept`, `create_time`, `update_user`, `update_time`, `status`,
|
||||||
|
`is_deleted`
|
||||||
|
) VALUES (
|
||||||
|
'1710000000000000001', 0, 'transit_manage', '在途管理', 'transit_manage', '/transit', 'el-icon-truck',
|
||||||
|
20, 1, 1, 1, '在途管理父菜单', '000000', 1, 1, NOW(), 1, NOW(), 1, 0
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `blade_menu` (
|
||||||
|
`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`,
|
||||||
|
`remark`, `tenant_id`, `create_user`, `create_dept`, `create_time`, `update_user`, `update_time`, `status`,
|
||||||
|
`is_deleted`
|
||||||
|
) VALUES (
|
||||||
|
'1710000000000000002', '1710000000000000001', 'risk_disposal', '风险处置', 'risk_disposal',
|
||||||
|
'/transit/risk-disposal', 'el-icon-warning-outline', 20, 1, 1, 1, '风险处置页面',
|
||||||
|
'000000', 1, 1, NOW(), 1, NOW(), 1, 0
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `blade_menu` (
|
||||||
|
`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`,
|
||||||
|
`remark`, `tenant_id`, `create_user`, `create_dept`, `create_time`, `update_user`, `update_time`, `status`,
|
||||||
|
`is_deleted`
|
||||||
|
) VALUES
|
||||||
|
('1710000000000000003', '1710000000000000002', 'risk_disposal_query', '查询', 'risk_disposal_query', '', '', 10, 2, 1, 1, '风险处置查询权限', '000000', 1, 1, NOW(), 1, NOW(), 1, 0),
|
||||||
|
('1710000000000000004', '1710000000000000002', 'risk_disposal_process', '处理', 'risk_disposal_process', '', '', 20, 2, 1, 1, '风险处置处理权限', '000000', 1, 1, NOW(), 1, NOW(), 1, 0),
|
||||||
|
('1710000000000000005', '1710000000000000002', 'risk_disposal_ignore', '忽略', 'risk_disposal_ignore', '', '', 30, 2, 1, 1, '风险处置忽略权限', '000000', 1, 1, NOW(), 1, NOW(), 1, 0),
|
||||||
|
('1710000000000000006', '1710000000000000002', 'risk_disposal_batch_process', '批量处理', 'risk_disposal_batch_process', '', '', 40, 2, 1, 1, '风险处置批量处理权限', '000000', 1, 1, NOW(), 1, NOW(), 1, 0),
|
||||||
|
('1710000000000000007', '1710000000000000002', 'risk_disposal_batch_ignore', '批量忽略', 'risk_disposal_batch_ignore', '', '', 50, 2, 1, 1, '风险处置批量忽略权限', '000000', 1, 1, NOW(), 1, NOW(), 1, 0),
|
||||||
|
('1710000000000000008', '1710000000000000002', 'risk_disposal_view', '查看', 'risk_disposal_view', '', '', 60, 2, 1, 1, '风险处置查看权限', '000000', 1, 1, NOW(), 1, NOW(), 1, 0);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `tms_risk_disposal` (
|
||||||
|
`id` bigint NOT NULL COMMENT '主键',
|
||||||
|
`tenant_id` varchar(12) DEFAULT '000000' COMMENT '租户ID',
|
||||||
|
`risk_no` varchar(30) NOT NULL COMMENT '风险编号',
|
||||||
|
`rule_name` varchar(50) NOT NULL COMMENT '规则名称',
|
||||||
|
`waybill_id` bigint DEFAULT NULL COMMENT '运单ID',
|
||||||
|
`waybill_no` varchar(30) DEFAULT NULL COMMENT '运单号',
|
||||||
|
`project_id` bigint DEFAULT NULL COMMENT '项目ID',
|
||||||
|
`project_name` varchar(50) DEFAULT NULL COMMENT '项目名称',
|
||||||
|
`risk_level` varchar(20) NOT NULL COMMENT '风险等级:low/medium/high',
|
||||||
|
`transport_type` varchar(20) DEFAULT NULL COMMENT '运输类型',
|
||||||
|
`risk_description` varchar(500) DEFAULT NULL COMMENT '风险描述',
|
||||||
|
`trigger_time` datetime DEFAULT NULL COMMENT '触发时间',
|
||||||
|
`disposal_status` varchar(20) NOT NULL DEFAULT 'pending' COMMENT '处理状态:pending/processed/ignored',
|
||||||
|
`disposal_method` varchar(20) DEFAULT NULL COMMENT '处理方式:process/ignore',
|
||||||
|
`disposal_remark` varchar(500) DEFAULT NULL COMMENT '处置说明',
|
||||||
|
`dispose_user` bigint DEFAULT NULL COMMENT '处置人',
|
||||||
|
`dispose_user_name` varchar(50) DEFAULT NULL COMMENT '处置人名称',
|
||||||
|
`dispose_time` datetime DEFAULT NULL COMMENT '处置时间',
|
||||||
|
`create_user` bigint DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_user` bigint DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`status` int DEFAULT 1 COMMENT '状态',
|
||||||
|
`is_deleted` int DEFAULT 0 COMMENT '是否已删除',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_tms_risk_disposal_no` (`risk_no`),
|
||||||
|
KEY `idx_tms_risk_disposal_status` (`disposal_status`),
|
||||||
|
KEY `idx_tms_risk_disposal_level` (`risk_level`),
|
||||||
|
KEY `idx_tms_risk_disposal_waybill` (`waybill_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='风险处置';
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
-- 在途追踪模块初始化脚本
|
||||||
|
|
||||||
|
INSERT INTO `blade_menu` (
|
||||||
|
`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`,
|
||||||
|
`remark`, `tenant_id`, `create_user`, `create_dept`, `create_time`, `update_user`, `update_time`, `status`,
|
||||||
|
`is_deleted`
|
||||||
|
) VALUES (
|
||||||
|
'1720000000000000001', 0, 'transit_manage', '在途管理', 'transit_manage', '/transit', 'el-icon-truck',
|
||||||
|
20, 1, 1, 1, '在途管理父菜单', '000000', 1, 1, NOW(), 1, NOW(), 1, 0
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `blade_menu` (
|
||||||
|
`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`,
|
||||||
|
`remark`, `tenant_id`, `create_user`, `create_dept`, `create_time`, `update_user`, `update_time`, `status`,
|
||||||
|
`is_deleted`
|
||||||
|
) VALUES (
|
||||||
|
'1720000000000000002', '1720000000000000001', 'track_tracking', '在途追踪', 'track_tracking',
|
||||||
|
'/transit/track-tracking', 'el-icon-position', 10, 1, 1, 1, '在途追踪页面',
|
||||||
|
'000000', 1, 1, NOW(), 1, NOW(), 1, 0
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `blade_menu` (
|
||||||
|
`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`,
|
||||||
|
`remark`, `tenant_id`, `create_user`, `create_dept`, `create_time`, `update_user`, `update_time`, `status`,
|
||||||
|
`is_deleted`
|
||||||
|
) VALUES
|
||||||
|
('1720000000000000003', '1720000000000000002', 'track_tracking_query', '查询', 'track_tracking_query', '', '', 10, 2, 1, 1, '在途追踪查询权限', '000000', 1, 1, NOW(), 1, NOW(), 1, 0),
|
||||||
|
('1720000000000000004', '1720000000000000002', 'track_tracking_ship_query', '船舶查询', 'track_tracking_ship_query', '', '', 20, 2, 1, 1, '在途追踪船舶查询权限', '000000', 1, 1, NOW(), 1, NOW(), 1, 0),
|
||||||
|
('1720000000000000005', '1720000000000000002', 'track_tracking_refresh', '刷新', 'track_tracking_refresh', '', '', 30, 2, 1, 1, '在途追踪刷新权限', '000000', 1, 1, NOW(), 1, NOW(), 1, 0);
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>图标悬停圆形背景效果</title>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
background: #f5f7fa;
|
||||||
|
padding: 40px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
h1 { font-size: 18px; margin: 0 0 24px; }
|
||||||
|
h2 { font-size: 14px; margin: 32px 0 12px; color: #666; }
|
||||||
|
.card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
max-width: 600px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0,0,0,0.06);
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 核心样式:图标按钮悬停圆形背景 */
|
||||||
|
.icon-hover-btn {
|
||||||
|
position: relative;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: transparent;
|
||||||
|
color: #333;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
.icon-hover-btn::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #f2f3f5;
|
||||||
|
transform: scale(0.6);
|
||||||
|
opacity: 0;
|
||||||
|
transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
.icon-hover-btn:hover::before,
|
||||||
|
.icon-hover-btn:focus-visible::before {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.icon-hover-btn svg {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-block {
|
||||||
|
background: #f8f9fa;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.6;
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: pre;
|
||||||
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>图标悬停圆形背景效果</h1>
|
||||||
|
<div class="card">
|
||||||
|
<p>把鼠标移到下面的 T 恤图标上,就会出现类似“铃铛”示例的浅灰圆形背景。</p>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<button class="icon-hover-btn" aria-label="T恤">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M7 3l3 3h4l3-3 4 4-3 2v11a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V9L3 7l4-4z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<span style="color:#999;font-size:13px;">↑ hover 查看效果</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>CSS 核心代码(可复制)</h2>
|
||||||
|
<pre class="code-block">.icon-hover-btn {
|
||||||
|
position: relative;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: transparent;
|
||||||
|
color: #333;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
.icon-hover-btn::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #f2f3f5;
|
||||||
|
transform: scale(0.6);
|
||||||
|
opacity: 0;
|
||||||
|
transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1),
|
||||||
|
opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
.icon-hover-btn:hover::before,
|
||||||
|
.icon-hover-btn:focus-visible::before {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.icon-hover-btn svg {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
}</pre>
|
||||||
|
|
||||||
|
<h2>Element Plus 用法示例</h2>
|
||||||
|
<pre class="code-block"><el-button class="icon-hover-btn" link>
|
||||||
|
<el-icon size="22"><Upload /></el-icon>
|
||||||
|
</el-button></pre>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -13,14 +13,14 @@
|
|||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||||
<meta name="format-detection" content="telephone=no" />
|
<meta name="format-detection" content="telephone=no" />
|
||||||
<link rel="icon" href="/favicon.png" />
|
|
||||||
<link rel="stylesheet" href="/iconfont/index.css" />
|
<link rel="stylesheet" href="/iconfont/index.css" />
|
||||||
<link rel="stylesheet" href="/iconfont/avue/iconfont.css" />
|
<link rel="stylesheet" href="/iconfont/avue/iconfont.css" />
|
||||||
<link rel="stylesheet" href="/iconfont/saber/iconfont.css" />
|
<link rel="stylesheet" href="/iconfont/saber/iconfont.css" />
|
||||||
<link rel="stylesheet" href="/iconfont/common/iconfont.css" />
|
<link rel="stylesheet" href="/iconfont/common/iconfont.css" />
|
||||||
<link rel="stylesheet" href="/css/loading.css" />
|
<link rel="stylesheet" href="/css/loading.css" />
|
||||||
<link rel="stylesheet" href="/css/saber.css" />
|
<link rel="stylesheet" href="/css/saber.css" />
|
||||||
<title>TMS物流运输管理系统</title>
|
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||||
|
<title>运输板块数字化管理平台</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -9,36 +9,42 @@
|
|||||||
"serve": "vite preview --host"
|
"serve": "vite preview --host"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"element-plus": "^2.14.2",
|
|
||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"@smallwei/avue": "~3.9.2",
|
"@open-file-viewer/core": "^0.1.27",
|
||||||
"avue-plugin-ueditor": "^1.0.4",
|
"@open-file-viewer/vue": "^0.1.27",
|
||||||
"@saber/nf-design-base-elp": "^1.3.0",
|
"@saber/nf-design-base-elp": "^1.3.0",
|
||||||
"@saber/nf-form-design-elp": "^1.6.1",
|
"@saber/nf-form-design-elp": "^1.6.1",
|
||||||
"@saber/nf-form-elp": "^1.6.3",
|
"@saber/nf-form-elp": "^1.6.3",
|
||||||
|
"@smallwei/avue": "~3.9.2",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
|
"avue-plugin-ueditor": "^1.0.4",
|
||||||
"axios": "^1.13.5",
|
"axios": "^1.13.5",
|
||||||
"codemirror": "^5.65.21",
|
"codemirror": "^5.65.21",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"dayjs": "^1.10.6",
|
"dayjs": "^1.10.6",
|
||||||
"disable-devtool": "^0.3.8",
|
"disable-devtool": "^0.3.8",
|
||||||
|
"element-plus": "^2.14.2",
|
||||||
|
"exceljs": "^4.4.0",
|
||||||
"highlight.js": "^11.9.0",
|
"highlight.js": "^11.9.0",
|
||||||
"js-base64": "^3.7.4",
|
"js-base64": "^3.7.4",
|
||||||
"js-cookie": "^3.0.0",
|
"js-cookie": "^3.0.0",
|
||||||
"js-md5": "^0.7.3",
|
"js-md5": "^0.7.3",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
|
"pdfjs-dist": "^6.1.200",
|
||||||
"sm-crypto": "^0.4.0",
|
"sm-crypto": "^0.4.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-i18n": "^11.4.2",
|
"vue-i18n": "^11.4.2",
|
||||||
|
"vue-pdf-embed": "^2.1.6",
|
||||||
"vue-router": "^4.3.2",
|
"vue-router": "^4.3.2",
|
||||||
"vue3-clipboard": "^1.0.0",
|
"vue3-clipboard": "^1.0.0",
|
||||||
"vuex": "^4.1.0"
|
"vuex": "^4.1.0",
|
||||||
|
"xlsx": "0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.2.4",
|
"@vitejs/plugin-vue": "^5.2.4",
|
||||||
"@vue/compiler-sfc": "^3.4.27",
|
"@vue/compiler-sfc": "^3.4.27",
|
||||||
"sass": "^1.85.1",
|
|
||||||
"prettier": "^2.8.7",
|
"prettier": "^2.8.7",
|
||||||
|
"sass": "^1.85.1",
|
||||||
"terser": "^5.31.1",
|
"terser": "^5.31.1",
|
||||||
"unplugin-auto-import": "^0.11.2",
|
"unplugin-auto-import": "^0.11.2",
|
||||||
"vite": "^5.4.19",
|
"vite": "^5.4.19",
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
diff --git a/es/packages/element-plus/form/index2.mjs b/es/packages/element-plus/form/index2.mjs
|
||||||
|
--- a/es/packages/element-plus/form/index2.mjs
|
||||||
|
+++ b/es/packages/element-plus/form/index2.mjs
|
||||||
|
@@ -100,6 +100,6 @@
|
||||||
|
},
|
||||||
|
labelSuffix() {
|
||||||
|
- return this.tableOption.labelSuffix || ":";
|
||||||
|
+ return this.tableOption.labelSuffix || "";
|
||||||
|
},
|
||||||
|
isMenu() {
|
||||||
|
return this.columnOption.length != 1;
|
||||||
|
diff --git a/lib/packages/element-plus/form/index2.js b/lib/packages/element-plus/form/index2.js
|
||||||
|
--- a/lib/packages/element-plus/form/index2.js
|
||||||
|
+++ b/lib/packages/element-plus/form/index2.js
|
||||||
|
@@ -104,6 +104,6 @@
|
||||||
|
},
|
||||||
|
labelSuffix() {
|
||||||
|
- return this.tableOption.labelSuffix || ":";
|
||||||
|
+ return this.tableOption.labelSuffix || "";
|
||||||
|
},
|
||||||
|
isMenu() {
|
||||||
|
return this.columnOption.length != 1;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
packages:
|
||||||
|
- '.'
|
||||||
|
|
||||||
|
patchedDependencies:
|
||||||
|
'@smallwei/avue': patches/@smallwei__avue.patch
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 794 B |
|
Before Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 310 KiB |
|
Before Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 450 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 310 KiB |
|
Before Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 243 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 113 KiB |
@@ -0,0 +1,73 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/cargo-type/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDetail = id => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/cargo-type/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const remove = ids => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/cargo-type/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
ids,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const submit = row => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/cargo-type/submit',
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importCargoType = file => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/cargo-type/import-cargo-type',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getParentOptions = keyword => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/cargo-type/parent-options',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
keyword,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getNextCode = parentCargoCode => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/cargo-type/next-code',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
parentCargoCode,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -39,14 +39,3 @@ export const remove = ids => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSourceOptions = (addressType, keyword) => {
|
|
||||||
return request({
|
|
||||||
url: '/blade-transport/common-address/source-options',
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
addressType,
|
|
||||||
keyword,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/insurance-ocr-template/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDetail = id => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/insurance-ocr-template/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const submit = row => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/insurance-ocr-template/submit',
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const remove = ids => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/insurance-ocr-template/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids },
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: '/blade-system/invoice-item/list',
|
||||||
|
method: 'get',
|
||||||
|
params: { ...params, current, size },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getAll = () => getList(1, 1000, {});
|
||||||
|
|
||||||
|
export const getDetail = id =>
|
||||||
|
request({
|
||||||
|
url: '/blade-system/invoice-item/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const submit = row =>
|
||||||
|
request({
|
||||||
|
url: '/blade-system/invoice-item/submit',
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const remove = ids =>
|
||||||
|
request({
|
||||||
|
url: '/blade-system/invoice-item/remove',
|
||||||
|
method: 'post',
|
||||||
|
params: { ids },
|
||||||
|
});
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { createCrudApi } from './common';
|
||||||
|
|
||||||
|
const api = createCrudApi('/blade-transport/common-cargo');
|
||||||
|
|
||||||
|
export const getList = api.getList;
|
||||||
|
export const getDetail = api.getDetail;
|
||||||
|
export const submit = api.submit;
|
||||||
|
export const remove = api.remove;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { createCrudApi } from './common';
|
||||||
|
|
||||||
|
const api = createCrudApi('/blade-transport/common-route');
|
||||||
|
|
||||||
|
export const getList = api.getList;
|
||||||
|
export const getDetail = api.getDetail;
|
||||||
|
export const submit = api.submit;
|
||||||
|
export const remove = api.remove;
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
export const createCrudApi = baseUrl => ({
|
||||||
|
getList(current, size, params) {
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/list`,
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getDetail(id) {
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/detail`,
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
submit(row) {
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/submit`,
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
remove(ids) {
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/remove`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
ids,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
copy(id) {
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/copy`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
enable(id) {
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/enable`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
disable(id) {
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/disable`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
cancel(id) {
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/cancel`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
complete(id) {
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/complete`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
dispatch(payload) {
|
||||||
|
const data = payload && typeof payload === 'object' ? payload : { id: payload };
|
||||||
|
const id = data.id || payload;
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/dispatch`,
|
||||||
|
method: 'post',
|
||||||
|
params: id ? { id } : undefined,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
reassign(id) {
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/reassign`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
batchComplete(ids) {
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/batch-complete`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
ids,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { createCrudApi } from './common';
|
||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/contract-manage';
|
||||||
|
const api = createCrudApi(baseUrl);
|
||||||
|
|
||||||
|
export const getList = api.getList;
|
||||||
|
export const getDetail = api.getDetail;
|
||||||
|
export const submit = api.submit;
|
||||||
|
export const remove = api.remove;
|
||||||
|
export const copy = api.copy;
|
||||||
|
|
||||||
|
export const saveDraft = row =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/save-draft`,
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const toTemporary = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/to-temporary`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const submitFormal = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/submit-formal`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const approve = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/approve`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const reject = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/reject`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateAttachments = row =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/update-attachments`,
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const withdraw = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/withdraw`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const startChange = (id, changeContent, changeReason = changeContent) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/start-change`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
changeContent: changeContent || '',
|
||||||
|
changeReason,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const submitChange = data =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/submit-change`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const terminate = (id, reason) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/terminate`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id, reason },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const expireStats = params =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/expire-stats`,
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
});
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/loading-manage';
|
||||||
|
|
||||||
|
export const exportUrl = `${baseUrl}/export-loading-manage`;
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/list`,
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getDetail = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/detail`,
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getCarrierContracts = projectIds =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/carrier-contracts`,
|
||||||
|
method: 'get',
|
||||||
|
params: { projectIds: (projectIds || []).join(',') },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const saveDraft = row =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/save-draft`,
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const submit = row =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/submit`,
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const remove = ids =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/remove`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
ids,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const copy = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/copy`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const reassign = row =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/reassign`,
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const cancel = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/cancel`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const complete = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/complete`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const batchComplete = ids =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/batch-complete`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
ids,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const changeRoute = row =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/change-route`,
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/master-order';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
export const getCarriers = id =>
|
||||||
|
request({ url: `${baseUrl}/carriers`, method: 'get', params: { id } });
|
||||||
|
export const submit = data => request({ url: `${baseUrl}/submit`, method: 'post', data });
|
||||||
|
export const saveDraft = data => request({ url: `${baseUrl}/draft`, method: 'post', data });
|
||||||
|
export const copy = id => request({ url: `${baseUrl}/copy`, method: 'post', params: { id } });
|
||||||
|
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
||||||
|
export const closeDispatch = id =>
|
||||||
|
request({ url: `${baseUrl}/close-dispatch`, method: 'post', params: { id } });
|
||||||
|
export const dispatch = data => request({ url: `${baseUrl}/dispatch`, method: 'post', data });
|
||||||
|
export const exportList = params =>
|
||||||
|
request({ url: `${baseUrl}/export`, method: 'get', params, responseType: 'blob' });
|
||||||
|
export const getAvailableProjects = () =>
|
||||||
|
request({
|
||||||
|
url: '/blade-transport/project-apply/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current: 1,
|
||||||
|
size: 9999,
|
||||||
|
approvalStatuses: 'approved,change_approved',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export const getProjectContracts = projectId =>
|
||||||
|
request({
|
||||||
|
url: '/blade-transport/contract-manage/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current: 1,
|
||||||
|
size: 9999,
|
||||||
|
projectId,
|
||||||
|
contractCategory: '客户合同',
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
import { createCrudApi } from './common';
|
||||||
|
|
||||||
|
const api = createCrudApi('/blade-transport/process-config');
|
||||||
|
|
||||||
|
export const getList = api.getList;
|
||||||
|
export const getDetail = (id, waybillId) =>
|
||||||
|
request({
|
||||||
|
url: '/blade-transport/process-config/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
...(waybillId === undefined ? {} : { waybillId }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export const getVoucherImages = waybillId =>
|
||||||
|
request({
|
||||||
|
url: '/blade-transport/process-config/voucher-images',
|
||||||
|
method: 'get',
|
||||||
|
params: { waybillId },
|
||||||
|
});
|
||||||
|
export const submit = api.submit;
|
||||||
|
export const remove = api.remove;
|
||||||
|
export const copy = api.copy;
|
||||||
|
export const enable = api.enable;
|
||||||
|
export const disable = api.disable;
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { createCrudApi } from './common';
|
||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/project-apply';
|
||||||
|
const api = createCrudApi(baseUrl);
|
||||||
|
|
||||||
|
export const getList = api.getList;
|
||||||
|
export const getDetail = api.getDetail;
|
||||||
|
export const submit = api.submit;
|
||||||
|
export const remove = api.remove;
|
||||||
|
|
||||||
|
// 项目资金使用风险统计(高风险 / 中风险数量),供列表头部快速筛选标签使用
|
||||||
|
export const getFundRiskStats = params =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/fund-risk-stats`,
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const saveDraft = row =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/save-draft`,
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const submitApproval = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/submit-approval`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const approve = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/approve`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const reject = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/reject`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const withdraw = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/withdraw`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const voidProject = (id, reason) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/void`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id, reason },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const saveChange = row =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/save-change`,
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const submitChange = row =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/submit-change`,
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const startChange = (id, reason) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/start-change`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
changeContent: reason,
|
||||||
|
changeReason: reason,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { createCrudApi } from './common';
|
||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const api = createCrudApi('/blade-transport/shipping-template');
|
||||||
|
|
||||||
|
export const getList = api.getList;
|
||||||
|
export const getDetail = api.getDetail;
|
||||||
|
export const submit = api.submit;
|
||||||
|
export const remove = api.remove;
|
||||||
|
export const copy = api.copy;
|
||||||
|
export const nextCode = () => request({ url: '/blade-transport/shipping-template/next-code', method: 'get' });
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { createCrudApi } from './common';
|
||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/temporary-credit-limit';
|
||||||
|
const api = createCrudApi(baseUrl);
|
||||||
|
|
||||||
|
export const getList = api.getList;
|
||||||
|
export const getDetail = api.getDetail;
|
||||||
|
export const submit = api.submit;
|
||||||
|
export const remove = api.remove;
|
||||||
|
|
||||||
|
export const saveDraft = row =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/save-draft`,
|
||||||
|
method: 'post',
|
||||||
|
data: row,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const submitApproval = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/submit-approval`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const approve = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/approve`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const reject = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/reject`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const withdraw = id =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/withdraw`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { createCrudApi } from './common';
|
||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/transport-plan';
|
||||||
|
const api = createCrudApi(baseUrl);
|
||||||
|
|
||||||
|
export const getList = api.getList;
|
||||||
|
export const getDetail = api.getDetail;
|
||||||
|
export const submit = api.submit;
|
||||||
|
export const dispatch = api.dispatch;
|
||||||
|
export const remove = api.remove;
|
||||||
|
export const copy = api.copy;
|
||||||
|
export const complete = api.complete;
|
||||||
|
|
||||||
|
export const importTransportPlan = ({
|
||||||
|
file,
|
||||||
|
projectId,
|
||||||
|
projectName,
|
||||||
|
customerName,
|
||||||
|
contractId,
|
||||||
|
contractName,
|
||||||
|
}) => {
|
||||||
|
const data = new FormData();
|
||||||
|
data.append('file', file);
|
||||||
|
data.append('projectId', projectId || '');
|
||||||
|
data.append('projectName', projectName || '');
|
||||||
|
data.append('customerName', customerName || '');
|
||||||
|
data.append('contractId', contractId || '');
|
||||||
|
data.append('contractName', contractName || '');
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/import-transport-plan`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const validateTransportPlan = ({
|
||||||
|
file,
|
||||||
|
projectId,
|
||||||
|
projectName,
|
||||||
|
customerName,
|
||||||
|
contractId,
|
||||||
|
contractName,
|
||||||
|
}) => {
|
||||||
|
const data = new FormData();
|
||||||
|
data.append('file', file);
|
||||||
|
data.append('projectId', projectId || '');
|
||||||
|
data.append('projectName', projectName || '');
|
||||||
|
data.append('customerName', customerName || '');
|
||||||
|
data.append('contractId', contractId || '');
|
||||||
|
data.append('contractName', contractName || '');
|
||||||
|
return request({
|
||||||
|
url: `${baseUrl}/validate-transport-plan`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60000,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/voucher-manage';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
export const getFolderPage = (voucherId, current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/folder-page`, method: 'get', params: { voucherId, current, size, ...params } });
|
||||||
|
export const getFolderDetail = (voucherId, plateNo) =>
|
||||||
|
request({ url: `${baseUrl}/folder-detail`, method: 'get', params: { voucherId, plateNo } });
|
||||||
|
export const replaceFolder = (voucherId, plateNo, file) => {
|
||||||
|
const data = new FormData();
|
||||||
|
data.append('voucherId', voucherId);
|
||||||
|
data.append('plateNo', plateNo);
|
||||||
|
data.append('file', file);
|
||||||
|
return request({ url: `${baseUrl}/folder-replace`, method: 'post', data });
|
||||||
|
};
|
||||||
|
export const replaceFolderByObject = (voucherId, plateNo, fileInfo) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/folder-replace-object`,
|
||||||
|
method: 'post',
|
||||||
|
params: { voucherId, plateNo, ...fileInfo },
|
||||||
|
timeout: 120000,
|
||||||
|
});
|
||||||
|
export const removeFolder = (voucherId, plateNo) =>
|
||||||
|
request({ url: `${baseUrl}/folder-remove`, method: 'post', params: { voucherId, plateNo } });
|
||||||
|
export const submit = data => request({ url: `${baseUrl}/submit`, method: 'post', data });
|
||||||
|
export const createUploadDraft = data =>
|
||||||
|
request({ url: `${baseUrl}/upload-draft`, method: 'post', data });
|
||||||
|
export const completeUploadFile = data =>
|
||||||
|
request({ url: `${baseUrl}/complete-upload-file`, method: 'post', data });
|
||||||
|
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
||||||
|
export const getWaybillBatches = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/waybill-batches`,
|
||||||
|
method: 'get',
|
||||||
|
params: { current, size, ...params },
|
||||||
|
});
|
||||||
|
export const auditPass = id => request({ url: `${baseUrl}/audit-pass`, method: 'post', params: { id } });
|
||||||
|
export const auditReject = (id, rejectReason) =>
|
||||||
|
request({ url: `${baseUrl}/audit-reject`, method: 'post', params: { id, rejectReason } });
|
||||||
|
|
||||||
|
const fileTaskBaseUrl = '/blade-file/fileTask';
|
||||||
|
export const queryFileTask = md5 =>
|
||||||
|
request({ url: `${fileTaskBaseUrl}/queryFileTask`, method: 'get', params: { md5 } });
|
||||||
|
export const getFileTask = id =>
|
||||||
|
request({ url: `${fileTaskBaseUrl}/queryFileTask`, method: 'get', params: { id } });
|
||||||
|
export const createFileTask = data =>
|
||||||
|
request({ url: `${fileTaskBaseUrl}/createFileTask`, method: 'post', data });
|
||||||
|
export const getPartUploadUrl = (id, partNumber) =>
|
||||||
|
request({ url: `${fileTaskBaseUrl}/getUploadUrl`, method: 'get', params: { id, partNumber } });
|
||||||
|
export const updateFileTask = data =>
|
||||||
|
request({ url: `${fileTaskBaseUrl}/updateFileTask`, method: 'post', data });
|
||||||
|
export const pauseFileTask = ids =>
|
||||||
|
request({ url: `${fileTaskBaseUrl}/updatePaused`, method: 'post', data: { ids } });
|
||||||
|
export const resumeFileTask = id =>
|
||||||
|
request({ url: `${fileTaskBaseUrl}/resume`, method: 'post', params: { id } });
|
||||||
|
export const getFileTaskList = (current, size, params) =>
|
||||||
|
request({ url: `${fileTaskBaseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const getFileTaskUrl = id =>
|
||||||
|
request({ url: `${fileTaskBaseUrl}/file-url`, method: 'get', params: { id } });
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
import { createCrudApi } from './common';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/waybill-manage';
|
||||||
|
const api = createCrudApi(baseUrl);
|
||||||
|
|
||||||
|
export const getList = api.getList;
|
||||||
|
export const getDetail = api.getDetail;
|
||||||
|
export const submit = api.submit;
|
||||||
|
export const saveDraft = data => request({ url: `${baseUrl}/save-draft`, method: 'post', data });
|
||||||
|
export const remove = api.remove;
|
||||||
|
export const copy = api.copy;
|
||||||
|
export const cancel = api.cancel;
|
||||||
|
export const reassign = api.reassign;
|
||||||
|
export const complete = api.complete;
|
||||||
|
export const batchComplete = api.batchComplete;
|
||||||
|
export const changeRoute = data =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/change-route`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
export const maintainMileage = data =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/maintain-mileage`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
export const roadLoading = ids =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/road-loading`,
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
ids,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getImportBatches = params => request({ url: `${baseUrl}/import-batch/list`, method: 'get', params });
|
||||||
|
export const getImportDetails = params => request({ url: `${baseUrl}/import-batch/details`, method: 'get', params });
|
||||||
|
export const removeImportBatches = ids => request({ url: `${baseUrl}/import-batch/remove`, method: 'post', params: { ids } });
|
||||||
|
export const getImportOptions = () => request({ url: `${baseUrl}/import-batch/options`, method: 'get' });
|
||||||
|
export const exportImportTemplate = () => request({ url: `${baseUrl}/import-batch/export-template`, method: 'get', responseType: 'blob' });
|
||||||
|
export const saveImportDraft = data => request({ url: `${baseUrl}/import-batch/draft`, method: 'post', data });
|
||||||
|
export const validateImport = data => request({ url: `${baseUrl}/import-batch/validate`, method: 'post', data, responseType: 'blob' });
|
||||||
|
export const confirmImport = data => request({ url: `${baseUrl}/import-batch/confirm`, method: 'post', data });
|
||||||
@@ -1,15 +1,48 @@
|
|||||||
import request from '@/axios';
|
import request from '@/axios';
|
||||||
|
import { ElLoading, ElMessage } from 'element-plus';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件流返回
|
* 文件流返回
|
||||||
* @param url 接口地址
|
* @param url 接口地址
|
||||||
* @param params 接口参数
|
* @param params 接口参数
|
||||||
*/
|
*/
|
||||||
export const exportBlob = (url, params) => {
|
export const exportBlob = (url, params, options = {}) => {
|
||||||
|
const { feedback = false, loadingText = '导出中', successText = '导出完成' } = options;
|
||||||
|
const loading = feedback
|
||||||
|
? ElLoading.service({
|
||||||
|
lock: true,
|
||||||
|
text: loadingText,
|
||||||
|
background: 'rgba(255, 255, 255, 0.7)',
|
||||||
|
})
|
||||||
|
: null;
|
||||||
return request({
|
return request({
|
||||||
url: url,
|
url: url,
|
||||||
params: params,
|
params: params,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (feedback) {
|
||||||
|
ElMessage.success(successText);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
if (loading) {
|
||||||
|
loading.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importBlob = (url, file, options = {}) => {
|
||||||
|
const { timeout = 60000 } = options;
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
return request({
|
||||||
|
url,
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/bill-ledger';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
export const getExpiryCounts = () => request({ url: `${baseUrl}/expiry-counts`, method: 'get' });
|
||||||
|
export const getAvailableOptions = (keyword, deptId, selectedId) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/available-options`,
|
||||||
|
method: 'get',
|
||||||
|
params: { keyword, deptId, selectedId },
|
||||||
|
});
|
||||||
|
export const getAvailablePage = (current, size, keyword, deptId, selectedId) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/available-page`,
|
||||||
|
method: 'get',
|
||||||
|
params: { current, size, keyword, deptId, selectedId },
|
||||||
|
});
|
||||||
|
export const submit = data => request({ url: `${baseUrl}/submit`, method: 'post', data });
|
||||||
|
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
||||||
|
|
||||||
|
export const billTypeOptions = [
|
||||||
|
{ label: '开票', value: 'issued' },
|
||||||
|
{ label: '收票', value: 'received' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const maturityStatusOptions = [
|
||||||
|
{ label: '未到期', value: 'unexpired' },
|
||||||
|
{ label: '今日到期', value: 'due_today' },
|
||||||
|
{ label: '已到期', value: 'expired' },
|
||||||
|
];
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/bill-payment';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
export const save = data => request({ url: `${baseUrl}/save`, method: 'post', data });
|
||||||
|
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
||||||
|
export const submit = data => request({ url: `${baseUrl}/submit`, method: 'post', data });
|
||||||
|
export const approve = data => request({ url: `${baseUrl}/approve`, method: 'post', data });
|
||||||
|
export const returnBill = data => request({ url: `${baseUrl}/return`, method: 'post', data });
|
||||||
|
export const voidBill = data => request({ url: `${baseUrl}/void`, method: 'post', data });
|
||||||
|
|
||||||
|
export const approvalStatusOptions = [
|
||||||
|
{ label: '草稿', value: 'draft' },
|
||||||
|
{ label: '审批中', value: 'reviewing' },
|
||||||
|
{ label: '已驳回', value: 'returned' },
|
||||||
|
{ label: '审批通过', value: 'approved' },
|
||||||
|
{ label: '已作废', value: 'voided' },
|
||||||
|
];
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/invoice-application';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
export const getSettlementCandidates = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/settlement-candidates`,
|
||||||
|
method: 'get',
|
||||||
|
params: { current, size, ...params },
|
||||||
|
});
|
||||||
|
export const getSettlementDetails = settlementIds =>
|
||||||
|
request({ url: `${baseUrl}/settlement-details`, method: 'get', params: { settlementIds } });
|
||||||
|
export const getReceiverInformation = settlementIds =>
|
||||||
|
request({ url: `${baseUrl}/receiver-information`, method: 'get', params: { settlementIds } });
|
||||||
|
export const save = data => request({ url: `${baseUrl}/save`, method: 'post', data });
|
||||||
|
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
||||||
|
export const submit = data => request({ url: `${baseUrl}/submit`, method: 'post', data });
|
||||||
|
export const approve = data => request({ url: `${baseUrl}/approve`, method: 'post', data });
|
||||||
|
export const returnBill = data => request({ url: `${baseUrl}/return`, method: 'post', data });
|
||||||
|
export const voidBill = data => request({ url: `${baseUrl}/void`, method: 'post', data });
|
||||||
|
export const syncKingdee = id =>
|
||||||
|
request({ url: `${baseUrl}/sync-kingdee`, method: 'post', params: { id } });
|
||||||
|
|
||||||
|
export const invoiceTypeOptions = [
|
||||||
|
{ label: '电子专票', value: 'electronic_special' },
|
||||||
|
{ label: '电子普票', value: 'electronic_normal' },
|
||||||
|
];
|
||||||
|
export const approvalStatusOptions = [
|
||||||
|
{ label: '草稿', value: 'draft' },
|
||||||
|
{ label: '审批中', value: 'reviewing' },
|
||||||
|
{ label: '审批通过', value: 'approved' },
|
||||||
|
{ label: '已驳回', value: 'returned' },
|
||||||
|
{ label: '已作废', value: 'voided' },
|
||||||
|
];
|
||||||
|
export const kingdeeStatusOptions = [
|
||||||
|
{ label: '未同步', value: 'unsynced' },
|
||||||
|
{ label: '已同步', value: 'synced' },
|
||||||
|
{ label: '同步失败', value: 'failed' },
|
||||||
|
];
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/invoice-receipt';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
export const getInvoicePool = keyword =>
|
||||||
|
request({ url: `${baseUrl}/invoice-pool`, method: 'get', params: { keyword } });
|
||||||
|
export const getSettlementCandidates = (keyword, receiptId) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/settlement-candidates`,
|
||||||
|
method: 'get',
|
||||||
|
params: { keyword, receiptId },
|
||||||
|
});
|
||||||
|
export const getReferenceInformation = settlementIds =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/reference-information`,
|
||||||
|
method: 'get',
|
||||||
|
params: { settlementIds },
|
||||||
|
});
|
||||||
|
export const save = data => request({ url: `${baseUrl}/save`, method: 'post', data });
|
||||||
|
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
||||||
|
export const submit = data => request({ url: `${baseUrl}/submit`, method: 'post', data });
|
||||||
|
export const approve = data => request({ url: `${baseUrl}/approve`, method: 'post', data });
|
||||||
|
export const returnBill = data => request({ url: `${baseUrl}/return`, method: 'post', data });
|
||||||
|
export const voidBill = data => request({ url: `${baseUrl}/void`, method: 'post', data });
|
||||||
|
export const syncKingdee = id =>
|
||||||
|
request({ url: `${baseUrl}/sync-kingdee`, method: 'post', params: { id } });
|
||||||
|
|
||||||
|
export const approvalStatusOptions = [
|
||||||
|
{ label: '草稿', value: 'draft' },
|
||||||
|
{ label: '审批中', value: 'reviewing' },
|
||||||
|
{ label: '审批通过', value: 'approved' },
|
||||||
|
{ label: '已驳回', value: 'returned' },
|
||||||
|
{ label: '已作废', value: 'voided' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const kingdeeStatusOptions = [
|
||||||
|
{ label: '未同步', value: 'unsynced' },
|
||||||
|
{ label: '已同步', value: 'synced' },
|
||||||
|
{ label: '同步失败', value: 'failed' },
|
||||||
|
];
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/payment-application';
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
export const getReferenceAmount = (paymentType, referenceId, excludeId) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/reference-amount`,
|
||||||
|
method: 'get',
|
||||||
|
params: { paymentType, referenceId, excludeId: excludeId || undefined },
|
||||||
|
});
|
||||||
|
export const save = data => request({ url: `${baseUrl}/save`, method: 'post', data });
|
||||||
|
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
||||||
|
export const submit = data => request({ url: `${baseUrl}/submit`, method: 'post', data });
|
||||||
|
export const approve = data => request({ url: `${baseUrl}/approve`, method: 'post', data });
|
||||||
|
export const returnBill = data => request({ url: `${baseUrl}/return`, method: 'post', data });
|
||||||
|
export const voidBill = data => request({ url: `${baseUrl}/void`, method: 'post', data });
|
||||||
|
export const syncKingdee = id =>
|
||||||
|
request({ url: `${baseUrl}/sync-kingdee`, method: 'post', params: { id } });
|
||||||
|
export const syncKingdeeBatch = ids =>
|
||||||
|
request({ url: `${baseUrl}/sync-kingdee-batch`, method: 'post', data: ids });
|
||||||
|
|
||||||
|
export const paymentTypeOptions = [
|
||||||
|
{ label: '项目预付', value: 'project_advance' },
|
||||||
|
{ label: '进度预付', value: 'progress_advance' },
|
||||||
|
{ label: '结算付款', value: 'settlement_payment' },
|
||||||
|
];
|
||||||
|
export const approvalStatusOptions = [
|
||||||
|
{ label: '草稿', value: 'draft' },
|
||||||
|
{ label: '审批中', value: 'reviewing' },
|
||||||
|
{ label: '审批通过', value: 'approved' },
|
||||||
|
{ label: '已驳回', value: 'returned' },
|
||||||
|
{ label: '已作废', value: 'voided' },
|
||||||
|
];
|
||||||
|
export const kingdeeStatusOptions = [
|
||||||
|
{ label: '未生成', value: 'unsynced' },
|
||||||
|
{ label: '已生成', value: 'synced' },
|
||||||
|
{ label: '生成失败', value: 'failed' },
|
||||||
|
];
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/receipt-claim-record';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
export const updateAttachments = data =>
|
||||||
|
request({ url: `${baseUrl}/attachments`, method: 'post', data });
|
||||||
|
export const voidClaim = id => request({ url: `${baseUrl}/void`, method: 'post', params: { id } });
|
||||||
|
|
||||||
|
export const claimStatusOptions = [
|
||||||
|
{ label: '已认领', value: 'claimed' },
|
||||||
|
{ label: '已作废', value: 'voided' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const kingdeeBillStatusOptions = [
|
||||||
|
{ label: '未生成', value: 'none' },
|
||||||
|
{ label: '审核通过', value: 'approved' },
|
||||||
|
{ label: '处理失败', value: 'failed' },
|
||||||
|
];
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/receipt-flow';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
export const getSettlementCandidates = (keyword, flowId) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/settlement-candidates`,
|
||||||
|
method: 'get',
|
||||||
|
params: { keyword, flowId },
|
||||||
|
});
|
||||||
|
export const claim = data => request({ url: `${baseUrl}/claim`, method: 'post', data });
|
||||||
|
export const sync = data => request({ url: `${baseUrl}/sync`, method: 'post', data });
|
||||||
|
|
||||||
|
export const claimStatusOptions = [
|
||||||
|
{ label: '未认领', value: 'unclaimed' },
|
||||||
|
{ label: '部分认领', value: 'partial' },
|
||||||
|
{ label: '认领完成', value: 'claimed' },
|
||||||
|
];
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/formal-settlement';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const exportList = params =>
|
||||||
|
request({ url: `${baseUrl}/export`, method: 'get', params, responseType: 'blob' });
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
export const getCandidates = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/candidate-pre-settlements`,
|
||||||
|
method: 'get',
|
||||||
|
params: { current, size, ...params },
|
||||||
|
});
|
||||||
|
export const getContractOptions = (keyword, projectId) =>
|
||||||
|
request({ url: `${baseUrl}/contract-options`, method: 'get', params: { keyword, projectId } });
|
||||||
|
export const getNextNo = settlementType =>
|
||||||
|
request({ url: `${baseUrl}/next-no`, method: 'get', params: { settlementType } });
|
||||||
|
export const getFeeOptions = () => request({ url: `${baseUrl}/fee-options`, method: 'get' });
|
||||||
|
export const getCandidateDetails = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/candidate-details`,
|
||||||
|
method: 'get',
|
||||||
|
params: { current, size, ...params },
|
||||||
|
});
|
||||||
|
export const save = data => request({ url: `${baseUrl}/save`, method: 'post', data });
|
||||||
|
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
||||||
|
export const submit = data => request({ url: `${baseUrl}/submit`, method: 'post', data });
|
||||||
|
export const approve = data => request({ url: `${baseUrl}/approve`, method: 'post', data });
|
||||||
|
export const returnBill = data => request({ url: `${baseUrl}/return`, method: 'post', data });
|
||||||
|
export const voidBill = data => request({ url: `${baseUrl}/void`, method: 'post', data });
|
||||||
|
export const syncKingdee = id =>
|
||||||
|
request({ url: `${baseUrl}/sync-kingdee`, method: 'post', params: { id } });
|
||||||
|
export const getDetailFees = detailId =>
|
||||||
|
request({ url: `${baseUrl}/detail-fees`, method: 'get', params: { detailId } });
|
||||||
|
export const getReceiptClaims = formalSettlementId =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/receipt-claims`,
|
||||||
|
method: 'get',
|
||||||
|
params: { formalSettlementId },
|
||||||
|
});
|
||||||
|
export const adjustDetail = data =>
|
||||||
|
request({ url: `${baseUrl}/adjust-detail`, method: 'post', data });
|
||||||
|
export const applyPayment = data =>
|
||||||
|
request({ url: `${baseUrl}/apply-payment`, method: 'post', data });
|
||||||
|
export const applyPayments = data =>
|
||||||
|
request({ url: `${baseUrl}/apply-payments`, method: 'post', data });
|
||||||
|
export const claimInvoices = data =>
|
||||||
|
request({ url: `${baseUrl}/claim-invoices`, method: 'post', data });
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/pre-settlement';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
|
||||||
|
export const getContractOptions = keyword =>
|
||||||
|
request({ url: `${baseUrl}/contract-options`, method: 'get', params: { keyword } });
|
||||||
|
|
||||||
|
export const getContractList = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: '/blade-transport/contract-manage/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
approvalStatuses: 'approved,change_approved',
|
||||||
|
...params,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getFeeOptions = () => request({ url: `${baseUrl}/fee-options`, method: 'get' });
|
||||||
|
|
||||||
|
export const getCandidateDetails = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/candidate-details`,
|
||||||
|
method: 'get',
|
||||||
|
params: { current, size, ...params },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const save = data => request({ url: `${baseUrl}/save`, method: 'post', data });
|
||||||
|
|
||||||
|
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
||||||
|
|
||||||
|
export const removeDetail = (id, detailId) =>
|
||||||
|
request({ url: `${baseUrl}/remove-detail`, method: 'post', params: { id, detailId } });
|
||||||
|
|
||||||
|
export const submit = data => request({ url: `${baseUrl}/submit`, method: 'post', data });
|
||||||
|
|
||||||
|
export const approve = data => request({ url: `${baseUrl}/approve`, method: 'post', data });
|
||||||
|
|
||||||
|
export const returnBill = data => request({ url: `${baseUrl}/return`, method: 'post', data });
|
||||||
|
|
||||||
|
export const voidBill = data => request({ url: `${baseUrl}/void`, method: 'post', data });
|
||||||
|
|
||||||
|
export const applyAdvance = data =>
|
||||||
|
request({ url: `${baseUrl}/apply-advance`, method: 'post', data });
|
||||||
|
|
||||||
|
export const updateAdvancePaid = params =>
|
||||||
|
request({ url: `${baseUrl}/update-advance-paid`, method: 'post', params });
|
||||||
|
|
||||||
|
export const voidAdvance = params =>
|
||||||
|
request({ url: `${baseUrl}/void-advance`, method: 'post', params });
|
||||||
|
|
||||||
|
export const formalSettlement = id =>
|
||||||
|
request({ url: `${baseUrl}/formal-settlement`, method: 'post', params: { id } });
|
||||||
|
|
||||||
|
export const getDetailFees = detailId =>
|
||||||
|
request({ url: `${baseUrl}/detail-fees`, method: 'get', params: { detailId } });
|
||||||
|
|
||||||
|
export const adjustDetail = data =>
|
||||||
|
request({ url: `${baseUrl}/adjust-detail`, method: 'post', data });
|
||||||
|
|
||||||
|
export const getPrintTemplates = id =>
|
||||||
|
request({ url: `${baseUrl}/print-templates`, method: 'get', params: { id } });
|
||||||
|
|
||||||
|
export const exportList = params =>
|
||||||
|
request({ url: `${baseUrl}/export`, method: 'get', params, responseType: 'blob' });
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/receivable-payable-detail';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
|
||||||
|
export const getFeeDetail = id =>
|
||||||
|
request({ url: `${baseUrl}/fee-detail`, method: 'get', params: { id } });
|
||||||
|
|
||||||
|
export const getChangeRecords = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/change-records`,
|
||||||
|
method: 'get',
|
||||||
|
params: { current, size, ...params },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateFee = data => request({ url: `${baseUrl}/update-fee`, method: 'post', data });
|
||||||
|
|
||||||
|
export const getUpdateFeeContracts = params =>
|
||||||
|
request({ url: `${baseUrl}/update-fee-contracts`, method: 'get', params });
|
||||||
|
|
||||||
|
export const adjustFee = data => request({ url: `${baseUrl}/adjust-fee`, method: 'post', data });
|
||||||
|
|
||||||
|
export const calculateAdjustedFee = data =>
|
||||||
|
request({ url: `${baseUrl}/calculate-adjusted-fee`, method: 'post', data });
|
||||||
|
|
||||||
|
export const getTransferCandidates = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/transfer-candidates`,
|
||||||
|
method: 'get',
|
||||||
|
params: { current, size, ...params },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getGenerateWaybills = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/generate-waybills`,
|
||||||
|
method: 'get',
|
||||||
|
params: { current, size, ...params },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getGeneratePreview = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/generate-preview`,
|
||||||
|
method: 'get',
|
||||||
|
params: { current, size, ...params },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const generateFee = data =>
|
||||||
|
request({ url: `${baseUrl}/generate-fee`, method: 'post', data });
|
||||||
|
|
||||||
|
export const exportDetail = params =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/export-receivable-payable-detail`,
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/settlement-adjustment';
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
export const getFormalSettlements = keyword =>
|
||||||
|
request({ url: `${baseUrl}/candidate-formal-settlements`, method: 'get', params: { keyword } });
|
||||||
|
export const getFormalDetails = formalSettlementId =>
|
||||||
|
request({ url: `${baseUrl}/formal-details`, method: 'get', params: { formalSettlementId } });
|
||||||
|
export const getFeeOptions = () =>
|
||||||
|
request({ url: `${baseUrl}/fee-options`, method: 'get' });
|
||||||
|
export const save = data => request({ url: `${baseUrl}/save`, method: 'post', data });
|
||||||
|
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
||||||
|
export const submit = id => request({ url: `${baseUrl}/submit`, method: 'post', data: { id } });
|
||||||
|
export const approve = id => request({ url: `${baseUrl}/approve`, method: 'post', data: { id } });
|
||||||
|
export const returnBill = (id, reason) =>
|
||||||
|
request({ url: `${baseUrl}/return`, method: 'post', data: { id, reason } });
|
||||||
|
export const repush = id => request({ url: `${baseUrl}/repush`, method: 'post', params: { id } });
|
||||||
|
|
||||||
|
export const approvalStatusOptions = [
|
||||||
|
{ label: '草稿', value: 'draft' },
|
||||||
|
{ label: '审批中', value: 'reviewing' },
|
||||||
|
{ label: '审批通过', value: 'approved' },
|
||||||
|
{ label: '已驳回', value: 'returned' },
|
||||||
|
];
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/transport-reconciliation';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
export const exportList = params =>
|
||||||
|
request({ url: `${baseUrl}/export`, method: 'get', params, responseType: 'blob' });
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
export const getFormalOptions = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/formal-options`,
|
||||||
|
method: 'get',
|
||||||
|
params: { current, size, ...params },
|
||||||
|
});
|
||||||
|
export const save = data => request({ url: `${baseUrl}/save`, method: 'post', data });
|
||||||
|
export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } });
|
||||||
|
export const match = id => request({ url: `${baseUrl}/match`, method: 'post', params: { id } });
|
||||||
|
export const matchPreview = data =>
|
||||||
|
request({ url: `${baseUrl}/match-preview`, method: 'post', data });
|
||||||
|
export const manualMatch = data =>
|
||||||
|
request({ url: `${baseUrl}/manual-match`, method: 'post', data });
|
||||||
|
export const unmatch = internalId =>
|
||||||
|
request({ url: `${baseUrl}/unmatch`, method: 'post', params: { internalId } });
|
||||||
|
export const adjust = data => request({ url: `${baseUrl}/adjust`, method: 'post', data });
|
||||||
|
export const updateByMatch = (id, data) =>
|
||||||
|
request({ url: `${baseUrl}/update-by-match`, method: 'post', params: { id }, data });
|
||||||
|
export const complete = id =>
|
||||||
|
request({ url: `${baseUrl}/complete`, method: 'post', params: { id } });
|
||||||
|
export const completeWithData = data =>
|
||||||
|
request({ url: `${baseUrl}/complete-with-data`, method: 'post', data });
|
||||||
|
export const template = mode =>
|
||||||
|
request({ url: `${baseUrl}/template`, method: 'get', params: { mode }, responseType: 'blob' });
|
||||||
|
|
||||||
|
const importFile = (url, id, file) => {
|
||||||
|
const data = new FormData();
|
||||||
|
data.append('id', id);
|
||||||
|
data.append('file', file);
|
||||||
|
return request({ url, method: 'post', data, responseType: 'blob', timeout: 60000 });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const importVehicle = (id, file) => importFile(`${baseUrl}/import-vehicle`, id, file);
|
||||||
|
export const importCargo = (id, file) => importFile(`${baseUrl}/import-cargo`, id, file);
|
||||||
@@ -86,3 +86,10 @@ export const getDictionary = params => {
|
|||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDictionaryAll = () => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/dict-biz/select-all',
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -61,6 +61,13 @@ export const getUser = id => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCustomerOptions = () => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-system/user/customer-options',
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const getUserPlatform = id => {
|
export const getUserPlatform = id => {
|
||||||
return request({
|
return request({
|
||||||
url: '/blade-system/user/platform-detail',
|
url: '/blade-system/user/platform-detail',
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/exception-disposal';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
|
||||||
|
export const follow = data => request({ url: `${baseUrl}/follow`, method: 'post', data });
|
||||||
|
|
||||||
|
export const complete = data => request({ url: `${baseUrl}/complete`, method: 'post', data });
|
||||||
|
|
||||||
|
export const batchComplete = ids =>
|
||||||
|
request({ url: `${baseUrl}/batch-complete`, method: 'post', params: { ids } });
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/risk-disposal';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
|
||||||
|
|
||||||
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
|
|
||||||
|
export const dispose = data => request({ url: `${baseUrl}/dispose`, method: 'post', data });
|
||||||
|
|
||||||
|
export const batchDispose = data =>
|
||||||
|
request({ url: `${baseUrl}/batch-dispose`, method: 'post', data });
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/track-tracking';
|
||||||
|
|
||||||
|
export const getTrackingMap = params =>
|
||||||
|
request({ url: `${baseUrl}/map`, method: 'get', params });
|
||||||
|
|
||||||
|
export const getShipTracking = params =>
|
||||||
|
request({ url: `${baseUrl}/ship`, method: 'get', params });
|
||||||
@@ -58,3 +58,36 @@ export const getExpiryStat = params => {
|
|||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const recognitionIDCard = url => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-file/file/recognitionIDCard',
|
||||||
|
method: 'get',
|
||||||
|
timeout: 60000,
|
||||||
|
params: {
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const recognitionTransportCertificates = certificates => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-file/file/recognitionTransportCertificates',
|
||||||
|
method: 'post',
|
||||||
|
timeout: 60000,
|
||||||
|
data: certificates,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const recognizeBaiduOcr = (imageUrl, type, side = '') => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/baidu-ocr/recognize-url',
|
||||||
|
method: 'post',
|
||||||
|
timeout: 60000,
|
||||||
|
params: {
|
||||||
|
imageUrl,
|
||||||
|
type,
|
||||||
|
...(side ? { side } : {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -58,3 +58,16 @@ export const getExpiryStat = params => {
|
|||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const recognizeBaiduOcr = (imageUrl, type, side = '') => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/baidu-ocr/recognize-url',
|
||||||
|
method: 'post',
|
||||||
|
timeout: 60000,
|
||||||
|
params: {
|
||||||
|
imageUrl,
|
||||||
|
type,
|
||||||
|
...(side ? { side } : {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -51,6 +51,14 @@ export const changeStatus = (id, status) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const auditCertification = (id, certificationStatus, rejectReason) => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/transport-vehicle/audit-certification',
|
||||||
|
method: 'post',
|
||||||
|
params: { id, certificationStatus, rejectReason },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const getExpiryStat = params => {
|
export const getExpiryStat = params => {
|
||||||
return request({
|
return request({
|
||||||
url: '/blade-transport/transport-vehicle/expiry-stat',
|
url: '/blade-transport/transport-vehicle/expiry-stat',
|
||||||
@@ -58,3 +66,25 @@ export const getExpiryStat = params => {
|
|||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const recognitionTransportCertificates = objectKeys => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-file/file/recognitionTransportCertificates',
|
||||||
|
method: 'post',
|
||||||
|
timeout: 60000,
|
||||||
|
data: objectKeys,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const recognizeBaiduOcr = (imageUrl, type, side = '') => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/baidu-ocr/recognize-url',
|
||||||
|
method: 'post',
|
||||||
|
timeout: 60000,
|
||||||
|
params: {
|
||||||
|
imageUrl,
|
||||||
|
type,
|
||||||
|
...(side ? { side } : {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ export const loginBySso = (tenantId, state, code) =>
|
|||||||
headers: {
|
headers: {
|
||||||
'Tenant-Id': tenantId,
|
'Tenant-Id': tenantId,
|
||||||
},
|
},
|
||||||
|
meta: {
|
||||||
|
isToken: false,
|
||||||
|
},
|
||||||
params: {
|
params: {
|
||||||
tenant_id: tenantId,
|
tenant_id: tenantId,
|
||||||
code,
|
code,
|
||||||
|
|||||||
@@ -22,6 +22,18 @@ export const getDetail = id => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getChangeRecordList = (customerId, current, size) => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/customer-archive/change-record/list',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
customerId,
|
||||||
|
current,
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const submit = row => {
|
export const submit = row => {
|
||||||
return request({
|
return request({
|
||||||
url: '/blade-transport/customer-archive/submit',
|
url: '/blade-transport/customer-archive/submit',
|
||||||
@@ -40,6 +52,16 @@ export const submitApproval = id => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const withdrawApproval = id => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/customer-archive/withdraw-approval',
|
||||||
|
method: 'post',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const approve = id => {
|
export const approve = id => {
|
||||||
return request({
|
return request({
|
||||||
url: '/blade-transport/customer-archive/approve',
|
url: '/blade-transport/customer-archive/approve',
|
||||||
@@ -90,3 +112,15 @@ export const getScoreTemplate = quantificationId => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const recognizeBusinessLicenseOcr = imageUrl => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/baidu-ocr/recognize-url',
|
||||||
|
method: 'post',
|
||||||
|
timeout: 60000,
|
||||||
|
params: {
|
||||||
|
imageUrl,
|
||||||
|
type: 'business_license',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
export const getList = (current, size, params) =>
|
||||||
|
request({
|
||||||
|
url: '/blade-transport/equipment-ledger/list',
|
||||||
|
method: 'get',
|
||||||
|
params: { ...params, current, size },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getDetail = id =>
|
||||||
|
request({ url: '/blade-transport/equipment-ledger/detail', method: 'get', params: { id } });
|
||||||
|
|
||||||
|
export const getNextEquipmentCode = () =>
|
||||||
|
request({ url: '/blade-transport/equipment-ledger/next-equipment-code', method: 'get' });
|
||||||
|
|
||||||
|
export const add = row =>
|
||||||
|
request({ url: '/blade-transport/equipment-ledger/submit', method: 'post', data: row });
|
||||||
|
|
||||||
|
export const update = row =>
|
||||||
|
request({ url: '/blade-transport/equipment-ledger/submit', method: 'post', data: row });
|
||||||
|
|
||||||
|
export const remove = ids =>
|
||||||
|
request({ url: '/blade-transport/equipment-ledger/remove', method: 'post', params: { ids } });
|
||||||
@@ -47,3 +47,15 @@ export const update = row => {
|
|||||||
data: row,
|
data: row,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const recognizeGeneralOcr = imageUrl => {
|
||||||
|
return request({
|
||||||
|
url: '/blade-transport/baidu-ocr/recognize-url',
|
||||||
|
method: 'post',
|
||||||
|
timeout: 60000,
|
||||||
|
params: {
|
||||||
|
imageUrl,
|
||||||
|
type: 'general',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||