Compare commits
32 Commits
zzl
...
81b2a3e99b
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
3
.workbuddy/memory/2025-07-29.md
Normal file
3
.workbuddy/memory/2025-07-29.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 2025-07-29
|
||||
|
||||
- 货物类型管理页面(cargo-type):搜索栏「类型」和「货物类型编码」字段 searchSpan 从 12 分别调整为 6 和 8,收窄 input 宽度使两字段在一排内紧凑展示
|
||||
77
.workbuddy/memory/2026-07-27.md
Normal file
77
.workbuddy/memory/2026-07-27.md
Normal file
@@ -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` 两条演示数据;用户应按需删除/替换示例再填自己的数据。
|
||||
53
.workbuddy/memory/2026-07-28.md
Normal file
53
.workbuddy/memory/2026-07-28.md
Normal file
@@ -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` 方法始终未删,仅缺按钮入口,现已恢复。
|
||||
28
.workbuddy/memory/2026-07-29.md
Normal file
28
.workbuddy/memory/2026-07-29.md
Normal file
@@ -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 不在拦截名单)。
|
||||
70
AGENTS.md
70
AGENTS.md
@@ -1,9 +1,11 @@
|
||||
# AGENTS.md
|
||||
|
||||
本文件用于指导 Codex (Codex.ai/code) 在 Saber3 代码仓库中工作时的行为规范。
|
||||
|
||||
> 本规范适用于 Saber3 前端工程的所有开发任务,为强制性条款。除非用户显式豁免,任何条目都不得忽视或删减。
|
||||
>
|
||||
> 作为 AI 助手参与本项目开发时,你必须:
|
||||
>
|
||||
> - 每次输出前深度理解 BladeX 微服务架构体系、Saber3 前端工程结构和 Vue 3 技术栈特征
|
||||
> - 当回答依赖外部知识时,先查询 Vue 3、Element Plus、Avue、Vite 等官方文档
|
||||
> - 若需求含糊,先复述已知信息并列出关键澄清问题
|
||||
@@ -21,7 +23,7 @@
|
||||
### 1.1 技术栈
|
||||
|
||||
| 技术 | 版本 | 用途 |
|
||||
| ------------------ | -------- | ------------------ |
|
||||
| --------------------- | ------- | ------------------ |
|
||||
| Vue | ^3.5.13 | 核心框架 |
|
||||
| Element Plus | ^2.10.1 | UI 组件库 |
|
||||
| @smallwei/avue | ^3.7.2 | 增强型 CRUD 组件库 |
|
||||
@@ -91,27 +93,36 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
||||
- 命名约定:列表 `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/`
|
||||
|
||||
### 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 编码)
|
||||
- Token:请求头 `Blade-Auth: bearer {token}`,支持 AES 加密模式
|
||||
- 存储:`saber3-access-token` / `saber3-refresh-token`(通过 `utils/auth.js` 管理)
|
||||
- 401 自动刷新 Token,并发请求排队等待;登录密码使用 SM2 国密加密
|
||||
|
||||
### 3.5 路由系统
|
||||
### 3.6 路由系统
|
||||
|
||||
- 静态路由:`router/page/` + `router/views/`
|
||||
- 动态路由:`avue-router.js` 将后端菜单数据转换为 Vue Router 路由
|
||||
- 多级路由自动扁平化为二级,支持 keep-alive 跨层级缓存
|
||||
- 外部链接自动转换为 iframe 路由,支持 Token 透传
|
||||
|
||||
### 3.6 权限控制
|
||||
### 3.7 权限控制
|
||||
|
||||
- 路由守卫:`permission.js` 控制登录态、锁屏、标签页
|
||||
- 按钮权限:`store.getters.permission`,格式 `{module}_{action}`(如 `dict_add`)
|
||||
- 管理员判断:`userInfo.authority.includes('admin')`
|
||||
|
||||
### 3.7 多租户
|
||||
### 3.8 多租户
|
||||
|
||||
通过 `website.tenantMode` 控制开关,管理组租户编号 `000000`,后端自动通过请求头传递租户信息。
|
||||
|
||||
@@ -124,6 +135,7 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
||||
项目同时支持 **Options API**(主流,绝大多数现有页面)和 **Composition API**(新组件可选)。
|
||||
|
||||
**选择原则**:
|
||||
|
||||
- 修改现有页面:保持该页面原有风格,不混用
|
||||
- 新建 CRUD 页面:调用 `avue-design` Skill 生成,或参考现有页面手动编写
|
||||
- 新建复杂交互页面:可使用 Composition API + `<script setup>`
|
||||
@@ -132,7 +144,7 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
||||
### 4.2 命名规范
|
||||
|
||||
| 类型 | 命名方式 | 示例 |
|
||||
| -------------- | -------------------------- | ----------------------------- |
|
||||
| ----------------------- | -------------------------- | --------------------------- |
|
||||
| 页面文件 | kebab-case | `notice.vue` |
|
||||
| 组件文件 | kebab-case 目录 + main.vue | `basic-container/main.vue` |
|
||||
| API / Option / 工具文件 | camelCase | `dict.js`、`dictbiz.js` |
|
||||
@@ -148,6 +160,21 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
||||
|
||||
- 全局 SCSS 变量通过 `styles/variables.scss` 定义,Vite 自动注入所有组件
|
||||
- 编写样式优先使用已有变量和 mixin(`styles/mixin.scss`),而非硬编码值
|
||||
- 系统主色调统一使用 `#409eff`,涉及主题变量、Element Plus 主题覆盖、按钮/链接/选中态等品牌色场景均应保持一致
|
||||
- 各页面搜索栏多行展示时,行与行之间的垂直间距统一为 `8px`
|
||||
- 搜索组件 label 宽度统一不小于 `160px`,label 文本不得换行。
|
||||
- 搜索组件内部 padding 统一为上、左右 `12px`,底部 `4px`。
|
||||
- 搜索组件需展示轻量阴影,统一使用 `0 2px 8px rgba(0, 0, 0, 0.06)`,不得被局部卡片去样式规则覆盖。
|
||||
- 表格线条颜色需统一使用 `#EFF1F7`,包括表格外边框、单元格分割线和固定列边线。
|
||||
- 表格偶数行需统一使用 `#FAFAFA` 背景色,固定列单元格必须与对应行背景保持一致。
|
||||
- 表格操作列同时展示“查看、编辑、删除”等三个按钮时,操作列宽度统一不小于 `220px`,禁止出现按钮裁切或显示不全。
|
||||
- `.avue-crud__header` 顶部间距统一为 `12px`。
|
||||
- 分页组件整体靠右展示,必须展示接口返回的数据总条数,`X条/页` 的页容量选择器必须放在总条数右侧。
|
||||
- `.basic-container__card` 的直接子级 `.el-card__body` 内边距统一去除,保持 `padding: 0`
|
||||
- 所有 CRUD 页面搜索栏需与下方表格拆分为独立区域,两者垂直间距统一为 `8px`。
|
||||
- 所有 CRUD 页面搜索栏操作按钮(搜索、清空等)需在搜索表单下方独占一行并放置在最右侧;搜索条件超过一行时必须显示“展开/折叠”,默认折叠且仅展示一行搜索条件;一行布局默认展示 4 个筛选项,Avue 配置统一使用 `searchIndex: 4`。
|
||||
- 所有 CRUD 页面中新增、批量导入等顶部按钮组行不需要背景色,应保持透明背景。
|
||||
- 所有 `.basic-container__card` 内的 Avue 内层 `el-card` 需去卡片化,仅保留外层容器卡片样式,禁止出现二级卡片边框、阴影或额外内边距。
|
||||
|
||||
### 4.5 全局注册组件
|
||||
|
||||
@@ -165,8 +192,15 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
||||
|
||||
### 4.8 表单布局规范
|
||||
|
||||
- 新增/编辑弹窗中的表单项,若 `label` 文案超过 4 个字,`label` 必须单独占一行展示,表单控件另起一行,避免长标签挤压输入控件或换行错位。
|
||||
- 使用 Avue 配置、Element Plus 表单或自定义弹窗表单时均需遵守该规则;可通过 `label-position="top"`、自定义 `formslot`、局部样式或单项布局调整实现。
|
||||
- 新增、编辑弹窗中的表单 `label` 默认必须位于控件左侧,Avue 配置统一使用 `labelPosition: 'right'` 与 `labelWidth: 'auto'`,Element Plus 表单统一使用 `label-position="right"` 与 `label-width="auto"`。
|
||||
- 新增、编辑弹窗中的表单 `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 审计字段展示规范
|
||||
|
||||
@@ -175,6 +209,24 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
||||
- “更新人”列统一使用 `prop: 'updateUserName'`,并通过 `src/utils/audit.js` 中的 `formatUpdateUserName(row)` 做兼容兜底。
|
||||
- 表单提交仍保留后端审计字段机制,前端不得手动提交或覆盖 `createUser`、`updateUser`。
|
||||
|
||||
### 4.10 表格排序规范
|
||||
|
||||
- 表格的排序默认按照创建时间降序;分页列表应由后端默认按 `create_time DESC` 返回,前端不得在无明确业务要求时覆盖为其它默认排序。
|
||||
|
||||
### 4.11 搜索顺序规范
|
||||
|
||||
- 当用户已明确列出筛选字段时,搜索栏必须严格按照用户给出的字段清单和顺序展示;不得自行新增、保留、推断或迁移未列出的筛选项。
|
||||
- 新增/编辑表单字段、表格列字段和搜索筛选字段必须分开理解;除非用户明确要求同步,禁止把表单字段或表格字段擅自加入搜索栏。
|
||||
- Avue 搜索栏的字段展示顺序按 `searchOrder` **倒序** 渲染,配置时数值越大越靠前。
|
||||
|
||||
### 4.12 日期区间搜索规范
|
||||
|
||||
- 当同一业务维度的筛选条件是开始/结束日期或时间,并且接口按区间查询时,前端搜索栏必须合并为一个 `searchRange: true` 的区间字段展示。
|
||||
- 区间字段在页面层统一归一化为后端所需的起止参数,禁止同时保留两个独立的“开始/结束”搜索项。
|
||||
- 若用户已明确要求展示区间样式,必须严格按区间控件实现,不得退回为两个普通日期控件。
|
||||
- 修改搜索项顺序时必须同步调整 `searchOrder`,不要依赖 `column` 数组前后位置来控制搜索栏顺序。
|
||||
- `searchIndex: 4` 仅控制首屏展示数量,不影响 `searchOrder` 的优先级。
|
||||
|
||||
---
|
||||
|
||||
## 5. 新功能开发流程
|
||||
@@ -226,7 +278,7 @@ pnpm run serve # 预览构建产物
|
||||
项目采用 **Gitmoji** 风格,中文描述:
|
||||
|
||||
| Emoji | 代码 | 场景 |
|
||||
| ---------- | ------------ | ---------------- |
|
||||
| ---------- | ------------ | ------------------ |
|
||||
| :sparkles: | `:sparkles:` | 新增功能、优化增强 |
|
||||
| :bug: | `:bug:` | 修复 Bug |
|
||||
| :zap: | `:zap:` | 性能优化、问题修复 |
|
||||
|
||||
BIN
import-cargo-type/货物类型导入_一级.xlsx
Normal file
BIN
import-cargo-type/货物类型导入_一级.xlsx
Normal file
Binary file not shown.
BIN
import-cargo-type/货物类型导入_二级.xlsx
Normal file
BIN
import-cargo-type/货物类型导入_二级.xlsx
Normal file
Binary file not shown.
BIN
import-cargo-type/货物类型导入_单文件(一级在前).xlsx
Normal file
BIN
import-cargo-type/货物类型导入_单文件(一级在前).xlsx
Normal file
Binary file not shown.
@@ -13,14 +13,13 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
<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/avue/iconfont.css" />
|
||||
<link rel="stylesheet" href="/iconfont/saber/iconfont.css" />
|
||||
<link rel="stylesheet" href="/iconfont/common/iconfont.css" />
|
||||
<link rel="stylesheet" href="/css/loading.css" />
|
||||
<link rel="stylesheet" href="/css/saber.css" />
|
||||
<title>TMS物流运输管理系统</title>
|
||||
<title>运输板块数字化管理平台</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
12
package.json
12
package.json
@@ -9,24 +9,28 @@
|
||||
"serve": "vite preview --host"
|
||||
},
|
||||
"dependencies": {
|
||||
"element-plus": "^2.14.2",
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@smallwei/avue": "~3.9.2",
|
||||
"avue-plugin-ueditor": "^1.0.4",
|
||||
"@open-file-viewer/core": "^0.1.27",
|
||||
"@open-file-viewer/vue": "^0.1.27",
|
||||
"@saber/nf-design-base-elp": "^1.3.0",
|
||||
"@saber/nf-form-design-elp": "^1.6.1",
|
||||
"@saber/nf-form-elp": "^1.6.3",
|
||||
"@smallwei/avue": "~3.9.2",
|
||||
"animate.css": "^4.1.1",
|
||||
"avue-plugin-ueditor": "^1.0.4",
|
||||
"axios": "^1.13.5",
|
||||
"codemirror": "^5.65.21",
|
||||
"crypto-js": "^4.1.1",
|
||||
"dayjs": "^1.10.6",
|
||||
"disable-devtool": "^0.3.8",
|
||||
"element-plus": "^2.14.2",
|
||||
"exceljs": "^4.4.0",
|
||||
"highlight.js": "^11.9.0",
|
||||
"js-base64": "^3.7.4",
|
||||
"js-cookie": "^3.0.0",
|
||||
"js-md5": "^0.7.3",
|
||||
"nprogress": "^0.2.0",
|
||||
"pdfjs-dist": "^6.1.200",
|
||||
"sm-crypto": "^0.4.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^11.4.2",
|
||||
@@ -37,8 +41,8 @@
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.4",
|
||||
"@vue/compiler-sfc": "^3.4.27",
|
||||
"sass": "^1.85.1",
|
||||
"prettier": "^2.8.7",
|
||||
"sass": "^1.85.1",
|
||||
"terser": "^5.31.1",
|
||||
"unplugin-auto-import": "^0.11.2",
|
||||
"vite": "^5.4.19",
|
||||
|
||||
1173
pnpm-lock.yaml
generated
1173
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
BIN
public/cargo-type-import-template.xlsx
Normal file
BIN
public/cargo-type-import-template.xlsx
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 88 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 113 KiB |
73
src/api/base/cargo-type.js
Normal file
73
src/api/base/cargo-type.js
Normal file
@@ -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,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
8
src/api/business/common-cargo.js
Normal file
8
src/api/business/common-cargo.js
Normal file
@@ -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;
|
||||
8
src/api/business/common-route.js
Normal file
8
src/api/business/common-route.js
Normal file
@@ -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;
|
||||
103
src/api/business/common.js
Normal file
103
src/api/business/common.js
Normal file
@@ -0,0 +1,103 @@
|
||||
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,
|
||||
},
|
||||
});
|
||||
},
|
||||
reassign(id) {
|
||||
return request({
|
||||
url: `${baseUrl}/reassign`,
|
||||
method: 'post',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
},
|
||||
batchComplete(ids) {
|
||||
return request({
|
||||
url: `${baseUrl}/batch-complete`,
|
||||
method: 'post',
|
||||
params: {
|
||||
ids,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
78
src/api/business/contract-manage.js
Normal file
78
src/api/business/contract-manage.js
Normal file
@@ -0,0 +1,78 @@
|
||||
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 withdraw = id =>
|
||||
request({
|
||||
url: `${baseUrl}/withdraw`,
|
||||
method: 'post',
|
||||
params: { id },
|
||||
});
|
||||
|
||||
export const startChange = (id, reason) =>
|
||||
request({
|
||||
url: `${baseUrl}/start-change`,
|
||||
method: 'post',
|
||||
params: {
|
||||
id,
|
||||
changeContent: reason,
|
||||
changeReason: reason,
|
||||
},
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
11
src/api/business/process-config.js
Normal file
11
src/api/business/process-config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { createCrudApi } from './common';
|
||||
|
||||
const api = createCrudApi('/blade-transport/process-config');
|
||||
|
||||
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 enable = api.enable;
|
||||
export const disable = api.disable;
|
||||
63
src/api/business/project-apply.js
Normal file
63
src/api/business/project-apply.js
Normal file
@@ -0,0 +1,63 @@
|
||||
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 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 startChange = (id, reason) =>
|
||||
request({
|
||||
url: `${baseUrl}/start-change`,
|
||||
method: 'post',
|
||||
params: {
|
||||
id,
|
||||
changeContent: reason,
|
||||
changeReason: reason,
|
||||
},
|
||||
});
|
||||
9
src/api/business/shipping-template.js
Normal file
9
src/api/business/shipping-template.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createCrudApi } from './common';
|
||||
|
||||
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;
|
||||
45
src/api/business/temporary-credit-limit.js
Normal file
45
src/api/business/temporary-credit-limit.js
Normal file
@@ -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 },
|
||||
});
|
||||
11
src/api/business/transport-plan.js
Normal file
11
src/api/business/transport-plan.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { createCrudApi } from './common';
|
||||
|
||||
const api = createCrudApi('/blade-transport/transport-plan');
|
||||
|
||||
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 cancel = api.cancel;
|
||||
export const complete = api.complete;
|
||||
13
src/api/business/waybill-manage.js
Normal file
13
src/api/business/waybill-manage.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { createCrudApi } from './common';
|
||||
|
||||
const api = createCrudApi('/blade-transport/waybill-manage');
|
||||
|
||||
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 cancel = api.cancel;
|
||||
export const reassign = api.reassign;
|
||||
export const complete = api.complete;
|
||||
export const batchComplete = api.batchComplete;
|
||||
@@ -13,3 +13,15 @@ export const exportBlob = (url, params) => {
|
||||
responseType: 'blob',
|
||||
});
|
||||
};
|
||||
|
||||
export const importBlob = (url, file) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data: formData,
|
||||
responseType: 'blob',
|
||||
timeout: 60000,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -48,6 +48,11 @@ export default {
|
||||
|
||||
&__card {
|
||||
width: 100%;
|
||||
background: none;
|
||||
|
||||
> .el-card__body {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div class="cron-config-area">
|
||||
<!-- 每天模式 -->
|
||||
<div v-if="cronMode === 'daily'" class="mode-content">
|
||||
<el-form :model="dailyConfig" label-width="100px" size="default">
|
||||
<el-form :model="dailyConfig" label-width="auto" size="default">
|
||||
<el-form-item label="执行时间">
|
||||
<el-time-picker
|
||||
v-model="dailyConfig.time"
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<!-- 每周模式 -->
|
||||
<div v-if="cronMode === 'weekly'" class="mode-content">
|
||||
<el-form :model="weeklyConfig" label-width="100px" size="default">
|
||||
<el-form :model="weeklyConfig" label-width="auto" size="default">
|
||||
<el-form-item label="星期选择">
|
||||
<el-checkbox-group v-model="weeklyConfig.weekDays" @change="generateCron" :disabled="disabled">
|
||||
<el-checkbox label="1">周一</el-checkbox>
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
<!-- 每月模式 -->
|
||||
<div v-if="cronMode === 'monthly'" class="mode-content">
|
||||
<el-form :model="monthlyConfig" label-width="100px" size="default">
|
||||
<el-form :model="monthlyConfig" label-width="auto" size="default">
|
||||
<el-form-item label="日期选择">
|
||||
<el-select
|
||||
v-model="monthlyConfig.days"
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
<!-- 自定义模式 -->
|
||||
<div v-if="cronMode === 'custom'" class="mode-content">
|
||||
<el-form :model="customConfig" label-width="100px" size="default">
|
||||
<el-form :model="customConfig" label-width="auto" size="default">
|
||||
<el-form-item label="秒">
|
||||
<el-input
|
||||
v-model="customConfig.second"
|
||||
@@ -161,7 +161,7 @@
|
||||
</div>
|
||||
|
||||
<div class="cron-result">
|
||||
<el-form label-width="120px">
|
||||
<el-form label-width="auto">
|
||||
<el-form-item label="Cron 表达式">
|
||||
<el-input
|
||||
v-model="cronExpression"
|
||||
@@ -567,7 +567,7 @@ export default {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
:deep(.el-checkbox) {
|
||||
|
||||
62
src/components/empty-pagination/main.vue
Normal file
62
src/components/empty-pagination/main.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div v-if="isEmptyTotal" class="empty-pagination avue-crud__pagination">
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="page.currentPage"
|
||||
:page-size="pageSize"
|
||||
:page-sizes="pageSizes"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'EmptyPagination',
|
||||
props: {
|
||||
page: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ['size-change', 'current-change', 'load'],
|
||||
computed: {
|
||||
isEmptyTotal() {
|
||||
return Number(this.page.total || 0) === 0;
|
||||
},
|
||||
pageSize() {
|
||||
return this.page.pageSize || 10;
|
||||
},
|
||||
pageSizes() {
|
||||
return this.page.pageSizes || [10, 20, 50, 100];
|
||||
},
|
||||
total() {
|
||||
return Number(this.page.total || 0);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleSizeChange(pageSize) {
|
||||
this.page.currentPage = 1;
|
||||
this.page.pageSize = pageSize;
|
||||
this.$emit('size-change', pageSize);
|
||||
this.$emit('load');
|
||||
},
|
||||
handleCurrentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
this.$emit('current-change', currentPage);
|
||||
this.$emit('load');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.empty-pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
89
src/components/image-upload-field/main.vue
Normal file
89
src/components/image-upload-field/main.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<el-form-item v-if="mode === 'form'" :label="label" :prop="prop">
|
||||
<el-upload
|
||||
:class="[`${classPrefix}-uploader`, { [`${classPrefix}-uploader--large`]: large }]"
|
||||
action="/api/blade-resource/oss/endpoint/put-file"
|
||||
name="file"
|
||||
:headers="headers"
|
||||
:show-file-list="false"
|
||||
:disabled="readonly"
|
||||
:before-upload="beforeUpload"
|
||||
:on-success="handleSuccess"
|
||||
>
|
||||
<img v-if="value" :src="value" :class="`${classPrefix}-uploader__image`" />
|
||||
<div v-else :class="`${classPrefix}-uploader__empty`">{{ emptyText }}</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item v-else :prop="prop" :class="`${classPrefix}-upload-item`">
|
||||
<div :class="[`${classPrefix}-upload-card`, { [`${classPrefix}-upload-card--large`]: large }]">
|
||||
<div v-if="label" :class="`${classPrefix}-upload-card__head`">
|
||||
<span :class="`${classPrefix}-upload-card__label`">{{ label }}</span>
|
||||
<span :class="`${classPrefix}-upload-card__action`">{{ actionText }}</span>
|
||||
</div>
|
||||
<el-upload
|
||||
:class="`${classPrefix}-upload-card__uploader`"
|
||||
action="/api/blade-resource/oss/endpoint/put-file"
|
||||
name="file"
|
||||
:headers="headers"
|
||||
:show-file-list="false"
|
||||
:disabled="readonly"
|
||||
:before-upload="beforeUpload"
|
||||
:on-success="handleSuccess"
|
||||
>
|
||||
<img v-if="value" :src="value" :class="`${classPrefix}-upload-card__image`" />
|
||||
<div v-else :class="`${classPrefix}-upload-card__empty`">{{ emptyText }}</div>
|
||||
</el-upload>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ImageUploadField',
|
||||
props: {
|
||||
label: String,
|
||||
prop: String,
|
||||
value: String,
|
||||
readonly: Boolean,
|
||||
headers: Object,
|
||||
large: Boolean,
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'form',
|
||||
},
|
||||
classPrefix: {
|
||||
type: String,
|
||||
default: 'driver',
|
||||
},
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: '上传证件图片',
|
||||
},
|
||||
actionText: {
|
||||
type: String,
|
||||
default: '点击上传',
|
||||
},
|
||||
},
|
||||
emits: ['success'],
|
||||
methods: {
|
||||
handleSuccess(res) {
|
||||
if (res.code === 200 && res.data) {
|
||||
this.$emit('success', res.data.link || res.data.url || res.data.domain || '');
|
||||
} else {
|
||||
this.$message.error(res.msg || '上传失败');
|
||||
}
|
||||
},
|
||||
beforeUpload(file) {
|
||||
const validType = ['image/jpeg', 'image/png', 'image/jpg', 'image/bmp'].includes(file.type);
|
||||
const validSize = file.size / 1024 / 1024 < 5;
|
||||
if (!validType) {
|
||||
this.$message.error('仅支持 JPG、PNG、BMP 图片');
|
||||
}
|
||||
if (!validSize) {
|
||||
this.$message.error('图片大小不能超过 5MB');
|
||||
}
|
||||
return validType && validSize;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -8,7 +8,7 @@
|
||||
:show-close="false"
|
||||
width="20%"
|
||||
>
|
||||
<el-form :model="form" ref="form" label-width="80px">
|
||||
<el-form :model="form" ref="form" label-width="auto">
|
||||
<el-form-item v-if="tenantMode" label="租户编号">
|
||||
<el-input v-model="form.tenantId" placeholder="请输入租户编号"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
364
src/components/vehicle-attachment-upload/main.vue
Normal file
364
src/components/vehicle-attachment-upload/main.vue
Normal file
@@ -0,0 +1,364 @@
|
||||
<template>
|
||||
<div class="vehicle-attachment-upload">
|
||||
<el-upload
|
||||
:action="uploadAction"
|
||||
name="file"
|
||||
:headers="uploadHeaders"
|
||||
:file-list="fileList"
|
||||
:accept="acceptText"
|
||||
:multiple="multiple"
|
||||
:limit="limit"
|
||||
:disabled="readonly"
|
||||
:show-file-list="true"
|
||||
:on-success="handleSuccess"
|
||||
:on-error="handleError"
|
||||
:on-remove="handleRemove"
|
||||
:on-preview="handlePreview"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<el-button v-if="!readonly" type="primary" plain :icon="buttonIcon">
|
||||
{{ buttonText }}
|
||||
</el-button>
|
||||
<template #tip>
|
||||
<div v-if="showTip && tipText && !readonly" class="el-upload__tip">
|
||||
{{ tipText }}
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<span v-if="readonly && fileList.length === 0" class="vehicle-attachment-upload__empty">
|
||||
-
|
||||
</span>
|
||||
|
||||
<el-dialog
|
||||
v-model="previewVisible"
|
||||
:title="previewFile.name || '附件预览'"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
width="90%"
|
||||
top="4vh"
|
||||
class="vehicle-attachment-upload__viewer-dialog"
|
||||
>
|
||||
<open-file-viewer
|
||||
v-if="previewVisible && previewSource"
|
||||
:file="previewSource"
|
||||
:file-name="previewFile.name"
|
||||
:mime-type="previewFile.mimeType"
|
||||
width="100%"
|
||||
height="72vh"
|
||||
fit="contain"
|
||||
theme="auto"
|
||||
locale="zh-CN"
|
||||
:toolbar="viewerToolbar"
|
||||
:plugins="viewerPlugins"
|
||||
@unsupported="handleUnsupported"
|
||||
@error="handleViewerError"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { OpenFileViewer } from '@open-file-viewer/vue';
|
||||
import {
|
||||
fallbackPlugin,
|
||||
imagePlugin,
|
||||
officePlugin,
|
||||
pdfPlugin,
|
||||
textPlugin,
|
||||
} from '@open-file-viewer/core';
|
||||
import '@open-file-viewer/core/style.css';
|
||||
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
||||
import { baseUrl } from '@/config/env';
|
||||
import website from '@/config/website';
|
||||
import { getToken } from '@/utils/auth';
|
||||
|
||||
const MIME_MAP = {
|
||||
jpg: 'image/jpeg',
|
||||
jpeg: 'image/jpeg',
|
||||
png: 'image/png',
|
||||
gif: 'image/gif',
|
||||
bmp: 'image/bmp',
|
||||
webp: 'image/webp',
|
||||
pdf: 'application/pdf',
|
||||
doc: 'application/msword',
|
||||
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
xls: 'application/vnd.ms-excel',
|
||||
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
ppt: 'application/vnd.ms-powerpoint',
|
||||
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
txt: 'text/plain',
|
||||
};
|
||||
|
||||
const viewerPlugins = [
|
||||
imagePlugin(),
|
||||
pdfPlugin({
|
||||
workerSrc: pdfWorkerSrc,
|
||||
useFetchData: true,
|
||||
}),
|
||||
officePlugin({
|
||||
pdf: {
|
||||
workerSrc: pdfWorkerSrc,
|
||||
useFetchData: true,
|
||||
},
|
||||
}),
|
||||
textPlugin(),
|
||||
fallbackPlugin(),
|
||||
];
|
||||
|
||||
export default {
|
||||
name: 'VehicleAttachmentUpload',
|
||||
components: {
|
||||
OpenFileViewer,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [Array, String, Object],
|
||||
default: () => [],
|
||||
},
|
||||
action: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
accept: {
|
||||
type: [String, Array],
|
||||
default: '',
|
||||
},
|
||||
fileTypes: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 20,
|
||||
},
|
||||
maxSize: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
buttonText: {
|
||||
type: String,
|
||||
default: '上传附件',
|
||||
},
|
||||
buttonIcon: {
|
||||
type: String,
|
||||
default: 'el-icon-upload',
|
||||
},
|
||||
tip: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
showTip: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue', 'change', 'success'],
|
||||
data() {
|
||||
return {
|
||||
fileList: [],
|
||||
previewVisible: false,
|
||||
previewFile: {},
|
||||
previewSource: '',
|
||||
viewerToolbar: {
|
||||
download: true,
|
||||
fullscreen: true,
|
||||
print: true,
|
||||
rotate: true,
|
||||
zoom: true,
|
||||
},
|
||||
viewerPlugins,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
uploadAction() {
|
||||
return this.action || `${baseUrl}/blade-resource/oss/endpoint/put-file`;
|
||||
},
|
||||
uploadHeaders() {
|
||||
return {
|
||||
[website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
acceptedList() {
|
||||
const types = this.fileTypes.length ? this.fileTypes : this.accept;
|
||||
if (Array.isArray(types)) {
|
||||
return types.map(item => String(item).trim()).filter(Boolean);
|
||||
}
|
||||
return String(types || '')
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean);
|
||||
},
|
||||
acceptText() {
|
||||
return this.acceptedList.join(',');
|
||||
},
|
||||
tipText() {
|
||||
if (this.tip) return this.tip;
|
||||
const typeText = this.acceptText || '所有类型';
|
||||
const sizeText = this.maxSize > 0 ? `,单文件不超过 ${this.maxSize}MB` : '';
|
||||
return `支持上传 ${typeText}${sizeText}`;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
handler(value) {
|
||||
this.fileList = this.toUploadList(value);
|
||||
},
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toUploadList(value) {
|
||||
const list = this.parseValue(value);
|
||||
return list.map((item, index) => {
|
||||
const url = this.getFileUrl(item);
|
||||
return {
|
||||
...item,
|
||||
uid: item.uid || `${url || item.name || 'file'}-${index}`,
|
||||
name: item.name || this.getFileName(url) || '附件',
|
||||
url,
|
||||
status: 'success',
|
||||
};
|
||||
});
|
||||
},
|
||||
parseValue(value) {
|
||||
if (!value) return [];
|
||||
if (Array.isArray(value)) return value.filter(Boolean);
|
||||
if (typeof value === 'object') return [value];
|
||||
try {
|
||||
const list = JSON.parse(value);
|
||||
return Array.isArray(list) ? list : [];
|
||||
} catch (error) {
|
||||
return String(value)
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean)
|
||||
.map(item => ({
|
||||
name: this.getFileName(item),
|
||||
url: item,
|
||||
}));
|
||||
}
|
||||
},
|
||||
getFileUrl(file) {
|
||||
return file.url || file.link || file.src || file.domain || '';
|
||||
},
|
||||
getFileName(url) {
|
||||
if (!url) return '';
|
||||
const path = String(url).split('?')[0];
|
||||
return decodeURIComponent(path.substring(path.lastIndexOf('/') + 1));
|
||||
},
|
||||
getExtension(file) {
|
||||
const name = String(file.name || this.getFileUrl(file) || '').split('?')[0];
|
||||
const index = name.lastIndexOf('.');
|
||||
return index >= 0 ? name.substring(index + 1).toLowerCase() : '';
|
||||
},
|
||||
beforeUpload(file) {
|
||||
if (!this.validFileType(file)) {
|
||||
this.$message.warning('文件类型不符合上传要求');
|
||||
return false;
|
||||
}
|
||||
if (this.maxSize > 0 && file.size / 1024 / 1024 > this.maxSize) {
|
||||
this.$message.warning(`单文件大小不能超过 ${this.maxSize}MB`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
validFileType(file) {
|
||||
if (!this.acceptedList.length) return true;
|
||||
const fileName = String(file.name || '').toLowerCase();
|
||||
const mimeType = String(file.type || '').toLowerCase();
|
||||
return this.acceptedList.some(type => {
|
||||
const value = String(type).toLowerCase();
|
||||
if (value === '*/*') return true;
|
||||
if (value.startsWith('.')) return fileName.endsWith(value);
|
||||
if (value.endsWith('/*')) return mimeType.startsWith(value.slice(0, -1));
|
||||
if (value.includes('/')) return mimeType === value;
|
||||
return fileName.endsWith(`.${value}`);
|
||||
});
|
||||
},
|
||||
handleSuccess(response, file, files) {
|
||||
if (!response || response.success === false || (response.code && response.code !== 200)) {
|
||||
this.$message.error((response && response.msg) || '上传失败');
|
||||
return;
|
||||
}
|
||||
this.emitUploadFiles(this.multiple ? files : [file]);
|
||||
this.$emit('success', this.normalizeUploadFile(file));
|
||||
this.$message.success('上传成功');
|
||||
},
|
||||
handleError() {
|
||||
this.$message.error('上传失败');
|
||||
},
|
||||
handleRemove(file, files) {
|
||||
if (this.readonly) return false;
|
||||
this.emitUploadFiles(files);
|
||||
return true;
|
||||
},
|
||||
emitUploadFiles(files) {
|
||||
const list = files
|
||||
.filter(file => file.status === 'success' || this.getFileUrl(file))
|
||||
.map(this.normalizeUploadFile)
|
||||
.filter(item => item.url);
|
||||
this.$emit('update:modelValue', list);
|
||||
this.$emit('change', list);
|
||||
},
|
||||
normalizeUploadFile(file) {
|
||||
const data = (file.response && file.response.data) || file.data || file;
|
||||
const url = data.link || data.url || data.domain || file.url || '';
|
||||
const name = data.name || data.originalName || file.name || this.getFileName(url) || '附件';
|
||||
const extension = this.getExtension({ name, url });
|
||||
return {
|
||||
...data,
|
||||
uid: file.uid || data.uid,
|
||||
name,
|
||||
url,
|
||||
link: data.link || url,
|
||||
size: data.size || data.attachSize || file.size || '',
|
||||
extension,
|
||||
mimeType: data.mimeType || data.contentType || file.raw?.type || MIME_MAP[extension] || '',
|
||||
};
|
||||
},
|
||||
handlePreview(file) {
|
||||
const previewFile = this.normalizeUploadFile(file);
|
||||
if (!previewFile.url) {
|
||||
this.$message.warning('附件地址为空,无法预览');
|
||||
return;
|
||||
}
|
||||
this.previewFile = previewFile;
|
||||
this.previewSource = previewFile.url;
|
||||
this.previewVisible = true;
|
||||
},
|
||||
handleUnsupported() {
|
||||
this.$message.warning('当前文件暂不支持在线预览');
|
||||
},
|
||||
handleViewerError() {
|
||||
this.$message.error('附件预览失败');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vehicle-attachment-upload {
|
||||
width: 100%;
|
||||
|
||||
:deep(.el-upload-list__item-name) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__empty {
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.vehicle-attachment-upload__viewer-dialog .el-dialog__body) {
|
||||
padding: 12px 16px 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -5,7 +5,7 @@ export default {
|
||||
title: '运输板块数字化管理平台',
|
||||
logo: 'X',
|
||||
key: 'saber', //配置主键,目前用于存储
|
||||
indexTitle: 'TMS物流运输管理系统',
|
||||
indexTitle: '运输板块数字化管理平台',
|
||||
clientId: 'saber3', // 客户端id
|
||||
clientSecret: 'saber3_secret', // 客户端密钥
|
||||
tenantMode: true, // 是否开启租户模式
|
||||
@@ -53,7 +53,7 @@ export default {
|
||||
//水印配置
|
||||
watermark: {
|
||||
mode: false,
|
||||
text: 'TMS物流运输管理系统',
|
||||
text: '运输板块数字化管理平台',
|
||||
},
|
||||
//oauth2配置
|
||||
oauth2: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default {
|
||||
title: 'TMS物流运输管理系统',
|
||||
title: '运输板块数字化管理平台',
|
||||
confirmTip: '提示',
|
||||
logoutTip: '退出系统, 是否继续?',
|
||||
submitText: '确定',
|
||||
@@ -79,7 +79,7 @@ export default {
|
||||
},
|
||||
login: {
|
||||
title: '登录 ',
|
||||
info: 'TMS物流运输管理系统',
|
||||
info: '运输板块数字化管理平台',
|
||||
tenantId: '请输入租户ID',
|
||||
name: '请输入姓名',
|
||||
username: '请输入账号',
|
||||
|
||||
18
src/main.js
18
src/main.js
@@ -13,6 +13,8 @@ import '@smallwei/avue/lib/index.css';
|
||||
import avueUeditor from 'avue-plugin-ueditor';
|
||||
import crudCommon from '@/mixins/crud.js';
|
||||
import { getScreen, findColumn } from './utils/util';
|
||||
import { setupPaginationEnhancer } from './utils/pagination';
|
||||
import { setupDialogFormLabelAlign } from './utils/dialog-form-label';
|
||||
import './permission';
|
||||
import error from './error';
|
||||
import App from './App.vue';
|
||||
@@ -27,6 +29,8 @@ import codeEditor from './components/code-editor/main.vue';
|
||||
import cronEditor from './components/cron-editor/main.vue';
|
||||
import basicBlock from './components/basic-block/main.vue';
|
||||
import basicContainer from './components/basic-container/main.vue';
|
||||
import emptyPagination from './components/empty-pagination/main.vue';
|
||||
import vehicleAttachmentUpload from './components/vehicle-attachment-upload/main.vue';
|
||||
import thirdRegister from './components/third-register/main.vue';
|
||||
import flowDesign from './components/flow-design/main.vue';
|
||||
import flowDesignStep from './components/flow-design-step/main.vue';
|
||||
@@ -45,6 +49,8 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
}
|
||||
app.component('basicContainer', basicContainer);
|
||||
app.component('basicBlock', basicBlock);
|
||||
app.component('emptyPagination', emptyPagination);
|
||||
app.component('vehicleAttachmentUpload', vehicleAttachmentUpload);
|
||||
app.component('highlight', highlight);
|
||||
app.component('codeEditor', codeEditor);
|
||||
app.component('cronEditor', cronEditor);
|
||||
@@ -70,6 +76,16 @@ app.use(ElementPlus, {
|
||||
app.use(Avue, {
|
||||
axios,
|
||||
calcHeight: 10,
|
||||
crudOption: {
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchLabelWidth: 160,
|
||||
searchMenuSpan: 24,
|
||||
searchMenuPosition: 'right',
|
||||
menuWidth: 220,
|
||||
},
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
locale: language,
|
||||
i18n: (key, options) => i18n.global.t(key, options),
|
||||
});
|
||||
@@ -79,3 +95,5 @@ app.use(VueClipboard, {
|
||||
appendToBody: true, // 这可以帮助解决一些更复杂的使用场景下的问题
|
||||
});
|
||||
app.mount('#app');
|
||||
setupPaginationEnhancer();
|
||||
setupDialogFormLabelAlign();
|
||||
|
||||
187
src/option/base/cargo-type.js
Normal file
187
src/option/base/cargo-type.js
Normal file
@@ -0,0 +1,187 @@
|
||||
const typeLevelDic = [
|
||||
{ label: '一级货物类型', value: 1 },
|
||||
{ label: '二级货物类型', value: 2 },
|
||||
];
|
||||
|
||||
const validateCargoName = (rule, value, callback) => {
|
||||
const v = String(value || '').trim();
|
||||
if (!v) {
|
||||
callback(new Error('请输入货物类型名称'));
|
||||
} else if (v.length > 50) {
|
||||
callback(new Error('货物类型名称不能超过50字符'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validateRemark = (rule, value, callback) => {
|
||||
if (String(value || '').length > 200) {
|
||||
callback(new Error('备注不能超过200字'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
export const getCargoTypeOption = ctx => ({
|
||||
height: 'auto',
|
||||
calcHeight: 22,
|
||||
dialogWidth: 760,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchIcon: true,
|
||||
searchLabelWidth: 50,
|
||||
searchMenuSpan: 10,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
indexWidth: 70,
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
editBtn: false,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 160,
|
||||
column: [
|
||||
{
|
||||
label: '类型',
|
||||
prop: 'typeLevel',
|
||||
type: 'radio',
|
||||
dataType: 'number',
|
||||
dicData: typeLevelDic,
|
||||
search: true,
|
||||
searchSpan: 6,
|
||||
value: 2,
|
||||
span: 24,
|
||||
minWidth: 120,
|
||||
editDisabled: true,
|
||||
rules: [{ required: true, message: '请选择货物类型级别', trigger: 'change' }],
|
||||
change: ({ value }) => ctx.handleTypeLevelChange(value),
|
||||
},
|
||||
{
|
||||
label: '上级货物类型',
|
||||
prop: 'parentId',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
display: false,
|
||||
span: 12,
|
||||
rules: [{ required: true, message: '请选择上级货物类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '上级货物类型',
|
||||
prop: 'parentCargoName',
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
showOverflowTooltip: true,
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
label: '上级货物编码',
|
||||
prop: 'parentCargoCode',
|
||||
slot: true,
|
||||
readonly: true,
|
||||
editDisabled: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
span: 12,
|
||||
minWidth: 160,
|
||||
rules: [{ required: true, message: '请选择上级货物类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '货物类型',
|
||||
prop: 'cargoName',
|
||||
minWidth: 150,
|
||||
maxlength: 50,
|
||||
showWordLimit: true,
|
||||
showOverflowTooltip: true,
|
||||
rules: [{ validator: validateCargoName, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '货物类型编码',
|
||||
prop: 'cargoCode',
|
||||
minWidth: 140,
|
||||
search: true,
|
||||
searchSpan: 6,
|
||||
maxlength: 4,
|
||||
editDisabled: true,
|
||||
rules: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
const form = ctx.form || {};
|
||||
const code = String(value || '').trim();
|
||||
const pattern = form.typeLevel === 1 ? /^\d{2}$/ : /^\d{4}$/;
|
||||
if (!code) {
|
||||
callback(new Error('请输入货物类型编码'));
|
||||
} else if (!pattern.test(code)) {
|
||||
callback(new Error('货物类型编码格式不正确'));
|
||||
} else if (
|
||||
form.typeLevel === 2 &&
|
||||
form.parentCargoCode &&
|
||||
!code.startsWith(form.parentCargoCode)
|
||||
) {
|
||||
callback(new Error('二级编码前2位必须与上级货物类型编码一致'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
change: ({ value }) => ctx.handleCargoCodeChange(value),
|
||||
},
|
||||
{
|
||||
label: '创建人',
|
||||
prop: 'createUserName',
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
minWidth: 180,
|
||||
maxlength: 200,
|
||||
showWordLimit: true,
|
||||
overHidden: true,
|
||||
showOverflowTooltip: true,
|
||||
rules: [{ validator: validateRemark, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '更新时间',
|
||||
prop: 'updateTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'createTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -5,13 +5,18 @@ export const addressTypeOptions = [
|
||||
{ label: '空港机场', value: '空港机场' },
|
||||
];
|
||||
|
||||
export const createOption = ({ validateLongitude, validateLatitude }) => ({
|
||||
export const createOption = () => ({
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
@@ -24,12 +29,64 @@ export const createOption = ({ validateLongitude, validateLatitude }) => ({
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
column: [
|
||||
{
|
||||
label: '类型',
|
||||
prop: 'addressType',
|
||||
type: 'select',
|
||||
formslot: true,
|
||||
search: true,
|
||||
span: 24,
|
||||
dicData: addressTypeOptions,
|
||||
rules: [{ required: true, message: '请选择类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '港口',
|
||||
prop: 'portId',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
display: false,
|
||||
span: 12,
|
||||
rules: [{ required: true, message: '请选择港口', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '码头',
|
||||
prop: 'terminalId',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
display: false,
|
||||
span: 12,
|
||||
rules: [{ required: true, message: '请选择码头', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '车站名称',
|
||||
prop: 'stationId',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
display: false,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择车站名称', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '机场标准名称',
|
||||
prop: 'airportId',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
display: false,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择机场标准名称', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '地址名称',
|
||||
prop: 'addressName',
|
||||
search: true,
|
||||
span: 24,
|
||||
minWidth: 150,
|
||||
rules: [{ required: true, message: '请输入地址名称', trigger: 'blur' }],
|
||||
maxlength: 100,
|
||||
showWordLimit: true,
|
||||
rules: [
|
||||
{ required: true, message: '请输入地址名称', trigger: 'blur' },
|
||||
{ max: 100, message: '地址名称不能超过100个字', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '地址编号',
|
||||
@@ -38,21 +95,6 @@ export const createOption = ({ validateLongitude, validateLatitude }) => ({
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '类型',
|
||||
prop: 'addressType',
|
||||
type: 'select',
|
||||
search: true,
|
||||
dicData: addressTypeOptions,
|
||||
rules: [{ required: true, message: '请选择类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '来源主数据',
|
||||
prop: 'sourceId',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '站点编码',
|
||||
prop: 'siteCodeDisplay',
|
||||
@@ -69,6 +111,14 @@ export const createOption = ({ validateLongitude, validateLatitude }) => ({
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '行政区划',
|
||||
prop: 'regionName',
|
||||
search: true,
|
||||
display: false,
|
||||
minWidth: 150,
|
||||
rules: [{ required: true, message: '请选择行政区划', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '详细地址',
|
||||
prop: 'detailAddress',
|
||||
@@ -78,29 +128,10 @@ export const createOption = ({ validateLongitude, validateLatitude }) => ({
|
||||
minRows: 3,
|
||||
span: 24,
|
||||
minWidth: 220,
|
||||
rules: [{ required: true, message: '请输入详细地址', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '经度',
|
||||
prop: 'longitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
minWidth: 120,
|
||||
rules: [{ validator: validateLongitude, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '纬度',
|
||||
prop: 'latitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
minWidth: 120,
|
||||
rules: [{ validator: validateLatitude, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '行政区划',
|
||||
prop: 'regionName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
rules: [
|
||||
{ required: true, message: '请输入详细地址', trigger: 'blur' },
|
||||
{ max: 255, message: '详细地址不能超过255个字', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '组织',
|
||||
@@ -123,12 +154,16 @@ export const createOption = ({ validateLongitude, validateLatitude }) => ({
|
||||
prop: 'contactName',
|
||||
search: true,
|
||||
minWidth: 110,
|
||||
maxlength: 50,
|
||||
rules: [{ max: 50, message: '联系人不能超过50个字', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '联系方式',
|
||||
prop: 'contactPhone',
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
maxlength: 20,
|
||||
rules: [{ max: 20, message: '联系方式不能超过20个字', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
|
||||
193
src/option/business/common-cargo.js
Normal file
193
src/option/business/common-cargo.js
Normal file
@@ -0,0 +1,193 @@
|
||||
import {
|
||||
auditColumns,
|
||||
createCrudOption,
|
||||
packageOptions,
|
||||
priceUnitOptions,
|
||||
textRule,
|
||||
} from './common';
|
||||
|
||||
export const config = {
|
||||
title: '常用货物',
|
||||
permission: 'common_cargo',
|
||||
exportUrl: '/blade-transport/common-cargo/export-common-cargo',
|
||||
exportName: '常用货物',
|
||||
importUrl: '/blade-transport/common-cargo/import-common-cargo',
|
||||
templateUrl: '/blade-transport/common-cargo/export-template',
|
||||
enableAllDept: false,
|
||||
};
|
||||
|
||||
export const option = createCrudOption([
|
||||
{
|
||||
label: '一级货物类型',
|
||||
prop: 'firstCargoTypeName',
|
||||
formslot: true,
|
||||
search: true,
|
||||
searchOrder: 3,
|
||||
minWidth: 150,
|
||||
editDisabled: true,
|
||||
rules: [{ required: true, message: '请选择一级货物类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '一级货物类型编码',
|
||||
prop: 'firstCargoTypeCode',
|
||||
hide: true,
|
||||
display: false,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '二级货物类型',
|
||||
prop: 'secondCargoTypeName',
|
||||
formslot: true,
|
||||
search: true,
|
||||
searchOrder: 4,
|
||||
minWidth: 150,
|
||||
editDisabled: true,
|
||||
rules: [{ required: true, message: '请选择二级货物类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '二级货物类型编码',
|
||||
prop: 'secondCargoTypeCode',
|
||||
hide: true,
|
||||
display: false,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '货物名称',
|
||||
prop: 'cargoName',
|
||||
search: true,
|
||||
searchOrder: 1,
|
||||
minWidth: 150,
|
||||
rules: textRule('货物名称', 100, true),
|
||||
},
|
||||
{
|
||||
label: '货物编号',
|
||||
prop: 'cargoCode',
|
||||
formslot: true,
|
||||
search: true,
|
||||
searchOrder: 2,
|
||||
minWidth: 120,
|
||||
maxlength: 20,
|
||||
showWordLimit: true,
|
||||
rules: [
|
||||
{ required: true, message: '请输入货物编号', trigger: 'blur' },
|
||||
{ max: 20, message: '货物编号不能超过20个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '组织',
|
||||
prop: 'deptName',
|
||||
search: true,
|
||||
searchOrder: 5,
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '品牌',
|
||||
prop: 'brand',
|
||||
search: false,
|
||||
minWidth: 120,
|
||||
rules: textRule('品牌', 50),
|
||||
},
|
||||
{
|
||||
label: '包装',
|
||||
prop: 'packageType',
|
||||
type: 'select',
|
||||
dicData: packageOptions,
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
label: '货值',
|
||||
prop: 'cargoValue',
|
||||
formslot: true,
|
||||
minWidth: 120,
|
||||
rules: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
if (!/^\d+(\.\d{1,2})?$/.test(String(value))) {
|
||||
callback(new Error('货值只能输入数字,最多保留2位小数'));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '计价单位',
|
||||
prop: 'priceUnit',
|
||||
type: 'select',
|
||||
dicData: priceUnitOptions,
|
||||
display: false,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '规格',
|
||||
prop: 'specification',
|
||||
minWidth: 120,
|
||||
rules: textRule('规格', 50),
|
||||
},
|
||||
{
|
||||
label: '型号',
|
||||
prop: 'model',
|
||||
minWidth: 120,
|
||||
rules: textRule('型号', 50),
|
||||
},
|
||||
{
|
||||
label: '说明',
|
||||
prop: 'descriptionOne',
|
||||
minWidth: 160,
|
||||
rules: textRule('说明', 100),
|
||||
},
|
||||
{
|
||||
label: '数据来源',
|
||||
prop: 'dataSource',
|
||||
minWidth: 120,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
hide: true,
|
||||
maxlength: 200,
|
||||
showWordLimit: true,
|
||||
rules: textRule('备注', 200),
|
||||
},
|
||||
...auditColumns,
|
||||
]);
|
||||
|
||||
export const excelOption = {
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '导入模板',
|
||||
prop: 'excelTemplate',
|
||||
formslot: true,
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: 'Excel文件',
|
||||
prop: 'excelFile',
|
||||
type: 'upload',
|
||||
drag: true,
|
||||
loadText: '模板上传中,请稍等',
|
||||
span: 24,
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
},
|
||||
tip: '请上传 .xls,.xlsx 标准格式文件',
|
||||
},
|
||||
],
|
||||
};
|
||||
145
src/option/business/common-route.js
Normal file
145
src/option/business/common-route.js
Normal file
@@ -0,0 +1,145 @@
|
||||
import { auditColumns, createCrudOption, phoneRule, textRule } from './common';
|
||||
|
||||
export const config = {
|
||||
title: '常用线路',
|
||||
permission: 'common_route',
|
||||
exportUrl: '/blade-transport/common-route/export-common-route',
|
||||
exportName: '常用线路',
|
||||
enableAllDept: false,
|
||||
};
|
||||
|
||||
export const option = createCrudOption([
|
||||
{
|
||||
label: '线路名称',
|
||||
prop: 'routeName',
|
||||
search: true,
|
||||
searchOrder: 1,
|
||||
span: 24,
|
||||
minWidth: 150,
|
||||
rules: textRule('线路名称', 100, true),
|
||||
},
|
||||
{
|
||||
label: '线路编号',
|
||||
prop: 'routeCode',
|
||||
search: true,
|
||||
searchOrder: 2,
|
||||
minWidth: 120,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '发货地',
|
||||
prop: 'departureName',
|
||||
search: true,
|
||||
searchOrder: 3,
|
||||
minWidth: 140,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '发货地址',
|
||||
prop: 'departureAddress',
|
||||
formslot: true,
|
||||
search: true,
|
||||
searchOrder: 4,
|
||||
span: 24,
|
||||
minWidth: 220,
|
||||
rules: textRule('发货地址', 255, true),
|
||||
},
|
||||
{
|
||||
label: '发货经度',
|
||||
prop: 'departureLongitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
minWidth: 120,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '发货纬度',
|
||||
prop: 'departureLatitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
minWidth: 120,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '发货联系人',
|
||||
prop: 'departureContact',
|
||||
minWidth: 120,
|
||||
rules: textRule('发货联系人', 50),
|
||||
},
|
||||
{
|
||||
label: '发货联系方式',
|
||||
prop: 'departurePhone',
|
||||
minWidth: 140,
|
||||
rules: [phoneRule('发货联系方式'), ...textRule('发货联系方式', 50)],
|
||||
},
|
||||
{
|
||||
label: '收货地',
|
||||
prop: 'arrivalName',
|
||||
search: true,
|
||||
searchOrder: 5,
|
||||
minWidth: 140,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '收货地址',
|
||||
prop: 'arrivalAddress',
|
||||
formslot: true,
|
||||
search: true,
|
||||
searchOrder: 6,
|
||||
span: 24,
|
||||
minWidth: 220,
|
||||
rules: textRule('收货地址', 255, true),
|
||||
},
|
||||
{
|
||||
label: '收货经度',
|
||||
prop: 'arrivalLongitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
minWidth: 120,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '收货纬度',
|
||||
prop: 'arrivalLatitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
minWidth: 120,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '收货联系人',
|
||||
prop: 'arrivalContact',
|
||||
minWidth: 120,
|
||||
rules: textRule('收货联系人', 50),
|
||||
},
|
||||
{
|
||||
label: '收货联系方式',
|
||||
prop: 'arrivalPhone',
|
||||
minWidth: 140,
|
||||
rules: [phoneRule('收货联系方式'), ...textRule('收货联系方式', 50)],
|
||||
},
|
||||
{
|
||||
label: '组织',
|
||||
prop: 'deptName',
|
||||
search: true,
|
||||
searchOrder: 7,
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
hide: true,
|
||||
maxlength: 500,
|
||||
showWordLimit: true,
|
||||
rules: textRule('备注', 500),
|
||||
},
|
||||
...auditColumns,
|
||||
]);
|
||||
246
src/option/business/common.js
Normal file
246
src/option/business/common.js
Normal file
@@ -0,0 +1,246 @@
|
||||
export const transportTypeOptions = [
|
||||
{ label: '公路整车', value: '公路整车' },
|
||||
{ label: '公路零担', value: '公路零担' },
|
||||
{ label: '铁路运输', value: '铁路运输' },
|
||||
{ label: '水路运输', value: '水路运输' },
|
||||
{ label: '跨境海运', value: '跨境海运' },
|
||||
{ label: '航空运输', value: '航空运输' },
|
||||
];
|
||||
|
||||
export const packageOptions = [
|
||||
{ label: '袋装', value: '袋装' },
|
||||
{ label: '散装', value: '散装' },
|
||||
{ label: '桶装', value: '桶装' },
|
||||
{ label: '箱装', value: '箱装' },
|
||||
{ label: '托盘', value: '托盘' },
|
||||
];
|
||||
|
||||
export const priceUnitOptions = [
|
||||
{ label: '元/吨', value: '元/吨' },
|
||||
{ label: '元/立方米', value: '元/立方米' },
|
||||
{ label: '元/件', value: '元/件' },
|
||||
{ label: '元/车', value: '元/车' },
|
||||
];
|
||||
|
||||
export const templateTypeOptions = [
|
||||
{ label: '运输计划', value: '运输计划' },
|
||||
{ label: '常规运单', value: '常规运单' },
|
||||
];
|
||||
|
||||
export const processStatusOptions = [
|
||||
{ label: '草稿', value: 0 },
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '停用', value: 2 },
|
||||
];
|
||||
|
||||
export const planStatusOptions = [
|
||||
{ label: '草稿', value: 'draft' },
|
||||
{ label: '待调度', value: 'waiting_dispatch' },
|
||||
{ label: '调度中', value: 'dispatching' },
|
||||
{ label: '已完成', value: 'completed' },
|
||||
{ label: '已取消', value: 'cancelled' },
|
||||
];
|
||||
|
||||
export const waybillStatusOptions = [
|
||||
{ label: '草稿', value: 'draft' },
|
||||
{ label: '待执行', value: 'pending' },
|
||||
{ label: '进行中', value: 'processing' },
|
||||
{ label: '已完成', value: 'completed' },
|
||||
{ label: '已取消', value: 'cancelled' },
|
||||
];
|
||||
|
||||
export const dataSourceOptions = [
|
||||
{ label: '手工创建', value: '手工创建' },
|
||||
{ label: '批量导入', value: '批量导入' },
|
||||
{ label: '模板生成', value: '模板生成' },
|
||||
{ label: '计划调度', value: '计划调度' },
|
||||
];
|
||||
|
||||
export const projectTypeOptions = [
|
||||
{ label: '普通项目', value: '普通项目' },
|
||||
{ label: '重大项目', value: '重大项目' },
|
||||
];
|
||||
|
||||
export const projectSourceOptions = [
|
||||
{ label: '商务洽谈', value: '商务洽谈' },
|
||||
{ label: '招投标', value: '招投标' },
|
||||
{ label: '客户委托', value: '客户委托' },
|
||||
{ label: '其它', value: '其它' },
|
||||
];
|
||||
|
||||
export const projectApprovalStatusOptions = [
|
||||
{ label: '草稿', value: 'draft' },
|
||||
{ label: '审批中', value: 'reviewing' },
|
||||
{ label: '已撤回', value: 'withdrawn' },
|
||||
{ label: '审批通过', value: 'approved' },
|
||||
{ label: '已驳回', value: 'rejected' },
|
||||
{ label: '变更审批中', value: 'change_reviewing' },
|
||||
{ label: '变更驳回', value: 'change_rejected' },
|
||||
{ label: '变更审批通过', value: 'change_approved' },
|
||||
{ label: '已作废', value: 'voided' },
|
||||
];
|
||||
|
||||
export const approvalStatusOptions = [
|
||||
{ label: '草稿', value: 'draft' },
|
||||
{ label: '审批中', value: 'reviewing' },
|
||||
{ label: '已撤回', value: 'withdrawn' },
|
||||
{ label: '审批通过', value: 'approved' },
|
||||
{ label: '已驳回', value: 'rejected' },
|
||||
];
|
||||
|
||||
export const effectiveTypeOptions = [
|
||||
{ label: '临时', value: 'temporary' },
|
||||
{ label: '正式', value: 'formal' },
|
||||
];
|
||||
|
||||
export const businessTypeOptions = [
|
||||
{ label: '一类规模业务', value: '一类规模业务' },
|
||||
{ label: '二类规模业务', value: '二类规模业务' },
|
||||
{ label: '普通业务', value: '普通业务' },
|
||||
];
|
||||
|
||||
export const settlementModeOptions = [
|
||||
{ label: '先票后款', value: '先票后款' },
|
||||
{ label: '先款后票', value: '先款后票' },
|
||||
{ label: '月结', value: '月结' },
|
||||
];
|
||||
|
||||
export const contractCategoryOptions = [
|
||||
{ label: '客户合同', value: '客户合同' },
|
||||
{ label: '承运商合同', value: '承运商合同' },
|
||||
{ label: '补充协议', value: '补充协议' },
|
||||
];
|
||||
|
||||
export const signTypeOptions = [
|
||||
{ label: '新签', value: '新签' },
|
||||
{ label: '续签', value: '续签' },
|
||||
];
|
||||
|
||||
export const contractStageOptions = [
|
||||
{ label: '草稿', value: 'draft' },
|
||||
{ label: '临时合同', value: 'temporary' },
|
||||
{ label: '正式合同', value: 'formal' },
|
||||
{ label: '已终止', value: 'terminated' },
|
||||
];
|
||||
|
||||
export const contractApprovalStatusOptions = [
|
||||
{ label: '草稿', value: 'draft' },
|
||||
{ label: '审批中', value: 'reviewing' },
|
||||
{ label: '已驳回', value: 'rejected' },
|
||||
{ label: '审批通过', value: 'approved' },
|
||||
{ label: '变更审批中', value: 'change_reviewing' },
|
||||
{ label: '变更驳回', value: 'change_rejected' },
|
||||
{ label: '变更审批通过', value: 'change_approved' },
|
||||
];
|
||||
|
||||
export const nodeOptions = [
|
||||
{ label: '接单', value: '接单' },
|
||||
{ label: '到场', value: '到场' },
|
||||
{ label: '装货', value: '装货' },
|
||||
{ label: '发货', value: '发货' },
|
||||
{ label: '在途', value: '在途' },
|
||||
{ label: '到货', value: '到货' },
|
||||
{ label: '卸货', value: '卸货' },
|
||||
{ label: '签收', value: '签收' },
|
||||
{ label: '回单', value: '回单' },
|
||||
];
|
||||
|
||||
export const textRule = (label, max, required = false) => {
|
||||
const rules = [];
|
||||
if (required) {
|
||||
rules.push({ required: true, message: `请输入${label}`, trigger: 'blur' });
|
||||
}
|
||||
if (max) {
|
||||
rules.push({ max, message: `${label}不能超过${max}个字符`, trigger: 'blur' });
|
||||
}
|
||||
return rules;
|
||||
};
|
||||
|
||||
export const selectRule = label => [
|
||||
{ required: true, message: `请选择${label}`, trigger: 'change' },
|
||||
];
|
||||
|
||||
export const nonNegativeRule = label => ({
|
||||
validator: (rule, value, callback) => {
|
||||
if (value !== undefined && value !== null && value !== '' && Number(value) < 0) {
|
||||
callback(new Error(`${label}不能小于0`));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: 'blur',
|
||||
});
|
||||
|
||||
export const phoneRule = label => ({
|
||||
pattern: /^(1\d{10}|0\d{2,3}-?\d{7,8})$/,
|
||||
message: `${label}格式不正确`,
|
||||
trigger: 'blur',
|
||||
});
|
||||
|
||||
export const createCrudOption = columns => ({
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
indexWidth: 70,
|
||||
selection: true,
|
||||
addBtn: false,
|
||||
viewBtn: false,
|
||||
editBtn: false,
|
||||
delBtn: false,
|
||||
dialogClickModal: false,
|
||||
menuFixed: 'right',
|
||||
menuWidth: 240,
|
||||
column: columns,
|
||||
});
|
||||
|
||||
export const auditColumns = [
|
||||
{
|
||||
label: '创建人',
|
||||
prop: 'createUserName',
|
||||
minWidth: 120,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '更新人',
|
||||
prop: 'updateUserName',
|
||||
minWidth: 120,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'createTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 160,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '更新时间',
|
||||
prop: 'updateTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 160,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
];
|
||||
333
src/option/business/contract-manage.js
Normal file
333
src/option/business/contract-manage.js
Normal file
@@ -0,0 +1,333 @@
|
||||
import {
|
||||
auditColumns,
|
||||
contractApprovalStatusOptions,
|
||||
contractCategoryOptions,
|
||||
contractStageOptions,
|
||||
createCrudOption,
|
||||
selectRule,
|
||||
settlementModeOptions,
|
||||
signTypeOptions,
|
||||
textRule,
|
||||
} from './common';
|
||||
|
||||
export const config = {
|
||||
title: '合同管理',
|
||||
permission: 'contract_manage',
|
||||
exportUrl: '/blade-transport/contract-manage/export-contract-manage',
|
||||
exportName: '合同管理',
|
||||
enableAllDept: true,
|
||||
actions: ['copy'],
|
||||
statusProp: 'approvalStatus',
|
||||
statusTextProp: 'approvalStatusName',
|
||||
deleteStatus: ['draft'],
|
||||
editStatus: ['draft', 'rejected', 'change_rejected'],
|
||||
operations: [
|
||||
{
|
||||
action: 'toTemporary',
|
||||
label: '转临时',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['draft'],
|
||||
stage: ['draft'],
|
||||
permission: 'contract_manage_temp',
|
||||
},
|
||||
{
|
||||
action: 'submitFormal',
|
||||
label: '转正式',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['draft', 'rejected', 'approved'],
|
||||
stage: ['temporary'],
|
||||
permission: 'contract_manage_formal',
|
||||
},
|
||||
{
|
||||
action: 'withdraw',
|
||||
label: '撤回',
|
||||
icon: 'el-icon-refresh-left',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['reviewing', 'change_reviewing'],
|
||||
permission: 'contract_manage_withdraw',
|
||||
},
|
||||
{
|
||||
action: 'approve',
|
||||
label: '审批通过',
|
||||
type: 'success',
|
||||
icon: 'el-icon-check',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['reviewing', 'change_reviewing'],
|
||||
permission: 'contract_manage_approve',
|
||||
},
|
||||
{
|
||||
action: 'reject',
|
||||
label: '审批驳回',
|
||||
type: 'warning',
|
||||
icon: 'el-icon-close',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['reviewing', 'change_reviewing'],
|
||||
permission: 'contract_manage_reject',
|
||||
},
|
||||
{
|
||||
action: 'startChange',
|
||||
label: '发起变更',
|
||||
icon: 'el-icon-edit-outline',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['approved', 'change_approved'],
|
||||
stage: ['formal'],
|
||||
permission: 'contract_manage_change',
|
||||
prompt: '请输入变更内容与原因',
|
||||
},
|
||||
{
|
||||
action: 'terminate',
|
||||
label: '终止',
|
||||
type: 'danger',
|
||||
icon: 'el-icon-close',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['approved', 'change_approved'],
|
||||
stage: ['formal'],
|
||||
permission: 'contract_manage_terminate',
|
||||
prompt: '请输入终止原因',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const option = createCrudOption([
|
||||
{
|
||||
label: '合同编号',
|
||||
prop: 'contractNo',
|
||||
search: true,
|
||||
minWidth: 160,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '合同名称',
|
||||
prop: 'contractName',
|
||||
search: true,
|
||||
minWidth: 180,
|
||||
rules: textRule('合同名称', 100, true),
|
||||
},
|
||||
{
|
||||
label: '所属项目',
|
||||
prop: 'projectName',
|
||||
search: true,
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
label: '所属项目ID',
|
||||
prop: 'projectId',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '所属组织',
|
||||
prop: 'organizationName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '合同类别',
|
||||
prop: 'contractCategory',
|
||||
type: 'select',
|
||||
search: true,
|
||||
dicData: contractCategoryOptions,
|
||||
minWidth: 130,
|
||||
rules: selectRule('合同类别'),
|
||||
},
|
||||
{
|
||||
label: '签约类型',
|
||||
prop: 'signType',
|
||||
type: 'select',
|
||||
search: true,
|
||||
dicData: signTypeOptions,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '甲方',
|
||||
prop: 'partyA',
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
label: '乙方',
|
||||
prop: 'partyB',
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
label: '开始日期',
|
||||
prop: 'startDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '结束日期',
|
||||
prop: 'endDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '临时效力起',
|
||||
prop: 'temporaryStartDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '临时效力止',
|
||||
prop: 'temporaryEndDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '经办人',
|
||||
prop: 'handlerUserName',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '签订日期',
|
||||
prop: 'signDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '结算方式',
|
||||
prop: 'settlementMode',
|
||||
type: 'select',
|
||||
dicData: settlementModeOptions,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '合同格式',
|
||||
prop: 'contractFormat',
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{ label: '电子合同', value: '电子合同' },
|
||||
{ label: '纸质合同', value: '纸质合同' },
|
||||
],
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '法人章',
|
||||
prop: 'legalSealFlag',
|
||||
type: 'switch',
|
||||
dicData: [
|
||||
{ label: '否', value: 0 },
|
||||
{ label: '是', value: 1 },
|
||||
],
|
||||
value: 0,
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
label: '一式份数',
|
||||
prop: 'copyCount',
|
||||
type: 'number',
|
||||
minWidth: 110,
|
||||
},
|
||||
{
|
||||
label: '回款账期(天)',
|
||||
prop: 'paymentDays',
|
||||
type: 'number',
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '合同阶段',
|
||||
prop: 'contractStage',
|
||||
type: 'select',
|
||||
search: true,
|
||||
slot: true,
|
||||
dicData: contractStageOptions,
|
||||
minWidth: 120,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
fixed: 'right',
|
||||
},
|
||||
{
|
||||
label: '审核状态',
|
||||
prop: 'approvalStatus',
|
||||
type: 'select',
|
||||
search: true,
|
||||
slot: true,
|
||||
dicData: contractApprovalStatusOptions,
|
||||
minWidth: 130,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
fixed: 'right',
|
||||
},
|
||||
{
|
||||
label: '当前节点',
|
||||
prop: 'currentNode',
|
||||
minWidth: 140,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '当前处理人',
|
||||
prop: 'currentProcessor',
|
||||
minWidth: 130,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '计费信息',
|
||||
prop: 'billingEnabled',
|
||||
type: 'switch',
|
||||
dicData: [
|
||||
{ label: '关闭', value: 0 },
|
||||
{ label: '开启', value: 1 },
|
||||
],
|
||||
value: 0,
|
||||
minWidth: 110,
|
||||
},
|
||||
{
|
||||
label: '合同主文件JSON',
|
||||
prop: 'contractFileJson',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '其它附件JSON',
|
||||
prop: 'attachmentsJson',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '计费方案JSON',
|
||||
prop: 'billingPlanJson',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '结算生成规则JSON',
|
||||
prop: 'settlementRuleJson',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '对账配置JSON',
|
||||
prop: 'reconciliationJson',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
hide: true,
|
||||
maxlength: 2000,
|
||||
showWordLimit: true,
|
||||
rules: textRule('备注', 2000),
|
||||
},
|
||||
...auditColumns,
|
||||
]);
|
||||
147
src/option/business/process-config.js
Normal file
147
src/option/business/process-config.js
Normal file
@@ -0,0 +1,147 @@
|
||||
import {
|
||||
auditColumns,
|
||||
createCrudOption,
|
||||
nodeOptions,
|
||||
processStatusOptions,
|
||||
selectRule,
|
||||
textRule,
|
||||
} from './common';
|
||||
|
||||
export const config = {
|
||||
title: '过程配置',
|
||||
permission: 'process_config',
|
||||
exportUrl: '/blade-transport/process-config/export-process-config',
|
||||
exportName: '过程配置',
|
||||
enableAllDept: false,
|
||||
actions: ['copy', 'enable', 'disable'],
|
||||
statusProp: 'status',
|
||||
statusTextProp: 'statusName',
|
||||
deleteStatus: [0, 2],
|
||||
};
|
||||
|
||||
export const option = {
|
||||
...createCrudOption([
|
||||
{
|
||||
label: '',
|
||||
prop: 'basicInfo',
|
||||
formslot: true,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '配置编号',
|
||||
prop: 'configCode',
|
||||
search: true,
|
||||
searchOrder: 4,
|
||||
minWidth: 130,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '配置名称',
|
||||
prop: 'configName',
|
||||
search: true,
|
||||
searchOrder: 6,
|
||||
span: 12,
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
rules: textRule('配置名称', 100, true),
|
||||
},
|
||||
{
|
||||
label: '项目ID集合',
|
||||
prop: 'projectIds',
|
||||
hide: true,
|
||||
display: false,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
rules: textRule('项目ID集合', 1000, true),
|
||||
},
|
||||
{
|
||||
label: '项目',
|
||||
prop: 'projectNames',
|
||||
formslot: true,
|
||||
search: true,
|
||||
searchOrder: 5,
|
||||
searchType: 'select',
|
||||
type: 'textarea',
|
||||
minRows: 2,
|
||||
span: 12,
|
||||
minWidth: 180,
|
||||
dicData: [],
|
||||
filterable: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
rules: textRule('项目', 1000, true),
|
||||
},
|
||||
{
|
||||
label: '包含过程节点',
|
||||
prop: 'includedNodes',
|
||||
search: true,
|
||||
searchOrder: 3,
|
||||
type: 'select',
|
||||
multiple: true,
|
||||
dicData: nodeOptions,
|
||||
minWidth: 180,
|
||||
rules: selectRule('包含过程节点'),
|
||||
},
|
||||
{
|
||||
label: '默认完成运输天数',
|
||||
prop: 'defaultFinishDays',
|
||||
formslot: true,
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 999,
|
||||
span: 12,
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
type: 'select',
|
||||
search: true,
|
||||
searchOrder: 1,
|
||||
dicData: processStatusOptions,
|
||||
slot: true,
|
||||
minWidth: 100,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '组织',
|
||||
prop: 'deptName',
|
||||
search: true,
|
||||
searchOrder: 2,
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
prop: 'nodeConfigJson',
|
||||
formslot: true,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
maxlength: 500,
|
||||
showWordLimit: true,
|
||||
rules: textRule('备注', 500),
|
||||
},
|
||||
...auditColumns,
|
||||
]),
|
||||
dialogTop: '10px',
|
||||
dialogWidth: '96%',
|
||||
searchSpan: 8,
|
||||
};
|
||||
359
src/option/business/project-apply.js
Normal file
359
src/option/business/project-apply.js
Normal file
@@ -0,0 +1,359 @@
|
||||
import {
|
||||
auditColumns,
|
||||
businessTypeOptions,
|
||||
createCrudOption,
|
||||
effectiveTypeOptions,
|
||||
nonNegativeRule,
|
||||
projectApprovalStatusOptions,
|
||||
projectSourceOptions,
|
||||
projectTypeOptions,
|
||||
selectRule,
|
||||
settlementModeOptions,
|
||||
textRule,
|
||||
transportTypeOptions,
|
||||
} from './common';
|
||||
|
||||
const amountRules = label => [
|
||||
{ required: true, message: `请输入${label}`, trigger: 'blur' },
|
||||
nonNegativeRule(label),
|
||||
];
|
||||
|
||||
export const config = {
|
||||
title: '项目管理',
|
||||
permission: 'project_apply',
|
||||
exportUrl: '/blade-transport/project-apply/export-project-apply',
|
||||
exportName: '项目立项',
|
||||
enableAllDept: true,
|
||||
statusProp: 'approvalStatus',
|
||||
statusTextProp: 'approvalStatusName',
|
||||
deleteStatus: ['draft', 'withdrawn', 'rejected'],
|
||||
editStatus: ['draft', 'withdrawn', 'rejected', 'change_rejected'],
|
||||
operations: [
|
||||
{
|
||||
action: 'submitApproval',
|
||||
label: '提交审批',
|
||||
icon: 'el-icon-position',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['draft', 'withdrawn', 'rejected'],
|
||||
permission: 'project_apply_submit',
|
||||
},
|
||||
{
|
||||
action: 'withdraw',
|
||||
label: '撤回',
|
||||
icon: 'el-icon-refresh-left',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['reviewing'],
|
||||
permission: 'project_apply_withdraw',
|
||||
},
|
||||
{
|
||||
action: 'approve',
|
||||
label: '审批通过',
|
||||
type: 'success',
|
||||
icon: 'el-icon-check',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['reviewing', 'change_reviewing'],
|
||||
permission: 'project_apply_approve',
|
||||
},
|
||||
{
|
||||
action: 'reject',
|
||||
label: '审批驳回',
|
||||
type: 'warning',
|
||||
icon: 'el-icon-close',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['reviewing', 'change_reviewing'],
|
||||
permission: 'project_apply_reject',
|
||||
},
|
||||
{
|
||||
action: 'startChange',
|
||||
label: '发起变更',
|
||||
icon: 'el-icon-edit-outline',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['approved', 'change_approved'],
|
||||
permission: 'project_apply_change',
|
||||
prompt: '请输入变更内容与原因',
|
||||
},
|
||||
{
|
||||
action: 'voidProject',
|
||||
label: '作废',
|
||||
type: 'danger',
|
||||
icon: 'el-icon-delete',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['approved', 'change_approved'],
|
||||
permission: 'project_apply_void',
|
||||
prompt: '请输入作废原因',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const option = createCrudOption([
|
||||
{
|
||||
label: '立项申请单号',
|
||||
prop: 'applyNo',
|
||||
search: true,
|
||||
minWidth: 160,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '项目编号',
|
||||
prop: 'projectCode',
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '项目名称',
|
||||
prop: 'projectName',
|
||||
search: true,
|
||||
minWidth: 180,
|
||||
maxlength: 50,
|
||||
rules: textRule('项目名称', 50, true),
|
||||
},
|
||||
{
|
||||
label: '项目简称',
|
||||
prop: 'projectShortName',
|
||||
minWidth: 140,
|
||||
maxlength: 20,
|
||||
rules: textRule('项目简称', 20, true),
|
||||
},
|
||||
{
|
||||
label: '项目类型',
|
||||
prop: 'projectType',
|
||||
type: 'select',
|
||||
search: true,
|
||||
dicData: projectTypeOptions,
|
||||
minWidth: 120,
|
||||
rules: selectRule('项目类型'),
|
||||
},
|
||||
{
|
||||
label: '业务部门',
|
||||
prop: 'businessDeptName',
|
||||
minWidth: 150,
|
||||
rules: textRule('业务部门', 50, true),
|
||||
},
|
||||
{
|
||||
label: '承办部门',
|
||||
prop: 'undertakeDeptName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
rules: textRule('承办部门', 50, true),
|
||||
},
|
||||
{
|
||||
label: '承办部门ID',
|
||||
prop: 'undertakeDeptId',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '项目负责人',
|
||||
prop: 'principalUserName',
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
rules: textRule('项目负责人', 50, true),
|
||||
},
|
||||
{
|
||||
label: '项目经办人',
|
||||
prop: 'handlerUserName',
|
||||
minWidth: 130,
|
||||
rules: textRule('项目经办人', 50, true),
|
||||
},
|
||||
{
|
||||
label: '项目由来',
|
||||
prop: 'projectSource',
|
||||
type: 'select',
|
||||
dicData: projectSourceOptions,
|
||||
value: '商务洽谈',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '项目资金使用额度(万元)',
|
||||
prop: 'fundLimit',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
minWidth: 190,
|
||||
rules: amountRules('项目资金使用额度'),
|
||||
},
|
||||
{
|
||||
label: '项目应收账款额度(万元)',
|
||||
prop: 'receivableLimit',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
minWidth: 200,
|
||||
rules: amountRules('项目应收账款额度'),
|
||||
},
|
||||
{
|
||||
label: '应收账款回款期限(天)',
|
||||
prop: 'receivableDays',
|
||||
type: 'number',
|
||||
minWidth: 190,
|
||||
rules: [{ required: true, message: '请输入应收账款回款期限', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '回款账期(天)',
|
||||
prop: 'paymentDays',
|
||||
type: 'number',
|
||||
minWidth: 140,
|
||||
rules: [{ required: true, message: '请输入回款账期', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '客户名称',
|
||||
prop: 'customerNames',
|
||||
search: true,
|
||||
minWidth: 180,
|
||||
rules: textRule('客户名称', 255, true),
|
||||
},
|
||||
{
|
||||
label: '下游承运商',
|
||||
prop: 'carrierNames',
|
||||
minWidth: 180,
|
||||
rules: textRule('下游承运商', 255, true),
|
||||
},
|
||||
{
|
||||
label: '货物类型',
|
||||
prop: 'cargoType',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '预估货物数量',
|
||||
prop: 'cargoQuantity',
|
||||
minWidth: 150,
|
||||
maxlength: 100,
|
||||
},
|
||||
{
|
||||
label: '业务周期开始',
|
||||
prop: 'businessStartDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '业务周期结束',
|
||||
prop: 'businessEndDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '运输线路',
|
||||
prop: 'transportRoute',
|
||||
minWidth: 180,
|
||||
maxlength: 100,
|
||||
},
|
||||
{
|
||||
label: '运输类型',
|
||||
prop: 'transportType',
|
||||
type: 'select',
|
||||
dicData: transportTypeOptions,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '业务类型',
|
||||
prop: 'businessType',
|
||||
type: 'select',
|
||||
dicData: businessTypeOptions,
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '项目规模(万元)',
|
||||
prop: 'projectScale',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
minWidth: 150,
|
||||
rules: [nonNegativeRule('项目规模')],
|
||||
},
|
||||
{
|
||||
label: '预计利润(万元)',
|
||||
prop: 'estimatedProfit',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
minWidth: 150,
|
||||
rules: [nonNegativeRule('预计利润')],
|
||||
},
|
||||
{
|
||||
label: '资金需求(万元)',
|
||||
prop: 'fundDemand',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
minWidth: 150,
|
||||
rules: [nonNegativeRule('资金需求')],
|
||||
},
|
||||
{
|
||||
label: '结算方式',
|
||||
prop: 'settlementMode',
|
||||
type: 'select',
|
||||
dicData: settlementModeOptions,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '审批状态',
|
||||
prop: 'approvalStatus',
|
||||
type: 'select',
|
||||
search: true,
|
||||
slot: true,
|
||||
dicData: projectApprovalStatusOptions,
|
||||
minWidth: 130,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
fixed: 'right',
|
||||
},
|
||||
{
|
||||
label: '当前节点',
|
||||
prop: 'currentNode',
|
||||
minWidth: 140,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '当前处理人',
|
||||
prop: 'currentProcessor',
|
||||
minWidth: 130,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '生效类型',
|
||||
prop: 'effectiveType',
|
||||
type: 'select',
|
||||
search: true,
|
||||
dicData: effectiveTypeOptions,
|
||||
value: 'temporary',
|
||||
minWidth: 110,
|
||||
},
|
||||
{
|
||||
label: '审核通过时间',
|
||||
prop: 'approvedTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 170,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '项目由来说明',
|
||||
prop: 'sourceRemark',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
hide: true,
|
||||
maxlength: 300,
|
||||
showWordLimit: true,
|
||||
},
|
||||
{
|
||||
label: '项目情况说明',
|
||||
prop: 'situationRemark',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
hide: true,
|
||||
maxlength: 1000,
|
||||
showWordLimit: true,
|
||||
},
|
||||
{
|
||||
label: '项目附件JSON',
|
||||
prop: 'attachmentsJson',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
...auditColumns,
|
||||
]);
|
||||
167
src/option/business/shipping-template.js
Normal file
167
src/option/business/shipping-template.js
Normal file
@@ -0,0 +1,167 @@
|
||||
import {
|
||||
auditColumns,
|
||||
createCrudOption,
|
||||
templateTypeOptions,
|
||||
transportTypeOptions,
|
||||
selectRule,
|
||||
textRule,
|
||||
} from './common';
|
||||
|
||||
export const config = {
|
||||
title: '发货模板',
|
||||
permission: 'shipping_template',
|
||||
exportUrl: '/blade-transport/shipping-template/export-shipping-template',
|
||||
exportName: '发货模板',
|
||||
enableAllDept: true,
|
||||
actions: ['copy'],
|
||||
};
|
||||
|
||||
export const option = createCrudOption([
|
||||
{
|
||||
label: '模板编号',
|
||||
prop: 'templateCode',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '模板名称',
|
||||
prop: 'templateName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
rules: textRule('模板名称', 50, true),
|
||||
},
|
||||
{
|
||||
label: '模板类型',
|
||||
prop: 'templateType',
|
||||
type: 'select',
|
||||
search: true,
|
||||
dicData: templateTypeOptions,
|
||||
minWidth: 120,
|
||||
value: '运输计划',
|
||||
rules: selectRule('模板类型'),
|
||||
},
|
||||
{
|
||||
label: '项目',
|
||||
prop: 'projectName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
rules: textRule('项目', 100, true),
|
||||
},
|
||||
{
|
||||
label: '项目ID',
|
||||
prop: 'projectId',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '客户合同',
|
||||
prop: 'contractName',
|
||||
minWidth: 160,
|
||||
rules: textRule('客户合同', 100, true),
|
||||
},
|
||||
{
|
||||
label: '客户合同ID',
|
||||
prop: 'contractId',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '运输类型',
|
||||
prop: 'transportType',
|
||||
type: 'select',
|
||||
dicData: transportTypeOptions,
|
||||
minWidth: 130,
|
||||
rules: selectRule('运输类型'),
|
||||
},
|
||||
{
|
||||
label: '发货地',
|
||||
prop: 'departureName',
|
||||
minWidth: 140,
|
||||
rules: textRule('发货地', 100, true),
|
||||
},
|
||||
{
|
||||
label: '发货地址',
|
||||
prop: 'departureAddress',
|
||||
minWidth: 220,
|
||||
rules: textRule('发货地址', 255, true),
|
||||
},
|
||||
{
|
||||
label: '发货联系人',
|
||||
prop: 'departureContact',
|
||||
minWidth: 120,
|
||||
rules: textRule('发货联系人', 20),
|
||||
},
|
||||
{
|
||||
label: '发货联系方式',
|
||||
prop: 'departurePhone',
|
||||
minWidth: 140,
|
||||
rules: textRule('发货联系方式', 20),
|
||||
},
|
||||
{
|
||||
label: '收货地',
|
||||
prop: 'arrivalName',
|
||||
minWidth: 140,
|
||||
rules: textRule('收货地', 100, true),
|
||||
},
|
||||
{
|
||||
label: '收货地址',
|
||||
prop: 'arrivalAddress',
|
||||
minWidth: 220,
|
||||
rules: textRule('收货地址', 255, true),
|
||||
},
|
||||
{
|
||||
label: '收货联系人',
|
||||
prop: 'arrivalContact',
|
||||
minWidth: 120,
|
||||
rules: textRule('收货联系人', 20),
|
||||
},
|
||||
{
|
||||
label: '收货联系方式',
|
||||
prop: 'arrivalPhone',
|
||||
minWidth: 140,
|
||||
rules: textRule('收货联系方式', 20),
|
||||
},
|
||||
{
|
||||
label: '货物信息JSON',
|
||||
prop: 'goodsJson',
|
||||
type: 'textarea',
|
||||
minRows: 5,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '运费信息JSON',
|
||||
prop: 'freightJson',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '附件JSON',
|
||||
prop: 'attachmentsJson',
|
||||
type: 'textarea',
|
||||
minRows: 3,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '所属组织',
|
||||
prop: 'deptName',
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
hide: true,
|
||||
maxlength: 200,
|
||||
showWordLimit: true,
|
||||
rules: textRule('备注', 200),
|
||||
},
|
||||
...auditColumns,
|
||||
]);
|
||||
191
src/option/business/temporary-credit-limit.js
Normal file
191
src/option/business/temporary-credit-limit.js
Normal file
@@ -0,0 +1,191 @@
|
||||
import { approvalStatusOptions, auditColumns, createCrudOption, nonNegativeRule, textRule } from './common';
|
||||
|
||||
export const config = {
|
||||
title: '临时额度管理',
|
||||
permission: 'temporary_credit_limit',
|
||||
exportUrl: '/blade-transport/temporary-credit-limit/export-temporary-credit-limit',
|
||||
exportName: '临时额度申请',
|
||||
enableAllDept: true,
|
||||
statusProp: 'approvalStatus',
|
||||
statusTextProp: 'approvalStatusName',
|
||||
deleteStatus: ['draft', 'withdrawn', 'rejected'],
|
||||
editStatus: ['draft', 'withdrawn', 'rejected'],
|
||||
operations: [
|
||||
{
|
||||
action: 'submitApproval',
|
||||
label: '提交审批',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['draft', 'withdrawn', 'rejected'],
|
||||
permission: 'temporary_credit_limit_submit',
|
||||
},
|
||||
{
|
||||
action: 'withdraw',
|
||||
label: '撤回',
|
||||
icon: 'el-icon-refresh-left',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['reviewing'],
|
||||
permission: 'temporary_credit_limit_withdraw',
|
||||
},
|
||||
{
|
||||
action: 'approve',
|
||||
label: '审批通过',
|
||||
type: 'success',
|
||||
icon: 'el-icon-check',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['reviewing'],
|
||||
permission: 'temporary_credit_limit_approve',
|
||||
},
|
||||
{
|
||||
action: 'reject',
|
||||
label: '审批驳回',
|
||||
type: 'warning',
|
||||
icon: 'el-icon-close',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['reviewing'],
|
||||
permission: 'temporary_credit_limit_reject',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const option = createCrudOption([
|
||||
{
|
||||
label: '申请单号',
|
||||
prop: 'applicationNo',
|
||||
search: true,
|
||||
minWidth: 160,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '项目名称',
|
||||
prop: 'projectName',
|
||||
search: true,
|
||||
minWidth: 180,
|
||||
rules: textRule('项目名称', 100, true),
|
||||
},
|
||||
{
|
||||
label: '项目ID',
|
||||
prop: 'projectId',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '项目编号',
|
||||
prop: 'projectCode',
|
||||
minWidth: 150,
|
||||
rules: textRule('项目编号', 50, true),
|
||||
},
|
||||
{
|
||||
label: '承办部门',
|
||||
prop: 'undertakeDeptName',
|
||||
minWidth: 150,
|
||||
rules: textRule('承办部门', 50, true),
|
||||
},
|
||||
{
|
||||
label: '项目资金使用额度(万元)',
|
||||
prop: 'projectFundLimit',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
minWidth: 200,
|
||||
editDisabled: true,
|
||||
rules: [nonNegativeRule('项目资金使用额度')],
|
||||
},
|
||||
{
|
||||
label: '已使用项目资金额度(万元)',
|
||||
prop: 'usedFundLimit',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
minWidth: 220,
|
||||
value: 0,
|
||||
editDisabled: true,
|
||||
rules: [nonNegativeRule('已使用项目资金额度')],
|
||||
},
|
||||
{
|
||||
label: '剩余项目资金使用额度(万元)',
|
||||
prop: 'remainingFundLimit',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
minWidth: 230,
|
||||
editDisabled: true,
|
||||
rules: [nonNegativeRule('剩余项目资金使用额度')],
|
||||
},
|
||||
{
|
||||
label: '申请临时额度(万元)',
|
||||
prop: 'applyLimit',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
minWidth: 180,
|
||||
rules: [
|
||||
{ required: true, message: '请输入申请临时额度', trigger: 'blur' },
|
||||
nonNegativeRule('申请临时额度'),
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '申请有效期至',
|
||||
prop: 'validUntil',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 140,
|
||||
rules: [{ required: true, message: '请选择申请有效期至', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '申请部门',
|
||||
prop: 'applyDeptName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '申请人',
|
||||
prop: 'applicantName',
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '审批状态',
|
||||
prop: 'approvalStatus',
|
||||
type: 'select',
|
||||
search: true,
|
||||
slot: true,
|
||||
dicData: approvalStatusOptions,
|
||||
minWidth: 130,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
fixed: 'right',
|
||||
},
|
||||
{
|
||||
label: '当前节点',
|
||||
prop: 'currentNode',
|
||||
minWidth: 140,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '当前处理人',
|
||||
prop: 'currentProcessor',
|
||||
minWidth: 130,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '附件JSON',
|
||||
prop: 'attachmentsJson',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
hide: true,
|
||||
maxlength: 500,
|
||||
showWordLimit: true,
|
||||
rules: textRule('备注', 500),
|
||||
},
|
||||
...auditColumns,
|
||||
]);
|
||||
175
src/option/business/transport-plan.js
Normal file
175
src/option/business/transport-plan.js
Normal file
@@ -0,0 +1,175 @@
|
||||
import {
|
||||
auditColumns,
|
||||
createCrudOption,
|
||||
dataSourceOptions,
|
||||
planStatusOptions,
|
||||
selectRule,
|
||||
textRule,
|
||||
transportTypeOptions,
|
||||
} from './common';
|
||||
|
||||
export const config = {
|
||||
title: '运输计划',
|
||||
permission: 'transport_plan',
|
||||
exportUrl: '/blade-transport/transport-plan/export-transport-plan',
|
||||
exportName: '运输计划',
|
||||
enableAllDept: true,
|
||||
actions: ['copy', 'cancel', 'complete'],
|
||||
statusProp: 'businessStatus',
|
||||
statusTextProp: 'businessStatusName',
|
||||
deleteStatus: ['draft'],
|
||||
editStatus: ['draft', 'waiting_dispatch'],
|
||||
};
|
||||
|
||||
export const option = createCrudOption([
|
||||
{
|
||||
label: '计划单号',
|
||||
prop: 'planNo',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '计划名称',
|
||||
prop: 'planName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
rules: textRule('计划名称', 50, true),
|
||||
},
|
||||
{
|
||||
label: '项目',
|
||||
prop: 'projectName',
|
||||
minWidth: 150,
|
||||
rules: textRule('项目', 100, true),
|
||||
},
|
||||
{
|
||||
label: '项目ID',
|
||||
prop: 'projectId',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '客户合同',
|
||||
prop: 'contractName',
|
||||
minWidth: 160,
|
||||
rules: textRule('客户合同', 100, true),
|
||||
},
|
||||
{
|
||||
label: '客户合同ID',
|
||||
prop: 'contractId',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '客户名称',
|
||||
prop: 'customerName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
label: '运输类型',
|
||||
prop: 'transportType',
|
||||
type: 'select',
|
||||
search: true,
|
||||
dicData: transportTypeOptions,
|
||||
minWidth: 130,
|
||||
rules: selectRule('运输类型'),
|
||||
},
|
||||
{
|
||||
label: '计划开始日期',
|
||||
prop: 'planStartDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '计划结束日期',
|
||||
prop: 'planEndDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '发货地',
|
||||
prop: 'departureName',
|
||||
minWidth: 140,
|
||||
rules: textRule('发货地', 100, true),
|
||||
},
|
||||
{
|
||||
label: '发货地址',
|
||||
prop: 'departureAddress',
|
||||
search: true,
|
||||
minWidth: 220,
|
||||
rules: textRule('发货地址', 255, true),
|
||||
},
|
||||
{
|
||||
label: '收货地',
|
||||
prop: 'arrivalName',
|
||||
minWidth: 140,
|
||||
rules: textRule('收货地', 100, true),
|
||||
},
|
||||
{
|
||||
label: '收货地址',
|
||||
prop: 'arrivalAddress',
|
||||
search: true,
|
||||
minWidth: 220,
|
||||
rules: textRule('收货地址', 255, true),
|
||||
},
|
||||
{
|
||||
label: '货物信息JSON',
|
||||
prop: 'goodsJson',
|
||||
type: 'textarea',
|
||||
minRows: 5,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '附件JSON',
|
||||
prop: 'attachmentsJson',
|
||||
type: 'textarea',
|
||||
minRows: 3,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '数据来源',
|
||||
prop: 'dataSource',
|
||||
type: 'select',
|
||||
search: true,
|
||||
dicData: dataSourceOptions,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '业务状态',
|
||||
prop: 'businessStatus',
|
||||
type: 'select',
|
||||
slot: true,
|
||||
search: true,
|
||||
dicData: planStatusOptions,
|
||||
minWidth: 120,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '所属组织',
|
||||
prop: 'deptName',
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
hide: true,
|
||||
maxlength: 200,
|
||||
showWordLimit: true,
|
||||
rules: textRule('备注', 200),
|
||||
},
|
||||
...auditColumns,
|
||||
]);
|
||||
254
src/option/business/waybill-manage.js
Normal file
254
src/option/business/waybill-manage.js
Normal file
@@ -0,0 +1,254 @@
|
||||
import {
|
||||
auditColumns,
|
||||
createCrudOption,
|
||||
dataSourceOptions,
|
||||
selectRule,
|
||||
textRule,
|
||||
transportTypeOptions,
|
||||
waybillStatusOptions,
|
||||
} from './common';
|
||||
|
||||
export const config = {
|
||||
title: '运单管理',
|
||||
permission: 'waybill_manage',
|
||||
exportUrl: '/blade-transport/waybill-manage/export-waybill-manage',
|
||||
exportName: '运单管理',
|
||||
enableAllDept: true,
|
||||
actions: ['copy', 'cancel', 'reassign', 'complete', 'batchComplete'],
|
||||
statusProp: 'businessStatus',
|
||||
statusTextProp: 'businessStatusName',
|
||||
deleteStatus: ['draft'],
|
||||
editStatus: ['draft', 'pending'],
|
||||
};
|
||||
|
||||
export const option = createCrudOption([
|
||||
{
|
||||
label: '运单号',
|
||||
prop: 'waybillNo',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '项目',
|
||||
prop: 'projectName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
rules: textRule('项目', 100, true),
|
||||
},
|
||||
{
|
||||
label: '项目ID',
|
||||
prop: 'projectId',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '客户合同',
|
||||
prop: 'contractName',
|
||||
minWidth: 160,
|
||||
rules: textRule('客户合同', 100, true),
|
||||
},
|
||||
{
|
||||
label: '客户合同ID',
|
||||
prop: 'contractId',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '客户名称',
|
||||
prop: 'customerName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
label: '运输类型',
|
||||
prop: 'transportType',
|
||||
type: 'select',
|
||||
search: true,
|
||||
dicData: transportTypeOptions,
|
||||
minWidth: 130,
|
||||
rules: selectRule('运输类型'),
|
||||
},
|
||||
{
|
||||
label: '货物名称',
|
||||
prop: 'cargoName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
rules: textRule('货物名称', 100, true),
|
||||
},
|
||||
{
|
||||
label: '货物类型',
|
||||
prop: 'cargoType',
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
rules: textRule('货物类型', 100, true),
|
||||
},
|
||||
{
|
||||
label: '发货地址',
|
||||
prop: 'departureAddress',
|
||||
search: true,
|
||||
minWidth: 220,
|
||||
rules: textRule('发货地址', 255, true),
|
||||
},
|
||||
{
|
||||
label: '收货地址',
|
||||
prop: 'arrivalAddress',
|
||||
search: true,
|
||||
minWidth: 220,
|
||||
rules: textRule('收货地址', 255, true),
|
||||
},
|
||||
{
|
||||
label: '承运商名称',
|
||||
prop: 'carrierName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
label: '司机姓名',
|
||||
prop: 'driverName',
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '车/船/航班/班列号',
|
||||
prop: 'vehicleNo',
|
||||
search: true,
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '原始单号',
|
||||
prop: 'originalNo',
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
label: '业务状态',
|
||||
prop: 'businessStatus',
|
||||
type: 'select',
|
||||
slot: true,
|
||||
search: true,
|
||||
dicData: waybillStatusOptions,
|
||||
minWidth: 120,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '数据来源',
|
||||
prop: 'dataSource',
|
||||
type: 'select',
|
||||
search: true,
|
||||
dicData: dataSourceOptions,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '开始日期',
|
||||
prop: 'startDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '结束日期',
|
||||
prop: 'endDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
label: '计划名称',
|
||||
prop: 'planName',
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
label: '多联总单',
|
||||
prop: 'masterNo',
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
label: '配载单号',
|
||||
prop: 'loadingNo',
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
label: '运单批次号',
|
||||
prop: 'batchNo',
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
label: '关联单号',
|
||||
prop: 'relationNo',
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
label: '当前过程节点',
|
||||
prop: 'currentProcessNode',
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
label: '货物信息JSON',
|
||||
prop: 'goodsJson',
|
||||
type: 'textarea',
|
||||
minRows: 5,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '承运信息JSON',
|
||||
prop: 'carrierJson',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '过程配置JSON',
|
||||
prop: 'processJson',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '运费信息JSON',
|
||||
prop: 'freightJson',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '附件JSON',
|
||||
prop: 'attachmentsJson',
|
||||
type: 'textarea',
|
||||
minRows: 3,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '所属组织',
|
||||
prop: 'deptName',
|
||||
minWidth: 150,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
hide: true,
|
||||
maxlength: 500,
|
||||
showWordLimit: true,
|
||||
rules: textRule('备注', 500),
|
||||
},
|
||||
...auditColumns,
|
||||
]);
|
||||
@@ -120,13 +120,16 @@ export default {
|
||||
calcHeight: 30,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
grid: true,
|
||||
labelWidth: 180,
|
||||
labelWidth: 'auto',
|
||||
menuWidth: 350,
|
||||
dialogWidth: 1200,
|
||||
dialogClickModal: false,
|
||||
|
||||
@@ -3,13 +3,16 @@ export default {
|
||||
calcHeight: 30,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
grid: true,
|
||||
labelWidth: 100,
|
||||
labelWidth: 'auto',
|
||||
menuWidth: 350,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
|
||||
@@ -12,7 +12,10 @@ export const LOCK_STATUS_DIC = [
|
||||
export const authLockOption = {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
addBtn: false,
|
||||
|
||||
@@ -10,7 +10,10 @@ export const GRANT_TYPE_DIC = [
|
||||
export const authLogOption = {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
addBtn: false,
|
||||
|
||||
@@ -3,7 +3,10 @@ export const optionParent = {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 10,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
@@ -92,7 +95,10 @@ export const optionChild = {
|
||||
calcHeight: 95,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 10,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
@@ -105,8 +111,8 @@ export const optionChild = {
|
||||
{
|
||||
label: '字典编号',
|
||||
prop: 'code',
|
||||
addDisabled: true,
|
||||
editDisabled: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
|
||||
@@ -3,7 +3,10 @@ export const optionParent = {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 10,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
@@ -93,7 +96,10 @@ export const optionChild = {
|
||||
calcHeight: 95,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 10,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
@@ -106,8 +112,8 @@ export const optionChild = {
|
||||
{
|
||||
label: '字典编号',
|
||||
prop: 'code',
|
||||
addDisabled: true,
|
||||
editDisabled: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
|
||||
@@ -23,7 +23,10 @@ export const userOption = safe => {
|
||||
calcHeight: 150,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
@@ -353,13 +356,16 @@ export const userOption = safe => {
|
||||
export const platformOption = {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 120,
|
||||
menuWidth: 220,
|
||||
editBtnText: '配置',
|
||||
column: [
|
||||
{
|
||||
@@ -475,7 +481,7 @@ export const excelOption = {
|
||||
export const platformFormOption = {
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
labelWidth: 100,
|
||||
labelWidth: 'auto',
|
||||
column: [
|
||||
{
|
||||
label: '用户账号',
|
||||
|
||||
@@ -6,11 +6,14 @@ export const codeOption = {
|
||||
dialogWidth: 900,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
menuWidth: 350,
|
||||
viewBtn: true,
|
||||
dialogClickModal: false,
|
||||
@@ -376,7 +379,7 @@ export const codeOption = {
|
||||
};
|
||||
|
||||
export const genOption = {
|
||||
labelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
column: [
|
||||
{
|
||||
label: '上级菜单',
|
||||
|
||||
@@ -3,14 +3,17 @@ export default {
|
||||
calcHeight: 50,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
grid: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 100,
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
menuWidth: 320,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
|
||||
@@ -3,14 +3,17 @@ export default {
|
||||
calcHeight: 50,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
grid: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 100,
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
menuWidth: 320,
|
||||
dialogWidth: 500,
|
||||
dialogClickModal: false,
|
||||
|
||||
@@ -3,7 +3,10 @@ export const option = {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
@@ -18,66 +21,61 @@ export const option = {
|
||||
label: '姓名',
|
||||
prop: 'driverName',
|
||||
search: true,
|
||||
searchOrder: 7,
|
||||
slot: true,
|
||||
width: 110,
|
||||
},
|
||||
{
|
||||
label: '身份证号',
|
||||
prop: 'idCardNo',
|
||||
search: true,
|
||||
width: 190,
|
||||
},
|
||||
{
|
||||
label: '手机号码',
|
||||
label: '手机号',
|
||||
prop: 'mobile',
|
||||
search: true,
|
||||
searchOrder: 6,
|
||||
width: 130,
|
||||
},
|
||||
{
|
||||
label: '性别',
|
||||
prop: 'gender',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
label: '司机类型',
|
||||
prop: 'driverType',
|
||||
search: true,
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{
|
||||
label: '自有',
|
||||
value: '自有',
|
||||
},
|
||||
{
|
||||
label: '外协',
|
||||
value: '外协',
|
||||
},
|
||||
],
|
||||
width: 100,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
label: '岗位',
|
||||
prop: 'posts',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: '准驾车型',
|
||||
prop: 'drivingType',
|
||||
search: true,
|
||||
width: 110,
|
||||
searchOrder: 5,
|
||||
hide: true,
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{
|
||||
label: 'A1',
|
||||
value: 'A1',
|
||||
},
|
||||
{
|
||||
label: '所属组织',
|
||||
prop: 'organizationName',
|
||||
search: true,
|
||||
width: 140,
|
||||
label: 'A2',
|
||||
value: 'A2',
|
||||
},
|
||||
{
|
||||
label: 'A3',
|
||||
value: 'A3',
|
||||
},
|
||||
{
|
||||
label: 'B1',
|
||||
value: 'B1',
|
||||
},
|
||||
{
|
||||
label: 'B2',
|
||||
value: 'B2',
|
||||
},
|
||||
{
|
||||
label: 'C1',
|
||||
value: 'C1',
|
||||
},
|
||||
{
|
||||
label: 'C2',
|
||||
value: 'C2',
|
||||
},
|
||||
],
|
||||
width: 110,
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
search: true,
|
||||
searchOrder: 4,
|
||||
type: 'select',
|
||||
slot: true,
|
||||
dicData: [
|
||||
@@ -93,6 +91,53 @@ export const option = {
|
||||
width: 90,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
label: '司机类型',
|
||||
prop: 'driverType',
|
||||
search: true,
|
||||
searchOrder: 3,
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{
|
||||
label: '自有员工',
|
||||
value: '自有',
|
||||
},
|
||||
{
|
||||
label: '外协',
|
||||
value: '外协',
|
||||
},
|
||||
],
|
||||
width: 100,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
label: '身份证号',
|
||||
prop: 'idCardNo',
|
||||
search: true,
|
||||
searchOrder: 2,
|
||||
width: 190,
|
||||
},
|
||||
{
|
||||
label: '所属组织',
|
||||
prop: 'organizationName',
|
||||
search: true,
|
||||
searchOrder: 1,
|
||||
type: 'select',
|
||||
filterable: true,
|
||||
dicData: [],
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
label: '性别',
|
||||
prop: 'gender',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
label: '岗位',
|
||||
prop: 'posts',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: '驾驶认证',
|
||||
prop: 'drivingLicenseEndDate',
|
||||
|
||||
@@ -3,7 +3,10 @@ export const option = {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
@@ -14,29 +17,49 @@ export const option = {
|
||||
menuWidth: 210,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{ label: '船舶名', prop: 'shipName', search: true, slot: true, width: 130 },
|
||||
{ label: '船舶识别号', prop: 'shipIdentifierNo', search: true, width: 140 },
|
||||
{ label: '所属组织', prop: 'organizationName', search: true, width: 160 },
|
||||
{ label: '船检登记号', prop: 'shipInspectionNo', search: true, width: 130 },
|
||||
{ label: '船舶名', prop: 'shipName', search: true, searchOrder: 5, slot: true, width: 130 },
|
||||
{
|
||||
label: '船舶识别号',
|
||||
prop: 'shipIdentifierNo',
|
||||
search: true,
|
||||
searchOrder: 4,
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
label: '船检登记号',
|
||||
prop: 'shipInspectionNo',
|
||||
search: true,
|
||||
searchOrder: 3,
|
||||
width: 130,
|
||||
},
|
||||
{
|
||||
label: '船舶类型',
|
||||
prop: 'shipType',
|
||||
search: true,
|
||||
searchOrder: 2,
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{ label: '干货船', value: '干货船' },
|
||||
{ label: '集装箱船', value: '集装箱船' },
|
||||
{ label: '散货船', value: '散货船' },
|
||||
{ label: '集装箱船', value: '集装箱船' },
|
||||
{ label: '杂货船', value: '杂货船' },
|
||||
{ label: '油船', value: '油船' },
|
||||
{ label: '拖轮', value: '拖轮' },
|
||||
],
|
||||
width: 110,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
label: '所属组织',
|
||||
prop: 'organizationName',
|
||||
search: true,
|
||||
searchOrder: 1,
|
||||
type: 'select',
|
||||
filterable: true,
|
||||
dicData: [],
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
search: true,
|
||||
type: 'select',
|
||||
slot: true,
|
||||
dicData: [
|
||||
@@ -46,7 +69,13 @@ export const option = {
|
||||
width: 90,
|
||||
align: 'center',
|
||||
},
|
||||
{ label: '国籍证有效期至', prop: 'nationalityCertEndDate', slot: true, width: 150, align: 'center' },
|
||||
{
|
||||
label: '国籍证有效期至',
|
||||
prop: 'nationalityCertEndDate',
|
||||
slot: true,
|
||||
width: 150,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
label: '最低安全配员证书有效期至',
|
||||
prop: 'safeManningCertEndDate',
|
||||
|
||||
@@ -3,7 +3,10 @@ export const option = {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
@@ -18,6 +21,7 @@ export const option = {
|
||||
label: '车牌号',
|
||||
prop: 'plateNo',
|
||||
search: true,
|
||||
searchOrder: 5,
|
||||
slot: true,
|
||||
width: 120,
|
||||
},
|
||||
@@ -25,46 +29,41 @@ export const option = {
|
||||
label: '所属组织',
|
||||
prop: 'organizationName',
|
||||
search: true,
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: '车辆类型',
|
||||
prop: 'vehicleType',
|
||||
search: true,
|
||||
searchOrder: 1,
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{ label: '厢式', value: '厢式' },
|
||||
{ label: '高栏', value: '高栏' },
|
||||
{ label: '平板', value: '平板' },
|
||||
{ label: '仓栅', value: '仓栅' },
|
||||
{ label: '牵引车', value: '牵引车' },
|
||||
],
|
||||
width: 110,
|
||||
align: 'center',
|
||||
filterable: true,
|
||||
dicData: [],
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: '业务关系',
|
||||
prop: 'businessRelation',
|
||||
search: true,
|
||||
searchOrder: 4,
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '自有', value: '自有' },
|
||||
{ label: '业务合作', value: '业务合作' },
|
||||
{ label: '管理挂靠', value: '管理挂靠' },
|
||||
{ label: '业务合作', value: '业务合作' },
|
||||
],
|
||||
width: 120,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
label: '能源类型',
|
||||
prop: 'energyType',
|
||||
label: '车辆类型',
|
||||
prop: 'vehicleType',
|
||||
search: true,
|
||||
searchOrder: 3,
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{ label: '柴油', value: '柴油' },
|
||||
{ label: '汽油', value: '汽油' },
|
||||
{ label: '新能源', value: '新能源' },
|
||||
{ label: '天然气', value: '天然气' },
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '高栏车', value: '高栏' },
|
||||
{ label: '低栏车', value: '低栏' },
|
||||
{ label: '平板车', value: '平板' },
|
||||
{ label: '厢式车', value: '厢式' },
|
||||
{ label: '拖挂车', value: '拖挂' },
|
||||
{ label: '半挂车', value: '半挂' },
|
||||
],
|
||||
width: 110,
|
||||
align: 'center',
|
||||
@@ -76,9 +75,10 @@ export const option = {
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
label: '车辆状态',
|
||||
prop: 'status',
|
||||
search: true,
|
||||
searchOrder: 2,
|
||||
type: 'select',
|
||||
slot: true,
|
||||
dicData: [
|
||||
|
||||
@@ -33,17 +33,26 @@ export const accidentResponsibilityDic = [
|
||||
export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 1050,
|
||||
labelPosition: 'top',
|
||||
dialogWidth: 960,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '车船类型',
|
||||
@@ -53,6 +62,7 @@ export const option = {
|
||||
dicData: vehicleTypeDic,
|
||||
value: '车辆',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -62,6 +72,8 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '输入车牌号/船号查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
@@ -74,6 +86,8 @@ export const option = {
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请选择,日期选择器',
|
||||
rules: [{ required: true, message: '请选择事故发生日期', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
@@ -81,6 +95,8 @@ export const option = {
|
||||
prop: 'accidentLocation',
|
||||
minWidth: 200,
|
||||
overHidden: true,
|
||||
span: 12,
|
||||
placeholder: '请输入,100字内',
|
||||
rules: [{ max: 100, message: '最多 100 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -90,6 +106,9 @@ export const option = {
|
||||
dicData: accidentNatureDic,
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择事故性质', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '事故责任',
|
||||
@@ -98,13 +117,19 @@ export const option = {
|
||||
dicData: accidentResponsibilityDic,
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择事故责任', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '直接经济损失',
|
||||
prop: 'directEconomicLoss',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -112,17 +137,22 @@ export const option = {
|
||||
prop: 'insuranceClaimAmount',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '事故原因',
|
||||
label: '事故原因及损坏情况',
|
||||
prop: 'accidentReasonDamage',
|
||||
type: 'textarea',
|
||||
minRows: 3,
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
search: true,
|
||||
hide: true,
|
||||
minWidth: 220,
|
||||
overHidden: true,
|
||||
placeholder: '请输入,500字以内',
|
||||
rules: [{ max: 500, message: '最多 500 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -131,25 +161,18 @@ export const option = {
|
||||
type: 'textarea',
|
||||
minRows: 3,
|
||||
span: 24,
|
||||
hide: true,
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
placeholder: '请输入,200字以内',
|
||||
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '事故评定开始日期',
|
||||
@@ -188,23 +211,12 @@ export const option = {
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '创建开始时间',
|
||||
prop: 'createTimeStart',
|
||||
label: '创建时间',
|
||||
prop: 'createTimeRange',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '创建结束时间',
|
||||
prop: 'createTimeEnd',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
searchRange: true,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
@@ -251,6 +263,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -32,17 +32,26 @@ export const shipInspectionTypeDic = [
|
||||
export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 1050,
|
||||
labelPosition: 'top',
|
||||
dialogWidth: 960,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '车船类型',
|
||||
@@ -52,6 +61,7 @@ export const option = {
|
||||
dicData: vehicleTypeDic,
|
||||
value: '车辆',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -61,6 +71,8 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '输入车牌号/船号查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
@@ -73,6 +85,8 @@ export const option = {
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请选择,日期选择器',
|
||||
rules: [{ required: true, message: '请选择检测评定日期', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
@@ -82,6 +96,8 @@ export const option = {
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ required: true, message: '请选择有效期截止日', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
@@ -90,6 +106,8 @@ export const option = {
|
||||
type: 'select',
|
||||
dicData: vehicleTechnicalLevelDic,
|
||||
minWidth: 130,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择车辆技术等级', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -99,19 +117,24 @@ export const option = {
|
||||
dicData: shipInspectionTypeDic,
|
||||
display: false,
|
||||
minWidth: 130,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择船舶检验类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '客车类型及等级',
|
||||
prop: 'passengerTypeLevel',
|
||||
minWidth: 150,
|
||||
hide: true,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '检测评定单位',
|
||||
prop: 'inspectionUnit',
|
||||
minWidth: 160,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -119,7 +142,10 @@ export const option = {
|
||||
prop: 'fee',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 100,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入费用', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
@@ -129,7 +155,8 @@ export const option = {
|
||||
label: '评定(复核)单位',
|
||||
prop: 'assessmentUnit',
|
||||
minWidth: 160,
|
||||
hide: true,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -138,25 +165,18 @@ export const option = {
|
||||
type: 'textarea',
|
||||
minRows: 3,
|
||||
span: 24,
|
||||
hide: true,
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
placeholder: '请输入,200字以内',
|
||||
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '检测评定开始日期',
|
||||
@@ -195,24 +215,11 @@ export const option = {
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '创建开始时间',
|
||||
prop: 'createTimeStart',
|
||||
label: '创建时间',
|
||||
prop: 'createTimeRange',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '创建结束时间',
|
||||
prop: 'createTimeEnd',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
search: true,
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -258,6 +265,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -22,17 +22,26 @@ const maskEtcCardNo = value => {
|
||||
export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'top',
|
||||
dialogWidth: 960,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
menuFixed: 'right',
|
||||
column: [
|
||||
{
|
||||
@@ -42,22 +51,13 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '输入车牌号模糊查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'ETC卡号',
|
||||
prop: 'etcCardNo',
|
||||
formatter: row => maskEtcCardNo(row.etcCardNo),
|
||||
search: true,
|
||||
minWidth: 220,
|
||||
rules: [
|
||||
{ required: true, message: '请输入ETC卡号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '入口时间',
|
||||
prop: 'entryTime',
|
||||
@@ -65,6 +65,21 @@ export const option = {
|
||||
format: 'YYYY-MM-DD HH:mm',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 170,
|
||||
span: 12,
|
||||
placeholder: '请选择,日期选择器',
|
||||
},
|
||||
{
|
||||
label: 'ETC卡号',
|
||||
prop: 'etcCardNo',
|
||||
formatter: row => maskEtcCardNo(row.etcCardNo),
|
||||
search: true,
|
||||
minWidth: 220,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入ETC卡号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '出口时间',
|
||||
@@ -73,18 +88,38 @@ export const option = {
|
||||
format: 'YYYY-MM-DD HH:mm',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 170,
|
||||
span: 12,
|
||||
placeholder: '请选择,日期选择器',
|
||||
rules: [{ required: true, message: '请选择出口时间', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
label: '入口站',
|
||||
prop: 'entryStation',
|
||||
minWidth: 150,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '交易金额',
|
||||
prop: 'transactionAmount',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 130,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入交易金额', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '出口站',
|
||||
prop: 'exitStation',
|
||||
minWidth: 150,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -94,18 +129,7 @@ export const option = {
|
||||
minWidth: 120,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '交易金额',
|
||||
prop: 'transactionAmount',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 130,
|
||||
rules: [
|
||||
{ required: true, message: '请输入交易金额', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
],
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
@@ -115,24 +139,16 @@ export const option = {
|
||||
span: 24,
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
placeholder: '请输入,200字以内',
|
||||
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '组织',
|
||||
@@ -185,6 +201,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -33,16 +33,23 @@ export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 1100,
|
||||
labelPosition: 'top',
|
||||
labelPosition: 'right',
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '车船类型',
|
||||
@@ -123,6 +130,7 @@ export const option = {
|
||||
prop: 'insuredAmount',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 120,
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
@@ -131,6 +139,7 @@ export const option = {
|
||||
prop: 'premium',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 120,
|
||||
rules: [
|
||||
{ required: true, message: '请输入保费', trigger: 'blur' },
|
||||
@@ -173,23 +182,12 @@ export const option = {
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '创建开始时间',
|
||||
prop: 'createTimeStart',
|
||||
label: '创建时间',
|
||||
prop: 'createTimeRange',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '创建结束时间',
|
||||
prop: 'createTimeEnd',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
searchRange: true,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
@@ -236,6 +234,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -12,66 +12,109 @@ const validateNonNegative = (rule, value, callback) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const vehicleTypeDic = [
|
||||
{ label: '车辆', value: '车辆' },
|
||||
{ label: '船舶', value: '船舶' },
|
||||
];
|
||||
|
||||
export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'top',
|
||||
labelPosition: 'right',
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
menuFixed: 'right',
|
||||
column: [
|
||||
{
|
||||
label: '车牌号',
|
||||
label: '车船类型',
|
||||
prop: 'vehicleType',
|
||||
type: 'radio',
|
||||
dicData: vehicleTypeDic,
|
||||
value: '车辆',
|
||||
search: true,
|
||||
minWidth: 110,
|
||||
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '车牌号/船号',
|
||||
prop: 'vehicleNo',
|
||||
slot: true,
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号', trigger: 'blur' },
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '上月统计日里程表示值(km)',
|
||||
label: '上月统计里程数',
|
||||
prop: 'previousMonthMileage',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '公里',
|
||||
minWidth: 210,
|
||||
slot: true,
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '本月统计日里程表示值(km)',
|
||||
label: '本月统计里程数',
|
||||
prop: 'currentMonthMileage',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '公里',
|
||||
minWidth: 210,
|
||||
slot: true,
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '本月行驶里程(km)',
|
||||
label: '本月行驶里程数',
|
||||
prop: 'monthlyMileage',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '公里',
|
||||
minWidth: 170,
|
||||
slot: true,
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '累计行驶里程(km)',
|
||||
label: '累计行驶里程数',
|
||||
prop: 'totalMileage',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '公里',
|
||||
minWidth: 170,
|
||||
slot: true,
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '里程单位',
|
||||
prop: 'mileageUnit',
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{ label: '公里', value: '公里' },
|
||||
{ label: '海里', value: '海里' },
|
||||
],
|
||||
value: '公里',
|
||||
hide: true,
|
||||
display: false,
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
@@ -85,37 +128,28 @@ export const option = {
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '累计行驶里程开始值',
|
||||
label: '累计行驶里程',
|
||||
prop: 'totalMileageStart',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
search: true,
|
||||
searchSpan: 8,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '累计行驶里程结束值',
|
||||
label: '累计行驶里程数结束值',
|
||||
prop: 'totalMileageEnd',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -134,23 +168,12 @@ export const option = {
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '创建开始时间',
|
||||
prop: 'createTimeStart',
|
||||
label: '创建时间',
|
||||
prop: 'createTimeRange',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '创建结束时间',
|
||||
prop: 'createTimeEnd',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
searchRange: true,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
@@ -197,6 +220,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -26,16 +26,19 @@ export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'top',
|
||||
labelPosition: 'right',
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
menuFixed: 'right',
|
||||
column: [
|
||||
{
|
||||
@@ -85,6 +88,7 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
placeholder: '输入车牌号/船号查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
@@ -159,19 +163,10 @@ export const option = {
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '交易开始时间',
|
||||
@@ -232,24 +227,11 @@ export const option = {
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '创建开始时间',
|
||||
prop: 'createTimeStart',
|
||||
label: '创建时间',
|
||||
prop: 'createTimeRange',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '创建结束时间',
|
||||
prop: 'createTimeEnd',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
search: true,
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -295,6 +277,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -30,37 +30,28 @@ export const expenseTypeDic = [
|
||||
export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'top',
|
||||
dialogWidth: 960,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
menuFixed: 'right',
|
||||
column: [
|
||||
{
|
||||
label: '费用日期',
|
||||
prop: 'expenseDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 140,
|
||||
rules: [{ required: true, message: '请选择费用日期', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
label: '其他费用类型',
|
||||
prop: 'expenseType',
|
||||
type: 'select',
|
||||
dicData: expenseTypeDic,
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
rules: [{ required: true, message: '请选择其他费用类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '车船类型',
|
||||
prop: 'vehicleType',
|
||||
@@ -69,6 +60,7 @@ export const option = {
|
||||
value: '车辆',
|
||||
search: true,
|
||||
minWidth: 110,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -78,11 +70,49 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
span: 12,
|
||||
placeholder: '输入车牌号/船号查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '费用日期',
|
||||
prop: 'expenseDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请选择,日期选择器',
|
||||
rules: [{ required: true, message: '请选择费用日期', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
label: '费用类型',
|
||||
prop: 'expenseType',
|
||||
type: 'select',
|
||||
dicData: expenseTypeDic,
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择费用类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '金额',
|
||||
prop: 'amount',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入金额', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '数据来源',
|
||||
prop: 'dataSource',
|
||||
@@ -90,18 +120,7 @@ export const option = {
|
||||
minWidth: 120,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '金额',
|
||||
prop: 'amount',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 120,
|
||||
rules: [
|
||||
{ required: true, message: '请输入金额', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
],
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
@@ -111,24 +130,16 @@ export const option = {
|
||||
span: 24,
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
placeholder: '请输入,200字以内',
|
||||
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '费用日期',
|
||||
@@ -194,6 +205,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -23,17 +23,26 @@ const validatePositiveInteger = (rule, value, callback) => {
|
||||
export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'top',
|
||||
dialogWidth: 960,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '车牌号',
|
||||
@@ -42,6 +51,8 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
span: 12,
|
||||
placeholder: '输入车牌号查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
@@ -51,6 +62,8 @@ export const option = {
|
||||
label: '处理人',
|
||||
prop: 'handler',
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 20, message: '最多 20 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -60,12 +73,16 @@ export const option = {
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 130,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择换胎时间', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
label: '轮胎品牌',
|
||||
prop: 'tireBrand',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -74,6 +91,8 @@ export const option = {
|
||||
type: 'number',
|
||||
precision: 0,
|
||||
minWidth: 110,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ validator: validatePositiveInteger, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -81,7 +100,10 @@ export const option = {
|
||||
prop: 'replacementCost',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入换胎费用', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
@@ -92,6 +114,8 @@ export const option = {
|
||||
prop: 'replacementDescription',
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
span: 24,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -102,24 +126,16 @@ export const option = {
|
||||
span: 24,
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 500, message: '最多 500 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
@@ -134,23 +150,12 @@ export const option = {
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '创建开始时间',
|
||||
prop: 'createTimeStart',
|
||||
label: '创建时间',
|
||||
prop: 'createTimeRange',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '创建结束时间',
|
||||
prop: 'createTimeEnd',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
searchRange: true,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
@@ -197,6 +202,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -17,16 +17,19 @@ export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'top',
|
||||
labelPosition: 'right',
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
menuFixed: 'right',
|
||||
column: [
|
||||
{
|
||||
@@ -46,6 +49,7 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
placeholder: '输入车牌号/船号模糊查询选择',
|
||||
rules: [{ max: 30, message: '最多 30 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -83,19 +87,10 @@ export const option = {
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
@@ -110,23 +105,12 @@ export const option = {
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '创建开始时间',
|
||||
prop: 'createTimeStart',
|
||||
label: '创建时间',
|
||||
prop: 'createTimeRange',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '创建结束时间',
|
||||
prop: 'createTimeEnd',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
searchRange: true,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
@@ -173,6 +157,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -41,17 +41,26 @@ export const processStatusDic = [
|
||||
export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 1100,
|
||||
labelPosition: 'top',
|
||||
dialogWidth: 960,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '车船类型',
|
||||
@@ -61,6 +70,7 @@ export const option = {
|
||||
dicData: vehicleTypeDic,
|
||||
value: '车辆',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -70,6 +80,8 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '输入车牌号/船号查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
@@ -81,6 +93,8 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入驾驶人/船长', trigger: 'blur' },
|
||||
{ max: 20, message: '最多 20 个字符', trigger: 'blur' },
|
||||
@@ -92,6 +106,8 @@ export const option = {
|
||||
type: 'select',
|
||||
dicData: violationTypeDic,
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -100,6 +116,8 @@ export const option = {
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
display: false,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入事项', trigger: 'blur' },
|
||||
{ max: 100, message: '最多 100 个字符', trigger: 'blur' },
|
||||
@@ -112,6 +130,8 @@ export const option = {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 170,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择时间', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
@@ -119,6 +139,8 @@ export const option = {
|
||||
prop: 'location',
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入地点', trigger: 'blur' },
|
||||
{ max: 100, message: '最多 100 个字符', trigger: 'blur' },
|
||||
@@ -129,7 +151,10 @@ export const option = {
|
||||
prop: 'fineAmount',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 100,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -137,14 +162,17 @@ export const option = {
|
||||
prop: 'deductPoints',
|
||||
type: 'number',
|
||||
precision: 0,
|
||||
hide: true,
|
||||
minWidth: 110,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ validator: validateDeductPoints, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '被罚单位',
|
||||
prop: 'penaltyUnit',
|
||||
minWidth: 150,
|
||||
hide: true,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -152,9 +180,11 @@ export const option = {
|
||||
prop: 'processStatus',
|
||||
type: 'radio',
|
||||
dicData: processStatusDic,
|
||||
value: '已处理',
|
||||
value: '未处理',
|
||||
search: true,
|
||||
clearable: true,
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
rules: [{ required: true, message: '请选择状态', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -163,7 +193,9 @@ export const option = {
|
||||
type: 'textarea',
|
||||
minRows: 3,
|
||||
span: 24,
|
||||
hide: true,
|
||||
minWidth: 220,
|
||||
overHidden: true,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入过程描述', trigger: 'blur' },
|
||||
{ max: 500, message: '最多 500 个字符', trigger: 'blur' },
|
||||
@@ -175,25 +207,18 @@ export const option = {
|
||||
type: 'textarea',
|
||||
minRows: 3,
|
||||
span: 24,
|
||||
hide: true,
|
||||
minWidth: 220,
|
||||
overHidden: true,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 500, message: '最多 500 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
@@ -208,23 +233,12 @@ export const option = {
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '创建开始时间',
|
||||
prop: 'createTimeStart',
|
||||
label: '创建时间',
|
||||
prop: 'createTimeRange',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '创建结束时间',
|
||||
prop: 'createTimeEnd',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
searchRange: true,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
@@ -271,6 +285,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -30,12 +30,10 @@ export default {
|
||||
</script>
|
||||
<style scoped>
|
||||
.logo-title {
|
||||
font-size: 16px;
|
||||
background-image: linear-gradient(120deg, #54b6d0 16%, #3f8bdb, #2c77f1);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
padding-left: 30px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
.logo-img {
|
||||
width: 40px;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
:key="index"
|
||||
@click="handleSelect(item)"
|
||||
>
|
||||
<i :class="[item[iconKey], 'avue-searchs__item-icon']"></i>
|
||||
<i v-if="item[iconKey]" :class="[item[iconKey], 'avue-searchs__item-icon']"></i>
|
||||
<span class="avue-searchs__item-title">{{ item[labelKey] }}</span>
|
||||
<div class="avue-searchs__item-path">
|
||||
{{ item[pathKey] }}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
@click="open(item)"
|
||||
:key="item[labelKey]"
|
||||
>
|
||||
<i :class="item[iconKey]"></i>
|
||||
<i v-if="item[iconKey]" :class="item[iconKey]"></i>
|
||||
<template #title>
|
||||
<span :alt="item[pathKey]">{{ getTitle(item) }}</span>
|
||||
</template>
|
||||
@@ -17,7 +17,7 @@
|
||||
:key="item[labelKey]"
|
||||
>
|
||||
<template #title>
|
||||
<i :class="item[iconKey]"></i>
|
||||
<i v-if="item[iconKey]" :class="item[iconKey]"></i>
|
||||
<span>{{ getTitle(item) }}</span>
|
||||
</template>
|
||||
<template v-for="(child, cindex) in item[childrenKey]" :key="child[labelKey]">
|
||||
@@ -26,7 +26,7 @@
|
||||
@click="open(child)"
|
||||
v-if="validatenull(child[childrenKey])"
|
||||
>
|
||||
<i :class="child[iconKey]"></i>
|
||||
<i v-if="child[iconKey]" :class="child[iconKey]"></i>
|
||||
<template #title>
|
||||
<span>{{ getTitle(child) }}</span>
|
||||
</template>
|
||||
|
||||
@@ -161,7 +161,7 @@ export default {
|
||||
roleId: '',
|
||||
},
|
||||
userOption: {
|
||||
labelWidth: 70,
|
||||
labelWidth: 'auto',
|
||||
submitBtn: true,
|
||||
emptyBtn: false,
|
||||
submitText: '切 换',
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'; // default color
|
||||
const DEFAULT_PRIMARY_COLOR = '#409eff';
|
||||
|
||||
export default {
|
||||
name: 'topColor',
|
||||
data() {
|
||||
@@ -17,7 +19,7 @@ export default {
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.themeVal = this.colorName || '#2C77F1';
|
||||
this.themeVal = this.colorName || DEFAULT_PRIMARY_COLOR;
|
||||
},
|
||||
watch: {
|
||||
themeVal(val, oldVal) {
|
||||
@@ -69,6 +71,7 @@ export default {
|
||||
`${this.getLightColor(e, i / 10)}`
|
||||
);
|
||||
}
|
||||
document.documentElement.style.setProperty(`${pre}-dark-2`, `${this.getDarkColor(e, 0.2)}`);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<span v-if="text" @click="handleLock">{{ text }}</span>
|
||||
<i v-else class="icon-suoping" @click="handleLock"></i>
|
||||
<el-dialog title="设置锁屏密码" v-model="box" width="30%" append-to-body>
|
||||
<el-form :model="form" ref="form" label-width="80px">
|
||||
<el-form :model="form" ref="form" label-width="auto">
|
||||
<el-form-item
|
||||
label="锁屏密码"
|
||||
prop="passwd"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<el-menu class="top-menu" :default-active="activeIndex" mode="horizontal" text-color="#333">
|
||||
<el-menu-item index="0" @click="openHome">
|
||||
<template #title>
|
||||
<i :class="itemHome.source" style="padding-right: 5px"></i>
|
||||
<i v-if="itemHome.source" :class="itemHome.source" style="padding-right: 5px"></i>
|
||||
<span>{{ itemHome.name }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
@@ -10,7 +10,7 @@
|
||||
<template v-for="(item, index) in items" :key="index">
|
||||
<el-menu-item :index="item.id + ''" @click="openMenu(item)">
|
||||
<template #title>
|
||||
<i :class="item.source" style="padding-right: 5px"></i>
|
||||
<i v-if="item.source" :class="item.source" style="padding-right: 5px"></i>
|
||||
<span>{{ item.name }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@select="handleSelect"
|
||||
>
|
||||
<template #="{ item }">
|
||||
<i :class="[item[iconKey], 'icon']"></i>
|
||||
<i v-if="item[iconKey]" :class="[item[iconKey], 'icon']"></i>
|
||||
<div class="name">{{ item[labelKey] }}</div>
|
||||
<div class="addr">{{ item[pathKey] }}</div>
|
||||
</template>
|
||||
|
||||
@@ -171,7 +171,7 @@ RouterPlugin.install = function (option = {}) {
|
||||
export const formatPath = (ele, first) => {
|
||||
const propsDefault = website.menu;
|
||||
const icon = ele[propsDefault.icon];
|
||||
ele[propsDefault.icon] = !icon ? propsDefault.iconDefault : icon;
|
||||
ele[propsDefault.icon] = icon || '';
|
||||
ele.meta = {
|
||||
keepAlive: ele.isOpen === 2,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { setStore, getStore, removeStore } from 'utils/store';
|
||||
import website from '@/config/website';
|
||||
|
||||
const DEFAULT_PRIMARY_COLOR = '#409eff';
|
||||
const LEGACY_PRIMARY_COLORS = ['#2C77F1', '#5C9DF8'];
|
||||
const storedColorName = getStore({ name: 'colorName' });
|
||||
|
||||
const common = {
|
||||
state: {
|
||||
language: getStore({ name: 'language' }) || 'zh-cn',
|
||||
@@ -10,7 +14,10 @@ const common = {
|
||||
isSearch: false,
|
||||
isRefresh: true,
|
||||
isLock: getStore({ name: 'isLock' }),
|
||||
colorName: getStore({ name: 'colorName' }) || '#2C77F1',
|
||||
colorName:
|
||||
!storedColorName || LEGACY_PRIMARY_COLORS.includes(String(storedColorName).toUpperCase())
|
||||
? DEFAULT_PRIMARY_COLOR
|
||||
: storedColorName,
|
||||
themeName: getStore({ name: 'themeName' }) || 'theme-go',
|
||||
lockPasswd: getStore({ name: 'lockPasswd' }) || '',
|
||||
website: website,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
//图标配置
|
||||
@use './iconfont.scss';
|
||||
//登录样式
|
||||
@use "./login.scss";
|
||||
@use './login.scss';
|
||||
//适配
|
||||
@use './media.scss';
|
||||
//滚动条样式
|
||||
@@ -30,6 +30,13 @@ a {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
.el-menu-item span,
|
||||
.el-sub-menu__title span {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
@@ -40,7 +47,7 @@ a {
|
||||
.avue-layout,
|
||||
.login-logo,
|
||||
.avue-main {
|
||||
transition: all .3s;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.avue-layout {
|
||||
@@ -50,7 +57,6 @@ a {
|
||||
|
||||
&:not(.avue-layout--horizontal) {
|
||||
.avue-sidebar {
|
||||
background-image: url('/img/左侧背景放底部.png') !important;
|
||||
background-repeat: no-repeat !important;
|
||||
background-position: left bottom !important;
|
||||
background-size: 100% auto !important;
|
||||
@@ -65,7 +71,9 @@ a {
|
||||
height: variables.$top_height;
|
||||
display: flex;
|
||||
|
||||
.avue-menu, .el-menu-item, .el-sub-menu__title {
|
||||
.avue-menu,
|
||||
.el-menu-item,
|
||||
.el-sub-menu__title {
|
||||
height: variables.$top_height;
|
||||
line-height: variables.$top_height;
|
||||
}
|
||||
@@ -76,7 +84,7 @@ a {
|
||||
}
|
||||
|
||||
.avue-logo {
|
||||
width: variables.$sidebar_width
|
||||
width: variables.$sidebar_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +112,7 @@ a {
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
background: #f0f2f5;
|
||||
background: #f4f6fa;
|
||||
}
|
||||
|
||||
#avue-view {
|
||||
@@ -112,6 +120,7 @@ a {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
margin-bottom: 10px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.avue-view {
|
||||
@@ -132,7 +141,7 @@ a {
|
||||
}
|
||||
|
||||
.mac_bg {
|
||||
background-image: url("/img/bg.jpg");
|
||||
background-image: url('/img/bg.jpg');
|
||||
background-color: #000;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
:root {
|
||||
--el-color-primary: #409eff;
|
||||
--el-color-primary-light-1: #53a8ff;
|
||||
--el-color-primary-light-2: #66b1ff;
|
||||
--el-color-primary-light-3: #79bbff;
|
||||
--el-color-primary-light-4: #8cc5ff;
|
||||
--el-color-primary-light-5: #a0cfff;
|
||||
--el-color-primary-light-6: #b3d8ff;
|
||||
--el-color-primary-light-7: #c6e2ff;
|
||||
--el-color-primary-light-8: #d9ecff;
|
||||
--el-color-primary-light-9: #ecf5ff;
|
||||
--el-color-primary-dark-2: #337ecc;
|
||||
}
|
||||
|
||||
.el-card.is-always-shadow {
|
||||
box-shadow: none;
|
||||
border: none !important;
|
||||
@@ -22,7 +36,6 @@
|
||||
z-index: 2048;
|
||||
}
|
||||
|
||||
|
||||
.el-col {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
@@ -31,12 +44,19 @@
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item--divided:before, .el-menu, .el-menu--horizontal > .el-menu-item:not(.is-disabled):focus, .el-menu--horizontal > .el-menu-item:not(.is-disabled):hover, .el-menu--horizontal > .el-sub-menu .el-sub-menu__title:hover {
|
||||
.el-dropdown-menu__item--divided:before,
|
||||
.el-menu,
|
||||
.el-menu--horizontal > .el-menu-item:not(.is-disabled):focus,
|
||||
.el-menu--horizontal > .el-menu-item:not(.is-disabled):hover,
|
||||
.el-menu--horizontal > .el-sub-menu .el-sub-menu__title:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
.el-dropdown-menu__item--divided:before, .el-menu, .el-menu--horizontal > .el-menu-item:not(.is-disabled):focus, .el-menu--horizontal > .el-menu-item:not(.is-disabled):hover, .el-menu--horizontal > .el-sub-menu .el-sub-menu__title:hover {
|
||||
.el-dropdown-menu__item--divided:before,
|
||||
.el-menu,
|
||||
.el-menu--horizontal > .el-menu-item:not(.is-disabled):focus,
|
||||
.el-menu--horizontal > .el-menu-item:not(.is-disabled):hover,
|
||||
.el-menu--horizontal > .el-sub-menu .el-sub-menu__title:hover {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
@@ -49,7 +69,8 @@
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.el-button.is-text:not(.is-disabled):focus, .el-button.is-text:not(.is-disabled):hover {
|
||||
.el-button.is-text:not(.is-disabled):focus,
|
||||
.el-button.is-text:not(.is-disabled):hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
.el-dropdown {
|
||||
@@ -60,7 +81,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.avue-icon i, .avue-icon svg {
|
||||
.avue-icon i,
|
||||
.avue-icon svg {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
@@ -68,6 +90,147 @@
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.basic-container__card .avue-crud > .el-card,
|
||||
.basic-container__card .avue-crud__pagination.el-card {
|
||||
border: none;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.basic-container__card .avue-crud__search.el-card {
|
||||
border: none;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.basic-container__card .avue-crud > .el-card > .el-card__body,
|
||||
.basic-container__card .avue-crud__pagination.el-card > .el-card__body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.basic-container__card .avue-crud__search.el-card > .el-card__body {
|
||||
padding: 12px 12px 4px;
|
||||
}
|
||||
|
||||
.basic-container__card .avue-crud__header {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.avue-crud__header,
|
||||
.avue-crud .el-table th .cell,
|
||||
.avue-crud .el-table td .cell {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.avue-crud__search .el-form-item,
|
||||
.avue-crud__search .el-form-item--default.el-form-item,
|
||||
.avue-crud__search .el-form-item--small.el-form-item,
|
||||
.avue-crud__search .el-form-item--large.el-form-item,
|
||||
.avue-crud__search .el-form-item--medium.el-form-item {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.avue-crud__search .el-form-item__label {
|
||||
min-width: 160px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.avue-crud__search.el-card {
|
||||
border: none;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.avue-crud__search.el-card > .el-card__body {
|
||||
padding: 12px 12px 4px;
|
||||
}
|
||||
|
||||
.avue-crud__search {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.avue-crud__search .avue-form__menu,
|
||||
.avue-crud__search .avue-form__menu--right {
|
||||
display: flex;
|
||||
flex: 0 0 100%;
|
||||
justify-content: flex-end;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.el-dialog .el-form-item__label {
|
||||
justify-content: flex-end;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.el-dialog .el-col {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.el-dialog .el-form-item,
|
||||
.el-dialog .el-form-item--default.el-form-item,
|
||||
.el-dialog .el-form-item--small.el-form-item,
|
||||
.el-dialog .el-form-item--large.el-form-item,
|
||||
.el-dialog .el-form-item--medium.el-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.el-dialog__title,
|
||||
.dialog-section-title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.el-dialog__title:not(:empty)::before,
|
||||
.dialog-section-title::before {
|
||||
content: '';
|
||||
flex: none;
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
border-radius: 2px;
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.avue-crud__dialog .avue-dialog__footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.avue-crud__dialog .avue-dialog__footer--left {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.avue-crud__dialog .avue-dialog__footer--center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.avue-crud__dialog .avue-dialog__footer > .el-button {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.avue-crud__dialog .avue-dialog__footer > .el-button:last-of-type {
|
||||
order: 1;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.avue-crud__dialog .avue-dialog__footer > .el-button.el-button--primary {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
// 统一表格线条颜色
|
||||
.el-table {
|
||||
--el-table-border-color: #eff1f7;
|
||||
--el-table-border: 1px solid var(--el-table-border-color);
|
||||
}
|
||||
|
||||
.el-table,
|
||||
.el-table th.el-table__cell,
|
||||
.el-table td.el-table__cell {
|
||||
border-color: #eff1f7;
|
||||
}
|
||||
|
||||
// 统一 avue-crud 表头背景色
|
||||
.avue-crud .el-table th.el-table__cell,
|
||||
.avue-crud .el-table__header-wrapper tr th.el-table-fixed-column--left,
|
||||
@@ -75,8 +238,212 @@
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
// 统一 avue-crud 偶数行背景色
|
||||
.avue-crud .el-table__body-wrapper tr.el-table__row:nth-child(even) td.el-table__cell,
|
||||
.avue-crud .el-table__body-wrapper tr.el-table__row:nth-child(even) td.el-table-fixed-column--left,
|
||||
.avue-crud
|
||||
.el-table__body-wrapper
|
||||
tr.el-table__row:nth-child(even)
|
||||
td.el-table-fixed-column--right {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
// 统一 avue-crud 行 hover 背景色
|
||||
.avue-crud .el-table__body-wrapper tr.hover-row td.el-table__cell,
|
||||
.avue-crud .el-table__body-wrapper tr.hover-row td.el-table-fixed-column--left,
|
||||
.avue-crud .el-table__body-wrapper tr.hover-row td.el-table-fixed-column--right {
|
||||
background-color: var(--el-table-row-hover-bg-color);
|
||||
}
|
||||
|
||||
.avue-crud__header {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
// 统一分页为按钮样式并靠右展示
|
||||
.avue-crud__pagination,
|
||||
.empty-pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 10px 0 !important;
|
||||
}
|
||||
|
||||
.avue-crud__pagination .el-pagination,
|
||||
.empty-pagination .el-pagination,
|
||||
.el-pagination {
|
||||
--el-pagination-button-width: 36px;
|
||||
--el-pagination-button-height: 32px;
|
||||
--el-pagination-border-radius: 4px;
|
||||
--el-pagination-button-bg-color: #fff;
|
||||
--el-pagination-bg-color: #fff;
|
||||
--el-pagination-hover-color: var(--el-color-primary);
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
gap: 6px;
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.el-pagination__total {
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.el-pagination__sizes {
|
||||
display: inline-flex !important;
|
||||
margin: 0 6px 0 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-pagination__sizes .el-select {
|
||||
width: 112px;
|
||||
}
|
||||
|
||||
.el-pagination__sizes .el-select__wrapper {
|
||||
min-height: 32px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 0 1px #dcdfe6 inset;
|
||||
}
|
||||
|
||||
.el-pagination.is-background .btn-prev,
|
||||
.el-pagination.is-background .btn-next,
|
||||
.el-pagination.is-background .el-pager li,
|
||||
.el-pagination .btn-prev,
|
||||
.el-pagination .btn-next,
|
||||
.el-pagination .el-pager li,
|
||||
.erp-pagination-button {
|
||||
min-width: 36px;
|
||||
height: 32px;
|
||||
margin: 0 !important;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
color: #303133;
|
||||
line-height: 30px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.el-pagination .btn-prev,
|
||||
.el-pagination .btn-next,
|
||||
.erp-pagination-first,
|
||||
.erp-pagination-last {
|
||||
min-width: 54px;
|
||||
}
|
||||
|
||||
.el-pagination .el-pager {
|
||||
gap: 6px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.el-pagination .btn-prev .el-icon,
|
||||
.el-pagination .btn-next .el-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-pagination .btn-prev::before {
|
||||
content: '上一页';
|
||||
}
|
||||
|
||||
.el-pagination .btn-next::before {
|
||||
content: '下一页';
|
||||
}
|
||||
|
||||
.el-pagination.is-background .el-pager li.is-active,
|
||||
.el-pagination .el-pager li.is-active {
|
||||
border-color: var(--el-color-primary);
|
||||
background-color: var(--el-color-primary);
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.el-pagination.is-background .btn-prev:hover,
|
||||
.el-pagination.is-background .btn-next:hover,
|
||||
.el-pagination.is-background .el-pager li:hover,
|
||||
.el-pagination .btn-prev:hover,
|
||||
.el-pagination .btn-next:hover,
|
||||
.el-pagination .el-pager li:hover,
|
||||
.erp-pagination-button:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-pagination.is-background .el-pager li.is-active:hover,
|
||||
.el-pagination .el-pager li.is-active:hover,
|
||||
.erp-pagination-jump:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-pagination .btn-prev:disabled,
|
||||
.el-pagination .btn-prev.is-disabled,
|
||||
.el-pagination .btn-next:disabled,
|
||||
.el-pagination .btn-next.is-disabled,
|
||||
.erp-pagination-button:disabled {
|
||||
border-color: #ebeef5;
|
||||
background: #fff;
|
||||
color: #c0c4cc;
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.el-pagination__jump {
|
||||
margin-left: 6px;
|
||||
color: #606266;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.el-pagination__goto {
|
||||
margin-right: 0;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.el-pagination__goto::before {
|
||||
content: '第';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.el-pagination__editor.el-input {
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
.el-pagination__editor .el-input__wrapper {
|
||||
height: 32px;
|
||||
padding: 0 6px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 0 1px #dcdfe6 inset;
|
||||
}
|
||||
|
||||
.el-pagination__editor .el-input__inner {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.el-pagination__classifier {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.erp-pagination-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.erp-pagination-jump {
|
||||
min-width: 60px;
|
||||
border-color: var(--el-color-primary);
|
||||
background: var(--el-color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.avue-crud__pagination .el-pagination,
|
||||
.empty-pagination .el-pagination,
|
||||
.el-pagination {
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
user-select: none;
|
||||
position: relative;
|
||||
padding: 0 10px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 5px;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid #f6f6f6;
|
||||
background-color: #fff;
|
||||
|
||||
@@ -46,16 +46,16 @@
|
||||
|
||||
.avue-logo {
|
||||
width:100%;
|
||||
height:50px;
|
||||
line-height: 50px;
|
||||
height:60px;
|
||||
line-height: 60px;
|
||||
background:#fff;
|
||||
color:#333;
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;
|
||||
}
|
||||
|
||||
.avue-top{
|
||||
height:50px;
|
||||
line-height: 50px;
|
||||
height:60px;
|
||||
line-height: 60px;
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;
|
||||
.top-bar__left i, .top-bar__right i{
|
||||
line-height: 50px;
|
||||
@@ -74,11 +74,11 @@
|
||||
}
|
||||
}
|
||||
.avue-main{
|
||||
background-color: rgb(249,250,251);
|
||||
background-color: #f4f6fa;
|
||||
}
|
||||
.avue-tags {
|
||||
margin: 5px 6px 8px 6px;
|
||||
padding-bottom: 2px;
|
||||
margin-left: 1px;
|
||||
|
||||
.el-tabs__nav-prev,
|
||||
.el-tabs__nav-next {
|
||||
|
||||
@@ -33,3 +33,50 @@
|
||||
|
||||
//beautiful
|
||||
@use './beautiful.scss';
|
||||
|
||||
.theme-white,
|
||||
.theme-star,
|
||||
.theme-vip,
|
||||
.theme-d2,
|
||||
.theme-bule,
|
||||
.theme-iview,
|
||||
.theme-go,
|
||||
.theme-hey,
|
||||
.theme-dark,
|
||||
.theme-lte,
|
||||
.theme-cool,
|
||||
.theme-beautiful {
|
||||
.avue-logo {
|
||||
background: var(--el-color-primary) !important;
|
||||
background-color: var(--el-color-primary) !important;
|
||||
color: #fff;
|
||||
|
||||
.avue-logo_title,
|
||||
.avue-logo_subtitle,
|
||||
.logo-title {
|
||||
color: #fff;
|
||||
background-image: none;
|
||||
-webkit-text-fill-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avue-layout:not(.avue-layout--horizontal) {
|
||||
.avue-sidebar {
|
||||
.el-menu-item,
|
||||
.el-sub-menu__title {
|
||||
&.is-active {
|
||||
background-color: #E6EAED !important;
|
||||
|
||||
&::before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
i,
|
||||
span {
|
||||
color: #333 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
.avue-breadcrumb {
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
|
||||
i {
|
||||
color: #fff;
|
||||
font-size: 30px !important;
|
||||
}
|
||||
|
||||
@@ -130,7 +132,7 @@
|
||||
height: 100%;
|
||||
padding-left: 50px;
|
||||
box-sizing: border-box;
|
||||
background: url('/img/系统首页顶部背景图.png') center center / cover no-repeat;
|
||||
background: var(--el-color-primary);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -141,7 +143,7 @@
|
||||
.avue-logo {
|
||||
height: variables.$top_height;
|
||||
line-height: variables.$top_height;
|
||||
background-color: #031527;
|
||||
background-color: var(--el-color-primary);
|
||||
font-size: 20px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
@@ -153,6 +155,7 @@
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&_subtitle {
|
||||
|
||||
54
src/utils/coordinate.js
Normal file
54
src/utils/coordinate.js
Normal file
@@ -0,0 +1,54 @@
|
||||
export const coordinateInputPattern = /^-?\d+(\.\d{0,6})?$/;
|
||||
|
||||
const coordinateRanges = {
|
||||
longitude: {
|
||||
label: '经度',
|
||||
min: -180,
|
||||
max: 180,
|
||||
},
|
||||
latitude: {
|
||||
label: '纬度',
|
||||
min: -90,
|
||||
max: 90,
|
||||
},
|
||||
};
|
||||
|
||||
export const normalizeCoordinateInput = value => {
|
||||
const rawValue = String(value ?? '').replace(/[^\d.-]/g, '');
|
||||
const isNegative = rawValue.startsWith('-');
|
||||
const unsignedValue = rawValue.replace(/-/g, '');
|
||||
if (!unsignedValue) {
|
||||
return isNegative ? '-' : '';
|
||||
}
|
||||
const [integerPart, ...decimalParts] = unsignedValue.split('.');
|
||||
const sign = isNegative ? '-' : '';
|
||||
if (!decimalParts.length) {
|
||||
return `${sign}${integerPart}`;
|
||||
}
|
||||
return `${sign}${integerPart || '0'}.${decimalParts.join('').slice(0, 6)}`;
|
||||
};
|
||||
|
||||
export const isCoordinateInput = value => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return true;
|
||||
}
|
||||
return coordinateInputPattern.test(String(value));
|
||||
};
|
||||
|
||||
export const getCoordinateValidationMessage = (value, type, required = false) => {
|
||||
const range = coordinateRanges[type];
|
||||
if (!range) {
|
||||
return '';
|
||||
}
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return required ? `${range.label}不能为空` : '';
|
||||
}
|
||||
if (!isCoordinateInput(value)) {
|
||||
return `${range.label}最多保留6位小数`;
|
||||
}
|
||||
const numberValue = Number(value);
|
||||
if (numberValue < range.min || numberValue > range.max) {
|
||||
return `${range.label}范围为 ${range.min} 到 ${range.max}`;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
136
src/utils/dialog-form-label.js
Normal file
136
src/utils/dialog-form-label.js
Normal file
@@ -0,0 +1,136 @@
|
||||
const FORM_SELECTOR = '.el-dialog .el-form';
|
||||
const ITEM_SELECTOR = '.el-form-item';
|
||||
const COLUMN_TOLERANCE = 8;
|
||||
const REQUIRED_MARK_WIDTH = 12;
|
||||
|
||||
let textMeasureEl;
|
||||
|
||||
const isVisible = element => {
|
||||
if (!element) return false;
|
||||
const rect = element.getBoundingClientRect();
|
||||
return rect.width > 0 && rect.height > 0;
|
||||
};
|
||||
|
||||
const getDirectLabel = item => {
|
||||
return Array.from(item.children).find(child => child.classList.contains('el-form-item__label'));
|
||||
};
|
||||
|
||||
const getColumnLeft = item => {
|
||||
const column = item.closest('.el-col');
|
||||
const target = column || item;
|
||||
return Math.round(target.getBoundingClientRect().left);
|
||||
};
|
||||
|
||||
const findColumnGroup = (groups, left) => {
|
||||
return groups.find(group => Math.abs(group.left - left) <= COLUMN_TOLERANCE);
|
||||
};
|
||||
|
||||
const getTextMeasureEl = () => {
|
||||
if (textMeasureEl) return textMeasureEl;
|
||||
textMeasureEl = document.createElement('span');
|
||||
textMeasureEl.style.position = 'fixed';
|
||||
textMeasureEl.style.left = '-9999px';
|
||||
textMeasureEl.style.top = '-9999px';
|
||||
textMeasureEl.style.visibility = 'hidden';
|
||||
textMeasureEl.style.whiteSpace = 'nowrap';
|
||||
textMeasureEl.style.pointerEvents = 'none';
|
||||
document.body.appendChild(textMeasureEl);
|
||||
return textMeasureEl;
|
||||
};
|
||||
|
||||
const measureLabelWidth = (label, item) => {
|
||||
const text = String(label.textContent || '').trim();
|
||||
if (!text) return 0;
|
||||
|
||||
const style = window.getComputedStyle(label);
|
||||
const measurer = getTextMeasureEl();
|
||||
measurer.style.font = style.font;
|
||||
measurer.style.letterSpacing = style.letterSpacing;
|
||||
measurer.textContent = text;
|
||||
|
||||
const paddingLeft = Number.parseFloat(style.paddingLeft) || 0;
|
||||
const paddingRight = Number.parseFloat(style.paddingRight) || 0;
|
||||
const requiredWidth = item.classList.contains('is-required') ? REQUIRED_MARK_WIDTH : 0;
|
||||
return Math.ceil(measurer.getBoundingClientRect().width + paddingLeft + paddingRight + requiredWidth);
|
||||
};
|
||||
|
||||
const collectLabelGroups = form => {
|
||||
const groups = [];
|
||||
const items = Array.from(form.querySelectorAll(ITEM_SELECTOR));
|
||||
|
||||
items.forEach(item => {
|
||||
if (!isVisible(item) || item.closest('.avue-form__menu')) return;
|
||||
|
||||
const label = getDirectLabel(item);
|
||||
if (!label || !isVisible(label)) return;
|
||||
|
||||
const width = measureLabelWidth(label, item);
|
||||
if (!width) return;
|
||||
|
||||
const left = getColumnLeft(item);
|
||||
let group = findColumnGroup(groups, left);
|
||||
if (!group) {
|
||||
group = {
|
||||
left,
|
||||
labels: [],
|
||||
width: 0,
|
||||
};
|
||||
groups.push(group);
|
||||
}
|
||||
|
||||
group.labels.push(label);
|
||||
group.width = Math.max(group.width, width);
|
||||
});
|
||||
|
||||
return groups;
|
||||
};
|
||||
|
||||
const alignFormLabelsByColumn = form => {
|
||||
const groups = collectLabelGroups(form);
|
||||
|
||||
groups.forEach(group => {
|
||||
group.labels.forEach(label => {
|
||||
const width = `${group.width}px`;
|
||||
if (label.style.width !== width) {
|
||||
label.style.width = width;
|
||||
}
|
||||
if (label.style.flexBasis !== width) {
|
||||
label.style.flexBasis = width;
|
||||
}
|
||||
if (label.style.justifyContent !== 'flex-end') {
|
||||
label.style.justifyContent = 'flex-end';
|
||||
}
|
||||
if (label.style.textAlign !== 'right') {
|
||||
label.style.textAlign = 'right';
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const alignAllDialogFormLabels = () => {
|
||||
document.querySelectorAll(FORM_SELECTOR).forEach(alignFormLabelsByColumn);
|
||||
};
|
||||
|
||||
export const setupDialogFormLabelAlign = () => {
|
||||
if (typeof window === 'undefined' || typeof document === 'undefined') return;
|
||||
|
||||
let frameId = 0;
|
||||
const scheduleAlign = () => {
|
||||
if (frameId) return;
|
||||
frameId = window.requestAnimationFrame(() => {
|
||||
frameId = 0;
|
||||
alignAllDialogFormLabels();
|
||||
});
|
||||
};
|
||||
|
||||
scheduleAlign();
|
||||
window.addEventListener('resize', scheduleAlign);
|
||||
|
||||
const observer = new MutationObserver(scheduleAlign);
|
||||
observer.observe(document.body, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class', 'style'],
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
};
|
||||
108
src/utils/import-excel.js
Normal file
108
src/utils/import-excel.js
Normal file
@@ -0,0 +1,108 @@
|
||||
import { importBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
const excelPattern = /\.(xls|xlsx)$/;
|
||||
|
||||
const isExcelResponse = res => {
|
||||
const contentType = res.headers['content-type'] || res.data.type || '';
|
||||
return contentType.includes('application/vnd.ms-excel');
|
||||
};
|
||||
|
||||
const parseBlobJson = async blob => {
|
||||
const text = await blob.text();
|
||||
return text ? JSON.parse(text) : {};
|
||||
};
|
||||
|
||||
/**
|
||||
* 加工后端返回的导入失败明细 Excel。
|
||||
* 若传入 failDetailDecorator(函数),则动态加载 exceljs 读取 blob 流,
|
||||
* 交由 decorator 完成改写原因列、调整列宽等加工,再导出新的 blob;
|
||||
* 否则原样返回 blob(保持旧行为,不影响未使用 decorator 的模块)。
|
||||
* @param {Blob} blob 后端返回的失败明细 Excel 流
|
||||
* @param {String} businessName 业务名称(如「货物类型」)
|
||||
* @param {Function} [failDetailDecorator] async (workbook, { businessName }) => void
|
||||
* @returns {Promise<Blob>}
|
||||
*/
|
||||
const decorateFailExcel = async (blob, businessName, failDetailDecorator) => {
|
||||
if (typeof failDetailDecorator !== 'function') return blob;
|
||||
const ExcelJS = await import('exceljs');
|
||||
const buf = await blob.arrayBuffer();
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
await workbook.xlsx.load(buf);
|
||||
await failDetailDecorator(workbook, { businessName });
|
||||
const out = await workbook.xlsx.writeBuffer();
|
||||
return new Blob([out], { type: 'application/vnd.ms-excel' });
|
||||
};
|
||||
|
||||
/**
|
||||
* 打开导入弹窗
|
||||
* @param {Object} vm 组件实例
|
||||
* @param {String} businessName 业务名称,用于失败明细文件命名
|
||||
* @param {Function} [onSuccess] 导入成功回调
|
||||
* @param {Object} [options] 扩展项
|
||||
* @param {Function} [options.failDetailDecorator] 失败明细加工函数,用于对后端返回的失败 Excel 标红/改写原因
|
||||
*/
|
||||
export const openImportDialog = (vm, businessName, onSuccess, options = {}) => {
|
||||
const { failDetailDecorator } = options;
|
||||
const column = vm.findColumn(vm.excelOption.column, 'excelFile');
|
||||
column.httpRequest = (option, uploadColumn) =>
|
||||
handleImportExcel(vm, option, uploadColumn, businessName, onSuccess, { failDetailDecorator });
|
||||
vm.excelBox = true;
|
||||
};
|
||||
|
||||
export const handleImportExcel = async (vm, option, column, businessName, onSuccess, options = {}) => {
|
||||
const { failDetailDecorator } = options;
|
||||
const file = option.file;
|
||||
const fileName = file && file.name ? file.name.toLowerCase() : '';
|
||||
if (!excelPattern.test(fileName)) {
|
||||
vm.$message.error('请上传 .xls,.xlsx 标准格式文件');
|
||||
if (typeof option.onError === 'function') {
|
||||
option.onError(new Error('请上传 .xls,.xlsx 标准格式文件'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
NProgress.start();
|
||||
try {
|
||||
const res = await importBlob(column.action, file);
|
||||
if (isExcelResponse(res)) {
|
||||
const decorated = await decorateFailExcel(res.data, businessName, failDetailDecorator);
|
||||
downloadXls(
|
||||
decorated,
|
||||
`${businessName}导入失败明细${vm.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`
|
||||
);
|
||||
vm.$message.warning('部分数据导入失败,已下载失败明细');
|
||||
} else {
|
||||
const result = await parseBlobJson(res.data);
|
||||
if (result.code !== 200) {
|
||||
vm.$message.error(result.msg || '导入失败');
|
||||
if (typeof option.onError === 'function') {
|
||||
option.onError(new Error(result.msg || '导入失败'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
vm.$message.success(result.msg || '导入数据成功');
|
||||
}
|
||||
vm.excelBox = false;
|
||||
vm.excelForm = {};
|
||||
if (typeof onSuccess === 'function') {
|
||||
onSuccess();
|
||||
} else if (typeof vm.onLoad === 'function') {
|
||||
vm.onLoad(vm.page, vm.query);
|
||||
} else if (typeof vm.initTree === 'function') {
|
||||
vm.initTree();
|
||||
}
|
||||
if (typeof option.onSuccess === 'function') {
|
||||
option.onSuccess(res);
|
||||
}
|
||||
} catch (error) {
|
||||
vm.$message.error(error.message || '导入失败');
|
||||
if (typeof option.onError === 'function') {
|
||||
option.onError(error);
|
||||
}
|
||||
} finally {
|
||||
NProgress.done();
|
||||
}
|
||||
};
|
||||
167
src/utils/pagination.js
Normal file
167
src/utils/pagination.js
Normal file
@@ -0,0 +1,167 @@
|
||||
const FIRST_BUTTON_CLASS = 'erp-pagination-first';
|
||||
const LAST_BUTTON_CLASS = 'erp-pagination-last';
|
||||
const JUMP_BUTTON_CLASS = 'erp-pagination-jump';
|
||||
const DEFAULT_PAGE_SIZE = 10;
|
||||
const DEFAULT_PAGE_SIZES = [10, 20, 50, 100];
|
||||
const PAGE_KEY_PATTERN = /(^page$|Page$|^page[A-Z])/;
|
||||
|
||||
const toNumber = value => {
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) ? number : 0;
|
||||
};
|
||||
|
||||
const getNativeInputValueSetter = input => {
|
||||
const prototype = Object.getPrototypeOf(input);
|
||||
const descriptor = Object.getOwnPropertyDescriptor(prototype, 'value');
|
||||
return descriptor && descriptor.set;
|
||||
};
|
||||
|
||||
const setInputValue = (input, value) => {
|
||||
const setter = getNativeInputValueSetter(input);
|
||||
if (setter) {
|
||||
setter.call(input, value);
|
||||
} else {
|
||||
input.value = value;
|
||||
}
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
};
|
||||
|
||||
const getPaginationState = pagination => {
|
||||
const active = pagination.querySelector('.el-pager li.is-active');
|
||||
const jumpInput = pagination.querySelector('.el-pagination__editor input');
|
||||
const currentPage = toNumber(
|
||||
active && active.textContent ? active.textContent.trim() : jumpInput?.value
|
||||
);
|
||||
const pageCount = toNumber(jumpInput?.getAttribute('max'));
|
||||
return {
|
||||
currentPage: currentPage || 1,
|
||||
pageCount: pageCount || 1,
|
||||
jumpInput,
|
||||
};
|
||||
};
|
||||
|
||||
const changePage = (pagination, page) => {
|
||||
const { currentPage, pageCount, jumpInput } = getPaginationState(pagination);
|
||||
const targetPage = Math.min(Math.max(toNumber(page), 1), pageCount);
|
||||
if (!targetPage || targetPage === currentPage) return;
|
||||
|
||||
const targetPager = Array.from(pagination.querySelectorAll('.el-pager li.number')).find(
|
||||
item => toNumber(item.textContent && item.textContent.trim()) === targetPage
|
||||
);
|
||||
if (targetPager) {
|
||||
targetPager.click();
|
||||
return;
|
||||
}
|
||||
|
||||
if (jumpInput) {
|
||||
setInputValue(jumpInput, targetPage);
|
||||
jumpInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
jumpInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
||||
}
|
||||
};
|
||||
|
||||
const createPaginationButton = (className, text, onClick) => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = `erp-pagination-button ${className}`;
|
||||
button.textContent = text;
|
||||
button.addEventListener('click', onClick);
|
||||
return button;
|
||||
};
|
||||
|
||||
const setButtonDisabled = (button, disabled) => {
|
||||
if (!button || button.disabled === disabled) return;
|
||||
button.disabled = disabled;
|
||||
};
|
||||
|
||||
const isPageConfig = value =>
|
||||
value &&
|
||||
typeof value === 'object' &&
|
||||
('pageSize' in value || 'currentPage' in value || 'total' in value);
|
||||
|
||||
const normalizePageConfig = page => {
|
||||
if (!isPageConfig(page)) return;
|
||||
page.pageSize = DEFAULT_PAGE_SIZES.includes(toNumber(page.pageSize))
|
||||
? toNumber(page.pageSize)
|
||||
: DEFAULT_PAGE_SIZE;
|
||||
page.pageSizes = DEFAULT_PAGE_SIZES;
|
||||
};
|
||||
|
||||
const normalizeComponentPageConfigs = pagination => {
|
||||
let component = pagination.__vueParentComponent;
|
||||
while (component) {
|
||||
[component.data, component.setupState].forEach(state => {
|
||||
if (!state) return;
|
||||
Object.keys(state)
|
||||
.filter(key => PAGE_KEY_PATTERN.test(key))
|
||||
.forEach(key => normalizePageConfig(state[key]));
|
||||
});
|
||||
component = component.parent;
|
||||
}
|
||||
};
|
||||
|
||||
const updatePaginationButtons = pagination => {
|
||||
const { currentPage, pageCount, jumpInput } = getPaginationState(pagination);
|
||||
const firstButton = pagination.querySelector(`.${FIRST_BUTTON_CLASS}`);
|
||||
const lastButton = pagination.querySelector(`.${LAST_BUTTON_CLASS}`);
|
||||
const jumpButton = pagination.querySelector(`.${JUMP_BUTTON_CLASS}`);
|
||||
|
||||
setButtonDisabled(firstButton, currentPage <= 1);
|
||||
setButtonDisabled(lastButton, currentPage >= pageCount);
|
||||
setButtonDisabled(jumpButton, !jumpInput || jumpInput.disabled);
|
||||
};
|
||||
|
||||
const enhancePagination = pagination => {
|
||||
const prevButton = pagination.querySelector('.btn-prev');
|
||||
const nextButton = pagination.querySelector('.btn-next');
|
||||
const jumpWrapper = pagination.querySelector('.el-pagination__jump');
|
||||
|
||||
normalizeComponentPageConfigs(pagination);
|
||||
|
||||
if (prevButton && !pagination.querySelector(`.${FIRST_BUTTON_CLASS}`)) {
|
||||
const firstButton = createPaginationButton(FIRST_BUTTON_CLASS, '首页', () => {
|
||||
changePage(pagination, 1);
|
||||
});
|
||||
prevButton.before(firstButton);
|
||||
}
|
||||
|
||||
if (nextButton && !pagination.querySelector(`.${LAST_BUTTON_CLASS}`)) {
|
||||
const lastButton = createPaginationButton(LAST_BUTTON_CLASS, '尾页', () => {
|
||||
const { pageCount } = getPaginationState(pagination);
|
||||
changePage(pagination, pageCount);
|
||||
});
|
||||
nextButton.after(lastButton);
|
||||
}
|
||||
|
||||
if (jumpWrapper && !pagination.querySelector(`.${JUMP_BUTTON_CLASS}`)) {
|
||||
const jumpButton = createPaginationButton(JUMP_BUTTON_CLASS, '跳转', () => {
|
||||
const { jumpInput } = getPaginationState(pagination);
|
||||
if (!jumpInput) return;
|
||||
changePage(pagination, jumpInput.value);
|
||||
});
|
||||
jumpWrapper.after(jumpButton);
|
||||
}
|
||||
|
||||
updatePaginationButtons(pagination);
|
||||
};
|
||||
|
||||
const enhanceAllPaginations = () => {
|
||||
document.querySelectorAll('.el-pagination').forEach(enhancePagination);
|
||||
};
|
||||
|
||||
export const setupPaginationEnhancer = () => {
|
||||
if (typeof window === 'undefined' || typeof document === 'undefined') return;
|
||||
|
||||
const scheduleEnhance = () => {
|
||||
window.requestAnimationFrame(enhanceAllPaginations);
|
||||
};
|
||||
|
||||
scheduleEnhance();
|
||||
const observer = new MutationObserver(scheduleEnhance);
|
||||
observer.observe(document.body, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class', 'disabled'],
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
};
|
||||
27
src/utils/search-range.js
Normal file
27
src/utils/search-range.js
Normal file
@@ -0,0 +1,27 @@
|
||||
export const normalizeSearchRangeParams = (params = {}, rangeMap = {}) => {
|
||||
const values = { ...params };
|
||||
|
||||
Object.entries(rangeMap).forEach(([rangeProp, mapping]) => {
|
||||
const [startProp, endProp, startSuffix = '00:00:00', endSuffix = '23:59:59'] = Array.isArray(
|
||||
mapping
|
||||
)
|
||||
? mapping
|
||||
: [mapping.startProp, mapping.endProp, mapping.startSuffix, mapping.endSuffix];
|
||||
|
||||
const range = values[rangeProp];
|
||||
if (Array.isArray(range) && range.length === 2) {
|
||||
values[startProp] = range[0] ? `${range[0]} ${startSuffix}` : undefined;
|
||||
values[endProp] = range[1] ? `${range[1]} ${endSuffix}` : undefined;
|
||||
}
|
||||
|
||||
delete values[rangeProp];
|
||||
});
|
||||
|
||||
Object.keys(values).forEach(key => {
|
||||
if (values[key] === undefined || values[key] === null || values[key] === '') {
|
||||
delete values[key];
|
||||
}
|
||||
});
|
||||
|
||||
return values;
|
||||
};
|
||||
@@ -72,7 +72,7 @@
|
||||
v-for="(item, index) in apiPathList"
|
||||
:key="index"
|
||||
:gutter="8"
|
||||
style="margin-bottom: 8px"
|
||||
style="margin-bottom: 16px"
|
||||
>
|
||||
<el-col :span="22">
|
||||
<el-input
|
||||
@@ -106,7 +106,7 @@
|
||||
v-for="(item, index) in extParamsList"
|
||||
:key="index"
|
||||
:gutter="8"
|
||||
style="margin-bottom: 8px"
|
||||
style="margin-bottom: 16px"
|
||||
>
|
||||
<el-col :span="8">
|
||||
<el-input v-model="item.key" placeholder="参数名" :disabled="type === 'view'" />
|
||||
@@ -330,7 +330,10 @@ export default {
|
||||
menuWidth: 350,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
@@ -493,7 +496,10 @@ export default {
|
||||
tip: false,
|
||||
header: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -157,13 +157,16 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
menuWidth: 250,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
@@ -263,7 +266,10 @@ export default {
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
@@ -324,12 +330,15 @@ export default {
|
||||
optionScope: {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
menuWidth: 250,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
|
||||
@@ -158,13 +158,16 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
menuWidth: 250,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
@@ -300,7 +303,10 @@ export default {
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
@@ -363,12 +369,15 @@ export default {
|
||||
optionScope: {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
menuWidth: 250,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
v-for="(item, index) in limitPatternList"
|
||||
:key="index"
|
||||
:gutter="8"
|
||||
style="margin-bottom: 8px"
|
||||
style="margin-bottom: 16px"
|
||||
>
|
||||
<el-col :span="22">
|
||||
<el-input
|
||||
@@ -208,9 +208,12 @@ export default {
|
||||
dialogWidth: 700,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
labelWidth: 110,
|
||||
searchLabelWidth: 90,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
@@ -360,7 +363,10 @@ export default {
|
||||
tip: false,
|
||||
header: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -242,14 +242,17 @@ export default {
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
menuWidth: 350,
|
||||
dialogWidth: 600,
|
||||
dialogClickModal: false,
|
||||
|
||||
@@ -79,15 +79,45 @@
|
||||
{{ row.status === 1 ? '停用' : '启用' }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #districtCode-form>
|
||||
<el-select
|
||||
v-model="form.districtCode"
|
||||
clearable
|
||||
filterable
|
||||
:loading="districtLoading"
|
||||
:disabled="!form.cityCode"
|
||||
:placeholder="form.cityCode ? '请选择区县' : '请先选择城市'"
|
||||
style="width: 100%"
|
||||
@change="handleDistrictChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in districtOptions"
|
||||
:key="item.id"
|
||||
:label="item.title"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
<template #longitude-form>
|
||||
<el-input
|
||||
v-model="form.longitude"
|
||||
clearable
|
||||
placeholder="请输入经度"
|
||||
@input="handleCoordinateInput('longitude', $event)"
|
||||
/>
|
||||
</template>
|
||||
<template #latitude-form>
|
||||
<el-input
|
||||
v-model="form.latitude"
|
||||
clearable
|
||||
placeholder="请输入纬度"
|
||||
@input="handleCoordinateInput('latitude', $event)"
|
||||
/>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<el-dialog title="空港机场主数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form
|
||||
:option="excelOption"
|
||||
v-model="excelForm"
|
||||
:upload-before="uploadBefore"
|
||||
:upload-after="uploadAfter"
|
||||
>
|
||||
<avue-form :option="excelOption" v-model="excelForm">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
@@ -104,11 +134,15 @@ import { getLazyTree } from '@/api/base/region';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { openImportDialog } from '@/utils/import-excel';
|
||||
import { formatUpdateUserName } from '@/utils/audit';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
const DEFAULT_COUNTRY_CODE = '+86';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
const validateIataCode = (rule, value, callback) => {
|
||||
@@ -126,19 +160,17 @@ export default {
|
||||
}
|
||||
};
|
||||
const validateLongitude = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback();
|
||||
} else if (Number(value) < -180 || Number(value) > 180) {
|
||||
callback(new Error('经度范围为 -180 到 180'));
|
||||
const message = getCoordinateValidationMessage(value, 'longitude');
|
||||
if (message) {
|
||||
callback(new Error(message));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const validateLatitude = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback();
|
||||
} else if (Number(value) < -90 || Number(value) > 90) {
|
||||
callback(new Error('纬度范围为 -90 到 90'));
|
||||
const message = getCoordinateValidationMessage(value, 'latitude');
|
||||
if (message) {
|
||||
callback(new Error(message));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
@@ -152,6 +184,9 @@ export default {
|
||||
excelForm: {},
|
||||
provinceOptions: [],
|
||||
cityOptions: [],
|
||||
districtOptions: [],
|
||||
districtLoading: false,
|
||||
regionInitializing: false,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
@@ -163,9 +198,15 @@ export default {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'right',
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 4,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
@@ -181,8 +222,8 @@ export default {
|
||||
label: '编码',
|
||||
prop: 'code',
|
||||
search: true,
|
||||
addDisabled: true,
|
||||
editDisabled: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
placeholder: '系统自动生成',
|
||||
searchPlaceholder: '请输入编码',
|
||||
},
|
||||
@@ -266,25 +307,80 @@ export default {
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '所属区县',
|
||||
prop: 'districtCode',
|
||||
type: 'select',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
props: {
|
||||
label: 'title',
|
||||
value: 'id',
|
||||
},
|
||||
dicData: [],
|
||||
filterable: true,
|
||||
rules: [{ required: true, message: '请选择区县', trigger: 'change' }],
|
||||
change: ({ value }) => this.handleDistrictChange(value),
|
||||
},
|
||||
{
|
||||
label: '所属区县',
|
||||
prop: 'districtName',
|
||||
minWidth: 140,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '行政区划编号',
|
||||
prop: 'regionCode',
|
||||
minWidth: 160,
|
||||
overHidden: false,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
rules: [{ max: 32, message: '行政区划编码不能超过32字', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '详细地址',
|
||||
prop: 'detailAddress',
|
||||
type: 'textarea',
|
||||
minRows: 3,
|
||||
span: 24,
|
||||
minWidth: 220,
|
||||
overHidden: false,
|
||||
maxlength: 255,
|
||||
showWordLimit: true,
|
||||
rules: [{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '经度',
|
||||
prop: 'longitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
rules: [{ validator: validateLongitude, trigger: 'blur' }],
|
||||
formslot: true,
|
||||
minWidth: 130,
|
||||
overHidden: false,
|
||||
rules: [
|
||||
{ required: true, message: '请输入经度', trigger: 'blur' },
|
||||
{ validator: validateLongitude, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '纬度',
|
||||
prop: 'latitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
rules: [{ validator: validateLatitude, trigger: 'blur' }],
|
||||
formslot: true,
|
||||
minWidth: 130,
|
||||
overHidden: false,
|
||||
rules: [
|
||||
{ required: true, message: '请输入纬度', trigger: 'blur' },
|
||||
{ validator: validateLatitude, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '数据来源',
|
||||
prop: 'dataSource',
|
||||
type: 'select',
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
overHidden: false,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
dicData: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '初始导入', value: '初始导入' },
|
||||
@@ -299,6 +395,10 @@ export default {
|
||||
search: true,
|
||||
slot: true,
|
||||
dataType: 'number',
|
||||
minWidth: 110,
|
||||
overHidden: false,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
dicData: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '启用', value: 1 },
|
||||
@@ -349,6 +449,7 @@ export default {
|
||||
],
|
||||
},
|
||||
excelOption: {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
@@ -385,7 +486,9 @@ export default {
|
||||
},
|
||||
isAdmin() {
|
||||
const authority = this.userInfo && this.userInfo.authority;
|
||||
return Array.isArray(authority) ? authority.includes('admin') : String(authority || '').includes('admin');
|
||||
return Array.isArray(authority)
|
||||
? authority.includes('admin')
|
||||
: String(authority || '').includes('admin');
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
@@ -406,8 +509,8 @@ export default {
|
||||
return this.option.column.find(item => item.prop === prop);
|
||||
},
|
||||
loadProvinceOptions() {
|
||||
getLazyTree('00').then(res => {
|
||||
this.provinceOptions = res.data.data;
|
||||
getLazyTree(DEFAULT_COUNTRY_CODE).then(res => {
|
||||
this.provinceOptions = (res.data.data || []).map(this.normalizeRegionOption);
|
||||
this.findColumn('provinceCode').dicData = this.provinceOptions;
|
||||
});
|
||||
},
|
||||
@@ -418,10 +521,60 @@ export default {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return getLazyTree(provinceCode).then(res => {
|
||||
this.cityOptions = res.data.data;
|
||||
this.cityOptions = (res.data.data || []).map(this.normalizeRegionOption);
|
||||
this.findColumn('cityCode').dicData = this.cityOptions;
|
||||
});
|
||||
},
|
||||
loadDistrictOptions(cityCode) {
|
||||
if (!cityCode) {
|
||||
this.districtOptions = [];
|
||||
this.findColumn('districtCode').dicData = [];
|
||||
return Promise.resolve();
|
||||
}
|
||||
this.districtLoading = true;
|
||||
return getLazyTree(cityCode)
|
||||
.then(res => {
|
||||
this.districtOptions = (res.data.data || []).map(this.normalizeRegionOption);
|
||||
this.findColumn('districtCode').dicData = this.districtOptions;
|
||||
})
|
||||
.finally(() => {
|
||||
this.districtLoading = false;
|
||||
});
|
||||
},
|
||||
normalizeRegionOption(region) {
|
||||
return {
|
||||
...region,
|
||||
id: region.id === undefined || region.id === null ? region.id : String(region.id),
|
||||
};
|
||||
},
|
||||
normalizeRegionForm(row) {
|
||||
return {
|
||||
...row,
|
||||
provinceCode:
|
||||
row.provinceCode === undefined || row.provinceCode === null
|
||||
? row.provinceCode
|
||||
: String(row.provinceCode),
|
||||
cityCode:
|
||||
row.cityCode === undefined || row.cityCode === null ? row.cityCode : String(row.cityCode),
|
||||
districtCode:
|
||||
row.districtCode === undefined || row.districtCode === null
|
||||
? row.districtCode
|
||||
: String(row.districtCode),
|
||||
};
|
||||
},
|
||||
ensureDistrictOption(row) {
|
||||
if (!row.districtCode || !row.districtName) return;
|
||||
const exists = this.districtOptions.some(item => item.id === row.districtCode);
|
||||
if (exists) return;
|
||||
this.districtOptions = [
|
||||
...this.districtOptions,
|
||||
{
|
||||
id: row.districtCode,
|
||||
title: row.districtName,
|
||||
},
|
||||
];
|
||||
this.findColumn('districtCode').dicData = this.districtOptions;
|
||||
},
|
||||
handleIataChange(value) {
|
||||
this.form.iataCode = String(value || '')
|
||||
.toUpperCase()
|
||||
@@ -438,20 +591,67 @@ export default {
|
||||
handleProvinceChange(value) {
|
||||
const province = this.provinceOptions.find(item => item.id === value);
|
||||
this.form.provinceName = province ? province.title : '';
|
||||
if (this.regionInitializing) {
|
||||
this.loadCityOptions(value);
|
||||
return;
|
||||
}
|
||||
this.form.cityCode = undefined;
|
||||
this.form.cityName = '';
|
||||
this.form.districtCode = undefined;
|
||||
this.form.districtName = '';
|
||||
this.form.regionCode = '';
|
||||
this.form.regionName = '';
|
||||
this.loadCityOptions(value);
|
||||
this.loadDistrictOptions();
|
||||
},
|
||||
handleCityChange(value) {
|
||||
const city = this.cityOptions.find(item => item.id === value);
|
||||
this.form.cityName = city ? city.title : '';
|
||||
if (this.regionInitializing) {
|
||||
this.loadDistrictOptions(value);
|
||||
return;
|
||||
}
|
||||
this.form.districtCode = undefined;
|
||||
this.form.districtName = '';
|
||||
this.form.regionCode = '';
|
||||
this.form.regionName = '';
|
||||
this.loadDistrictOptions(value);
|
||||
},
|
||||
handleDistrictChange(value) {
|
||||
const district = this.districtOptions.find(item => item.id === value);
|
||||
this.form.districtName = district ? district.title : '';
|
||||
this.form.regionCode = value || '';
|
||||
this.form.regionName = district ? district.title : '';
|
||||
},
|
||||
handleCoordinateInput(prop, value) {
|
||||
this.form[prop] = normalizeCoordinateInput(value);
|
||||
},
|
||||
normalizeRow(row) {
|
||||
row.code = row.iataCode ? `JC-${row.iataCode}` : row.code;
|
||||
row.regionCode = String(row.regionCode || '').trim();
|
||||
row.regionName = String(row.regionName || '').trim();
|
||||
row.districtCode = String(row.districtCode || '').trim();
|
||||
row.districtName = String(row.districtName || '').trim();
|
||||
row.detailAddress = String(row.detailAddress || '').trim();
|
||||
row.longitude = normalizeCoordinateInput(row.longitude);
|
||||
row.latitude = normalizeCoordinateInput(row.latitude);
|
||||
if (!row.dataSource) row.dataSource = '手动录入';
|
||||
if (!row.status) row.status = 1;
|
||||
return row;
|
||||
},
|
||||
validateCoordinateFields(row) {
|
||||
const longitudeMessage = getCoordinateValidationMessage(row.longitude, 'longitude', true);
|
||||
if (longitudeMessage) {
|
||||
this.$message.warning(longitudeMessage);
|
||||
return false;
|
||||
}
|
||||
const latitudeMessage = getCoordinateValidationMessage(row.latitude, 'latitude', true);
|
||||
if (latitudeMessage) {
|
||||
this.$message.warning(latitudeMessage);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
validateUnique(row) {
|
||||
const rowId = String(row.id || '');
|
||||
const hasDuplicate = (params, prop) => {
|
||||
@@ -483,6 +683,10 @@ export default {
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const submitRow = this.normalizeRow(row);
|
||||
if (!this.validateCoordinateFields(submitRow)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
this.validateUnique(submitRow)
|
||||
.then(() => submit(submitRow))
|
||||
.then(() => {
|
||||
@@ -494,6 +698,10 @@ export default {
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const submitRow = this.normalizeRow(row);
|
||||
if (!this.validateCoordinateFields(submitRow)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
this.validateUnique(submitRow)
|
||||
.then(() => submit(submitRow))
|
||||
.then(() => {
|
||||
@@ -548,16 +756,30 @@ export default {
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
this.regionInitializing = true;
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
this.loadCityOptions(this.form.provinceCode).then(() => done());
|
||||
const detail = this.normalizeRegionForm(res.data.data || {});
|
||||
this.loadCityOptions(detail.provinceCode)
|
||||
.then(() => this.loadDistrictOptions(detail.cityCode))
|
||||
.then(() => {
|
||||
this.ensureDistrictOption(detail);
|
||||
this.form = detail;
|
||||
done();
|
||||
this.$nextTick(() => {
|
||||
window.setTimeout(() => {
|
||||
this.regionInitializing = false;
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.form.code = '';
|
||||
this.form.dataSource = '手动录入';
|
||||
this.form.status = 1;
|
||||
this.loadCityOptions(this.form.provinceCode).then(() => done());
|
||||
this.loadCityOptions(this.form.provinceCode)
|
||||
.then(() => this.loadDistrictOptions(this.form.cityCode))
|
||||
.then(() => done());
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
@@ -599,23 +821,7 @@ export default {
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadBefore(file, done) {
|
||||
const fileName = file && file.name ? file.name.toLowerCase() : '';
|
||||
if (!/\.(xls|xlsx)$/.test(fileName)) {
|
||||
this.$message.error('请上传 .xls,.xlsx 标准格式文件');
|
||||
return false;
|
||||
}
|
||||
if (typeof done === 'function') {
|
||||
done();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
openImportDialog(this, '空港机场主数据');
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出空港机场主数据?', '提示', {
|
||||
|
||||
643
src/views/base/cargo-type.vue
Normal file
643
src/views/base/cargo-type.vue
Normal file
@@ -0,0 +1,643 @@
|
||||
<template>
|
||||
<basic-container class="cargo-type-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
v-model:form="form"
|
||||
ref="crud"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
@row-save="rowSave"
|
||||
@row-update="rowUpdate"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button type="primary" v-if="permission.cargo_type_add" @click="$refs.crud.rowAdd()">
|
||||
<el-icon class="el-icon--left"><Plus /></el-icon>新增
|
||||
</el-button>
|
||||
<el-button type="primary" plain v-if="permission.cargo_type_import" @click="handleImport"
|
||||
><el-icon class="el-icon--left"><Upload /></el-icon>批量导入</el-button
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
v-if="permission.cargo_type_template"
|
||||
@click="handleTemplate"
|
||||
><el-icon class="el-icon--left"><Download /></el-icon>下载模板</el-button
|
||||
>
|
||||
<el-button type="primary" plain v-if="permission.cargo_type_export" @click="handleExport"
|
||||
><el-icon class="el-icon--left"><Download /></el-icon>批量导出</el-button
|
||||
>
|
||||
<el-button type="danger" plain v-if="permission.cargo_type_delete" @click="handleDelete"
|
||||
><el-icon class="el-icon--left"><Delete /></el-icon>批量删除</el-button
|
||||
>
|
||||
</template>
|
||||
|
||||
<template #typeLevel-header>
|
||||
<span>货物类型</span>
|
||||
</template>
|
||||
<template #parentCargoName="{ row }">
|
||||
{{ row.typeLevel === 1 ? '/' : row.parentCargoName || '/' }}
|
||||
</template>
|
||||
<template #parentCargoCode-header>
|
||||
<span>上级货物类型编码</span>
|
||||
</template>
|
||||
<template #parentCargoCode="{ row }">
|
||||
{{ row.typeLevel === 1 ? '/' : row.parentCargoCode || '/' }}
|
||||
</template>
|
||||
<template #createUserName="{ row }">
|
||||
{{ row.createUserName || row.createUser || '' }}
|
||||
</template>
|
||||
|
||||
<template #parentId-form>
|
||||
<el-select
|
||||
v-model="form.parentId"
|
||||
filterable
|
||||
remote
|
||||
clearable
|
||||
:remote-method="loadParentOptions"
|
||||
:loading="parentLoading"
|
||||
:disabled="dialogType === 'view' || form.typeLevel !== 2"
|
||||
placeholder="请输入名称或编码搜索"
|
||||
style="width: 100%"
|
||||
@change="handleParentChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in parentOptions"
|
||||
:key="item.id"
|
||||
:label="`${item.cargoName}(${item.cargoCode})`"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<template #menu="{ row, index }">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
v-if="permission.cargo_type_view"
|
||||
@click="$refs.crud.rowView(row, index)"
|
||||
>
|
||||
查看
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
v-if="permission.cargo_type_edit"
|
||||
@click="$refs.crud.rowEdit(row, index)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="primary" text v-if="permission.cargo_type_delete" @click="rowDel(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<empty-pagination
|
||||
:page="page"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
@load="onLoad(page, query)"
|
||||
/>
|
||||
|
||||
<el-dialog title="货物类型导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getNextCode, getParentOptions, remove, submit } from '@/api/base/cargo-type';
|
||||
import { getCargoTypeOption } from '@/option/base/cargo-type';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { openImportDialog } from '@/utils/import-excel';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
import { Plus, Upload, Download, Delete } from '@element-plus/icons-vue';
|
||||
|
||||
// 导入失败明细中,每个错误文本对应的「字段列」与「规则说明」
|
||||
const CARGO_TYPE_FAIL_RULES = [
|
||||
{ match: '请选择货物类型级别', col: '类型', rule: '仅允许:一级货物类型/二级货物类型' },
|
||||
{ match: '货物类型编码格式不正确', col: '货物类型编码', rule: '一级2位数字,二级4位数字' },
|
||||
{ match: '该货物类型编码已存在', col: '货物类型编码', rule: '请更换唯一编码' },
|
||||
{ match: '请选择上级货物类型', col: '上级货物类型', rule: '二级货物类型必须选择上级' },
|
||||
{
|
||||
match: '二级编码前2位必须与上级货物类型编码一致',
|
||||
col: '货物类型编码',
|
||||
rule: '二级编码前2位必须与上级货物类型编码一致',
|
||||
},
|
||||
{ match: '货物类型名称不能超过50字符', col: '货物类型', rule: '货物类型名称不能超过50字符' },
|
||||
{ match: '备注不能超过200字', col: '备注', rule: '备注不能超过200字' },
|
||||
];
|
||||
|
||||
// 失败明细各列目标宽度(Excel 字符数),key 与后端 CargoTypeImportFailureExcel 表头完全一致
|
||||
const CARGO_TYPE_FAIL_COL_WIDTH = {
|
||||
'*类型': 16,
|
||||
'上级货物类型(如为一级则不需填写)': 22,
|
||||
'上级货物类型编码(如为一级则不需填写)': 26,
|
||||
'*货物类型': 18,
|
||||
'*货物类型编码': 18,
|
||||
备注: 24,
|
||||
导入失败原因: 70,
|
||||
};
|
||||
|
||||
/**
|
||||
* 加工后端返回的导入失败明细 Excel:
|
||||
* - 「导入失败原因」列按 A. B. C. 编号列出,多条以分号隔开
|
||||
* - 每个错误后追加对应规则说明,仅失败原因列标红
|
||||
* - 适当加宽各列、原因列自动换行,避免内容拥挤
|
||||
*/
|
||||
export const decorateCargoTypeFailDetail = async workbook => {
|
||||
const ws = workbook.worksheets[0];
|
||||
if (!ws) return;
|
||||
const headerRow = ws.getRow(1);
|
||||
const colMap = {};
|
||||
headerRow.eachCell((cell, colNumber) => {
|
||||
const name = String(cell.value == null ? '' : cell.value).trim();
|
||||
if (name) colMap[name] = colNumber;
|
||||
});
|
||||
const reasonCol = colMap['导入失败原因'];
|
||||
if (!reasonCol) return;
|
||||
ws.eachRow((row, rowNumber) => {
|
||||
if (rowNumber === 1) return; // 跳过表头
|
||||
// 仅「导入失败原因」列允许红色,其他列红色字体统一清除
|
||||
row.eachCell((cell, colNumber) => {
|
||||
if (colNumber === reasonCol) return;
|
||||
if (cell.font && cell.font.color) {
|
||||
const font = { ...cell.font };
|
||||
delete font.color;
|
||||
cell.font = font;
|
||||
}
|
||||
});
|
||||
const reasonCell = row.getCell(reasonCol);
|
||||
let raw = String(reasonCell.value == null ? '' : reasonCell.value).trim();
|
||||
if (!raw) return;
|
||||
// 去掉后端可能已经存在的「第N行:」前缀,避免重复
|
||||
raw = raw.replace(/^第\s*\d+\s*行[::]\s*/, '');
|
||||
const parts = raw
|
||||
.split(/[;;\n\r]+/)
|
||||
.map(s => s.trim().replace(/^第\s*\d+\s*行[::]\s*/, ''))
|
||||
.filter(Boolean);
|
||||
if (parts.length === 0) return;
|
||||
const decorated = parts.map((part, index) => {
|
||||
const label = `${String.fromCharCode(65 + index)}.`; // A. B. C.
|
||||
const rule = CARGO_TYPE_FAIL_RULES.find(r => part.includes(r.match));
|
||||
if (rule) {
|
||||
// 规则说明若已包含在错误文本中则不再重复拼接
|
||||
return part.includes(rule.rule) ? `${label}${part}` : `${label}${part},${rule.rule}`;
|
||||
}
|
||||
return `${label}${part}`;
|
||||
});
|
||||
// 兜底:失败行的备注列超长(>200字)时,补充备注超长提示
|
||||
const remarkCol = colMap['备注'];
|
||||
if (remarkCol) {
|
||||
const remarkVal = String(
|
||||
row.getCell(remarkCol).value == null ? '' : row.getCell(remarkCol).value
|
||||
).trim();
|
||||
if (remarkVal.length > 200 && !decorated.some(d => d.includes('备注不能超过200字'))) {
|
||||
decorated.push(`${String.fromCharCode(65 + decorated.length)}.备注不能超过200字`);
|
||||
}
|
||||
}
|
||||
reasonCell.value = `第${rowNumber}行:${decorated.join(';')}`;
|
||||
// 仅操作 G 列:黑色字体、无背景填充,其余列保持后端原样
|
||||
reasonCell.fill = { type: 'pattern', pattern: 'none' };
|
||||
reasonCell.font = { color: { argb: 'FF000000' } };
|
||||
reasonCell.alignment = { wrapText: true, vertical: 'top' };
|
||||
});
|
||||
// 加宽各列,避免内容拥挤(仅改列宽,不改动单元格样式)
|
||||
Object.keys(colMap).forEach(name => {
|
||||
const col = ws.getColumn(colMap[name]);
|
||||
col.width = CARGO_TYPE_FAIL_COL_WIDTH[name] || 20;
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
components: { Plus, Upload, Download, Delete },
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
data: [],
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
parentOptions: [],
|
||||
parentLoading: false,
|
||||
dialogType: 'add',
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: getCargoTypeOption(this),
|
||||
excelOption: {
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '模板上传',
|
||||
prop: 'excelFile',
|
||||
type: 'upload',
|
||||
drag: true,
|
||||
loadText: '模板上传中,请稍等',
|
||||
span: 24,
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
},
|
||||
headers: {
|
||||
'Blade-Auth': `bearer ${getToken()}`,
|
||||
},
|
||||
tip: '请上传 .xls,.xlsx 标准格式文件',
|
||||
action: '/blade-system/cargo-type/import-cargo-type',
|
||||
},
|
||||
{
|
||||
label: '模板下载',
|
||||
prop: 'excelTemplate',
|
||||
formslot: true,
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: false,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
return this.selectionList.map(ele => ele.id).join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
findCargoColumn(prop) {
|
||||
return this.option.column.find(item => item.prop === prop) || {};
|
||||
},
|
||||
syncColumnDisplay() {
|
||||
const isChild = this.form.typeLevel === 2;
|
||||
this.findCargoColumn('parentId').display = isChild;
|
||||
this.findCargoColumn('parentCargoCode').display = isChild;
|
||||
this.findCargoColumn('parentCargoCode').addDisplay = isChild;
|
||||
this.findCargoColumn('parentCargoCode').editDisplay = isChild;
|
||||
},
|
||||
handleTypeLevelChange(value) {
|
||||
this.form.typeLevel = value;
|
||||
if (value === 1) {
|
||||
this.form.parentId = undefined;
|
||||
this.form.parentCargoName = '';
|
||||
this.form.parentCargoCode = '';
|
||||
this.form.cargoCode = String(this.form.cargoCode || '')
|
||||
.replace(/\D/g, '')
|
||||
.slice(0, 2);
|
||||
} else {
|
||||
this.form.cargoCode = String(this.form.cargoCode || '')
|
||||
.replace(/\D/g, '')
|
||||
.slice(0, 4);
|
||||
}
|
||||
this.syncColumnDisplay();
|
||||
},
|
||||
handleCargoCodeChange(value) {
|
||||
const maxLength = this.form.typeLevel === 1 ? 2 : 4;
|
||||
this.form.cargoCode = String(value || '')
|
||||
.replace(/\D/g, '')
|
||||
.slice(0, maxLength);
|
||||
},
|
||||
loadParentOptions(keyword = '') {
|
||||
this.parentLoading = true;
|
||||
return getParentOptions(keyword)
|
||||
.then(res => {
|
||||
this.parentOptions = res.data.data || [];
|
||||
})
|
||||
.finally(() => {
|
||||
this.parentLoading = false;
|
||||
});
|
||||
},
|
||||
ensureParentOption(row) {
|
||||
if (!row.parentId || !row.parentCargoName) return;
|
||||
const exists = this.parentOptions.some(item => String(item.id) === String(row.parentId));
|
||||
if (exists) return;
|
||||
this.parentOptions = [
|
||||
...this.parentOptions,
|
||||
{
|
||||
id: row.parentId,
|
||||
cargoName: row.parentCargoName,
|
||||
cargoCode: row.parentCargoCode,
|
||||
},
|
||||
];
|
||||
},
|
||||
handleParentChange(value) {
|
||||
const parent = this.parentOptions.find(item => String(item.id) === String(value));
|
||||
this.form.parentCargoName = parent ? parent.cargoName : '';
|
||||
this.form.parentCargoCode = parent ? parent.cargoCode || parent.code || '' : '';
|
||||
if (!parent) {
|
||||
this.form.cargoCode = '';
|
||||
return;
|
||||
}
|
||||
if (!this.form.parentCargoCode) {
|
||||
this.form.cargoCode = '';
|
||||
return;
|
||||
}
|
||||
getNextCode(this.form.parentCargoCode).then(res => {
|
||||
this.form.cargoCode = res.data.data;
|
||||
});
|
||||
},
|
||||
normalizeRow(row) {
|
||||
row.typeLevel = Number(row.typeLevel || 1);
|
||||
row.cargoName = String(row.cargoName || '').trim();
|
||||
row.cargoCode = String(row.cargoCode || '').trim();
|
||||
row.remark = String(row.remark || '').trim();
|
||||
if (row.typeLevel === 1) {
|
||||
row.parentId = undefined;
|
||||
row.parentCargoName = '';
|
||||
row.parentCargoCode = '';
|
||||
}
|
||||
return row;
|
||||
},
|
||||
validateRow(row) {
|
||||
if (![1, 2].includes(row.typeLevel)) {
|
||||
this.$message.warning('请选择货物类型级别');
|
||||
return false;
|
||||
}
|
||||
if (row.typeLevel === 2 && !row.parentId) {
|
||||
this.$message.warning('请选择上级货物类型');
|
||||
return false;
|
||||
}
|
||||
if (!row.cargoName) {
|
||||
this.$message.warning('请输入货物类型名称');
|
||||
return false;
|
||||
}
|
||||
if (row.cargoName.length > 50) {
|
||||
this.$message.warning('货物类型名称不能超过50字符');
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const submitRow = this.normalizeRow(row);
|
||||
if (!this.validateRow(submitRow)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
submit(submitRow).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const submitRow = this.normalizeRow(row);
|
||||
if (!this.validateRow(submitRow)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
submit(submitRow).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定要删除该货物类型吗?删除后不可恢复。', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.afterDelete(1);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定删除所选数据,删除后不可恢复?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.afterDelete(this.selectionList.length);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
afterDelete(deleteCount) {
|
||||
if (this.page.currentPage > 1 && this.data.length <= deleteCount) {
|
||||
this.page.currentPage -= 1;
|
||||
}
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
this.dialogType = type || 'add';
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
// 列表已返回完整 CargoTypeVO,直接复用 Avue 填充的 this.form,无需再请求 detail
|
||||
this.ensureParentOption(this.form);
|
||||
this.syncColumnDisplay();
|
||||
done();
|
||||
return;
|
||||
}
|
||||
this.form = {
|
||||
typeLevel: 2,
|
||||
cargoName: '',
|
||||
cargoCode: '',
|
||||
parentId: undefined,
|
||||
parentCargoName: '',
|
||||
parentCargoCode: '',
|
||||
remark: '',
|
||||
};
|
||||
this.syncColumnDisplay();
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records || [];
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
openImportDialog(this, '货物类型', undefined, {
|
||||
failDetailDecorator: decorateCargoTypeFailDetail,
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return { ...this.query };
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出货物类型数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-system/cargo-type/export-cargo-type', this.buildExportParams())
|
||||
.then(res => {
|
||||
downloadXls(res.data, `货物类型${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
handleTemplate() {
|
||||
NProgress.start();
|
||||
// 直接下载工程内置的标准导入模板(public 目录静态资源),表头与可正常导入的模板一致,
|
||||
// 含一级(19 类)及全部二级示例,便于用户参照填写层级关系与编码规则
|
||||
const templateUrl = `${import.meta.env.BASE_URL}cargo-type-import-template.xlsx`;
|
||||
fetch(templateUrl)
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error('模板文件不存在');
|
||||
return res.blob();
|
||||
})
|
||||
.then(blob => {
|
||||
downloadXls(blob, '货物类型导入模板.xlsx');
|
||||
})
|
||||
.catch(() => {
|
||||
this.$message.error('模板下载失败,请稍后重试');
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cargo-type-page {
|
||||
:deep(.avue-crud__header) {
|
||||
margin-top: 12px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
// <20><>除 basic-container 卡片内层 el-card 的 padding(规范 4.4)
|
||||
:deep(.basic-container__card) > .el-card__body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// 搜索栏 label 收窄(覆盖 Avue 内置 min-width:160px)
|
||||
:deep(.avue-crud__search) .el-form-item_label {
|
||||
min-width: 50px !important;
|
||||
width: 50px !important;
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
--el-table-border-color: #eff1f7;
|
||||
--el-table-row-hover-bg-color: #f5f7fa;
|
||||
--el-table-row-height: 40px;
|
||||
|
||||
// 表头加高
|
||||
.el-table__header-wrapper .el-table__cell {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
// 数据行保持紧凑
|
||||
.el-table__body .el-table__cell {
|
||||
padding: 6px 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 底部空白区域自适应:表格 body 不再强制撑满计算高度
|
||||
:deep(.avue-crud .el-table__body-wrapper) {
|
||||
max-height: none !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
// 同步让外层表格容器也自适应
|
||||
//:deep(.avue-crud .el-table) {
|
||||
// height: auto !important;
|
||||
//}
|
||||
|
||||
:deep(.el-table__body tr:nth-child(even) > td.el-table__cell) {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
:deep(.el-table__body tr:nth-child(even) > td.el-table-fixed-column--left),
|
||||
:deep(.el-table__body tr:nth-child(even) > td.el-table-fixed-column--right) {
|
||||
background: #fafafa;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user