Compare commits

..

24 Commits

Author SHA1 Message Date
44e7a2692b Merge remote-tracking branch 'origin/master' 2026-08-07 15:37:44 +08:00
8e2ad08bf2 fix(transport-plan): 修复详情页列对齐并调整新增弹窗卡片间距
- 调整发货地址、到货地址及备注字段span设置,实现基本信息区多列平整布局
- 优化标签列固定宽度及内容换行样式,保证表格展示整齐不溢出
- transport-plan新增弹窗各分组白卡间距从12px增至16px,提升视觉分隔效果
- 验证构建成功,确保样式修改不影响整体功能表现
2026-08-07 15:37:37 +08:00
d5528541ce fix(option): 调整收发货信息字段布局
- 取消发货地址字段的额外参数,调整布局样式
- 取消到货地址字段的额外参数,统一字段显示格式
- 取消备注字段的额外参数,简化信息展示方式
2026-08-07 15:29:38 +08:00
9aa1bb4fab feat(core): 统一全站时间列加排序并优化司机管理显示
- 所有含时间的列表列(创建时间、更新时间、起止日期等)均增加排序功能,兼顾Avue option和手写el-table-column
- 司机管理「姓名身份证号」列拆分为独立的「姓名」「身份证号」两列,分别使用插槽渲染
- 修复车辆/船舶/司机管理「所属组织」列前导空格问题,value 使用无缩进原始标签
- 调整司机管理页到期筛选标签位置,移至搜索菜单行并优化样式,删除过渡占位筛选字段
- 增加组织名称列自定义插槽,去除已入库旧数据的前导空白
- 更新相关样式,删除无用样式代码,增加表格列排序样式支持
2026-08-07 15:26:27 +08:00
7fb8113c8e feat(core): 统一全站时间列加排序并优化司机管理显示
- 所有含时间的列表列(创建时间、更新时间、起止日期等)均增加排序功能,兼顾Avue option和手写el-table-column
- 司机管理「姓名身份证号」列拆分为独立的「姓名」「身份证号」两列,分别使用插槽渲染
- 修复车辆/船舶/司机管理「所属组织」列前导空格问题,value 使用无缩进原始标签
- 调整司机管理页到期筛选标签位置,移至搜索菜单行并优化样式,删除过渡占位筛选字段
- 增加组织名称列自定义插槽,去除已入库旧数据的前导空白
- 更新相关样式,删除无用样式代码,增加表格列排序样式支持
2026-08-07 15:11:08 +08:00
04ab648f5b Merge remote-tracking branch 'websoft/master' 2026-08-07 13:15:44 +08:00
f550b55341 1、新增复评权限判断 2026-08-07 13:15:38 +08:00
5aefa23f50 fix(system): 修复部门管理操作列错位问题并统一客商档案弹窗风格
- 修复/system/dept页面操作列竖排错位问题,通过提升CSS选择器特异性并强制inline-flex布局
- 在department页面添加scoped样式,双保险保证操作列横排显示及间距正确
- 将客商档案/vehicle/customer-archive中的表单分组和弹窗内容统一改为section-card组件风格
- 清理废弃旧样式类,提升界面一致性及可维护性
- 调整相关弹窗中卡片内容结构,消除双层灰色背景,使弹窗风格与/business/project-apply一致
- 对评分、联系人、收款信息、发票信息、客商材料、变更记录和证明材料等模块使用全局section-card组件包裹
- 优化样式细节,确保按钮组进入卡头右侧,保证操作体验一致
- 验证构建成功及热更新生效,提醒用户发布需清除旧CSS缓存
2026-08-07 12:47:19 +08:00
c92d2a405a style(ui): 统一操作列文字字重和全站表格文字溢出提示
- 取消操作列内 el-link 默认加粗,统一为常规字体粗细,避免字体显得突兀
- 在 element-ui.scss 中新增样式覆盖 el-link 的 font-weight 为 normal
- 全站表格开启 Element Plus 全局配置 showOverflowTooltip,统一显示单元格文字省略提示
- Avue 全局 crudOption 和手写表格均启用 showOverflowTooltip,覆盖所有相关表格列
- 通过 CSS 豁免操作列单元格不受 tooltip 固定宽度裁切影响,保证操作列内部链接显示完整
- 调整部分菜单宽度以适配视觉和布局需求
- 验证构建成功,无新增 CSS 警告,确保兼容性和功能正常
2026-08-07 12:24:17 +08:00
be4ccc89f6 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	src/views/system/dept.vue
2026-08-07 12:02:41 +08:00
565553b3f7 refactor(table): 统一调整全仓表格操作列宽度及按钮样式
- 将所有操作按钮数量≤3的表格操作列宽度统一调整为180px
- 调整涉及全局默认值、各模块和页面中menuWidth显式设定
- 单独修改/authority/role页面操作列宽度为240px,/system/menu及流程配置对应调为220px
- 通过CSS新规则视觉统一Avue内置操作按钮为el-link链接样式,无功能变更
- 优化通知公告操作列宽度,解决三按钮链接换行问题
- 调整首页Logo图标尺寸及边距以适配设计需求
- 保证所有相关改动经过npm run build验证通过
2026-08-07 12:02:29 +08:00
52d4b6b486 Merge remote-tracking branch 'websoft/master'
# Conflicts:
#	src/option/transportCapacity/transport-ship.js
#	src/option/transportCapacity/transport-vehicle.js
2026-08-07 08:28:42 +08:00
6a02e9126b 1、车船模块fix bug
2、运力模块fix bug
3、新增设备台账
4、其他bug 修复
5、调整组织、人员模块
2026-08-07 08:28:21 +08:00
6364b593ce style(business): 优化派遣摘要及路线项布局样式
- 调整派遣摘要网格列宽,改善整体布局比例
- 增加派遣路线容器间距,提升视觉分隔效果
- 修改派遣路线项最大宽度和内边距,提升内容展示空间
- 添加派遣路线项最小高度及虚线边框,增强区块感和可视性
- 调整派遣路线项边角圆角,优化界面圆润度
2026-08-07 01:41:43 +08:00
bb580b2bfc style(business): 优化派遣摘要及路线项布局样式
- 调整派遣摘要网格列宽,改善整体布局比例
- 增加派遣路线容器间距,提升视觉分隔效果
- 修改派遣路线项最大宽度和内边距,提升内容展示空间
- 添加派遣路线项最小高度及虚线边框,增强区块感和可视性
- 调整派遣路线项边角圆角,优化界面圆润度
2026-08-07 01:41:35 +08:00
6c91889e33 style(business): 优化调度路线组件样式及结构调整
- 用车辆图标替换调度路线横线,提升视觉表现
- 调整调度路线布局,使用 grid 增强对齐和间距控制
- 修改调度路线节点样式,统一圆形背景和阴影效果
- 优化标题、地址、联系人文本样式,增加省略号和颜色区分
- 调整调度路线图标容器样式,增加边框和柔和阴影
- 使用伪元素实现两侧虚线提醒,增强组件连接感
2026-08-07 01:29:32 +08:00
4bef3430e0 refactor(business): 优化派车信息展示和样式布局
- 将基本信息部分整体包裹在section-card内并调整布局顺序
- 调整收发货路线结构,合并地址和联系人信息至路线节点内
- 使用CSS grid替代部分flex布局,优化网格间距和对齐
- 更新文字换行和溢出处理,增强可读性
- 删除冗余的收发货路线详细信息区域,精简代码结构
- 统一section-card的样式和内部内容伸缩布局
- 调整附件显示方式,保证链接样式一致性
2026-08-07 01:21:46 +08:00
17d12d279d refactor(business): 优化派车信息展示和样式布局
- 将基本信息部分整体包裹在section-card内并调整布局顺序
- 调整收发货路线结构,合并地址和联系人信息至路线节点内
- 使用CSS grid替代部分flex布局,优化网格间距和对齐
- 更新文字换行和溢出处理,增强可读性
- 删除冗余的收发货路线详细信息区域,精简代码结构
- 统一section-card的样式和内部内容伸缩布局
- 调整附件显示方式,保证链接样式一致性
2026-08-07 01:03:29 +08:00
9cf005cece refactor(business): 优化派车信息展示和样式布局
- 将基本信息部分整体包裹在section-card内并调整布局顺序
- 调整收发货路线结构,合并地址和联系人信息至路线节点内
- 使用CSS grid替代部分flex布局,优化网格间距和对齐
- 更新文字换行和溢出处理,增强可读性
- 删除冗余的收发货路线详细信息区域,精简代码结构
- 统一section-card的样式和内部内容伸缩布局
- 调整附件显示方式,保证链接样式一致性
2026-08-07 00:54:19 +08:00
d8db9b1edd feat(table): 调整表格操作列宽度并统一文字链接间距
- /business/project-apply 主表操作列宽度固定为 220,移除 applyTableMenuWidth 调用
- /business/temporary-credit-limit 通过传入 menuWidth prop 固定操作列宽度为 180
- /base/common-address 操作列宽度由 220 降至 180
- /business/process-config 操作列宽度由 320 调整为 180,修复末尾语法错误
- /vehicle/insurance-record 操作列宽度先降至 140 后改回 160,后全部车/船务管理模块记录页统一为 180
- 多个车/船务管理模块相关 option 文件及 vue 文件操作列宽统调为 180
- 新增 business-crud-page 组件 menuWidth prop,支持外部传值控制操作列宽度
- 全站操作列文字链接去除 el-link 默认 margin,避免和 gap 叠加导致文字间距过宽
- 优化操作列按钮过多时自动换行展示逻辑,确保按钮不被裁切
2026-08-07 00:44:26 +08:00
cf04d48010 feat(table): 调整表格操作列宽度并统一文字链接间距
- /business/project-apply 主表操作列宽度固定为 220,移除 applyTableMenuWidth 调用
- /business/temporary-credit-limit 通过传入 menuWidth prop 固定操作列宽度为 180
- /base/common-address 操作列宽度由 220 降至 180
- /business/process-config 操作列宽度由 320 调整为 180,修复末尾语法错误
- /vehicle/insurance-record 操作列宽度先降至 140 后改回 160,后全部车/船务管理模块记录页统一为 180
- 多个车/船务管理模块相关 option 文件及 vue 文件操作列宽统调为 180
- 新增 business-crud-page 组件 menuWidth prop,支持外部传值控制操作列宽度
- 全站操作列文字链接去除 el-link 默认 margin,避免和 gap 叠加导致文字间距过宽
- 优化操作列按钮过多时自动换行展示逻辑,确保按钮不被裁切
2026-08-06 23:55:30 +08:00
454608d7be feat(dispatch): 优化运输计划调度弹窗视觉布局并提升紧凑度
- 弹窗标题和单号合并一行,移除分隔符,提升信息整体感
- 基本信息和收发货路线由左右双列改为上下堆叠,改善视觉层次
- 基本信息网格调整为4列布局,货物信息扩大占2列,附件独占一行
- 计划执行时间范围由‘-’改为‘ ~ ’,更清晰表达时间区间
- 路线节点展示改为横向徽章加下方双列详细信息,避免空白和居中留白
- 调整字体大小、颜色及间距,整体风格更简洁且信息更紧凑
- 移除无用边距和min-height约束,使弹窗整体更加紧凑精简
- 更新代理配置,开发期/api请求代理指向指定内网地址,统一路径无需重写,
  以解决跨域问题并保持与后端网关路径一致
- 进行了完整构建验证,确保修改不会影响现有功能稳定性
2026-08-06 23:10:06 +08:00
d82d06dc78 feat(dispatch): 优化运输计划调度弹窗视觉布局并提升紧凑度
- 弹窗标题和单号合并一行,移除分隔符,提升信息整体感
- 基本信息和收发货路线由左右双列改为上下堆叠,改善视觉层次
- 基本信息网格调整为4列布局,货物信息扩大占2列,附件独占一行
- 计划执行时间范围由‘-’改为‘ ~ ’,更清晰表达时间区间
- 路线节点展示改为横向徽章加下方双列详细信息,避免空白和居中留白
- 调整字体大小、颜色及间距,整体风格更简洁且信息更紧凑
- 移除无用边距和min-height约束,使弹窗整体更加紧凑精简
- 更新代理配置,开发期/api请求代理指向指定内网地址,统一路径无需重写,
  以解决跨域问题并保持与后端网关路径一致
- 进行了完整构建验证,确保修改不会影响现有功能稳定性
2026-08-06 23:06:45 +08:00
f03f764472 feat(dispatch): 优化运输计划调度弹窗视觉布局并提升紧凑度
- 弹窗标题和单号合并一行,移除分隔符,提升信息整体感
- 基本信息和收发货路线由左右双列改为上下堆叠,改善视觉层次
- 基本信息网格调整为4列布局,货物信息扩大占2列,附件独占一行
- 计划执行时间范围由‘-’改为‘ ~ ’,更清晰表达时间区间
- 路线节点展示改为横向徽章加下方双列详细信息,避免空白和居中留白
- 调整字体大小、颜色及间距,整体风格更简洁且信息更紧凑
- 移除无用边距和min-height约束,使弹窗整体更加紧凑精简
- 更新代理配置,开发期/api请求代理指向指定内网地址,统一路径无需重写,
  以解决跨域问题并保持与后端网关路径一致
- 进行了完整构建验证,确保修改不会影响现有功能稳定性
2026-08-06 19:53:34 +08:00
110 changed files with 3607 additions and 1050 deletions

View File

@@ -1,5 +1,11 @@
# 2026-08-06 工作日志
## 全站表格操作列文字链接统一为蓝色
- 需求:客户档案页(/vehicle/customer-archive操作列颜色统一蓝色且全站统一风格。
- 实现:在 `src/styles/element-ui.scss` 新增全局规则,覆盖 Avue `#menu` 槽位(`.avue-crud__menu .el-link`)与手写表格操作列(`.el-table .el-table__body .cell:has(.el-link) .el-link`)中所有 `el-link``--el-link-text-color` / hover / active / disabled 变量为主色 `#409eff` 及其浅/深色,从而把 warning/success/danger 等类型强制转蓝。
- 单点生效,无需逐页改 `type`。dev server(2888) 已运行会热更新。
- 注意:选择器以「表格体内含 el-link 的单元格」为目标,本仓库表格内 el-link 仅用于操作列,无副作用;若未来某数据单元格用 el-link 作强调色需另行排除。
## 运输计划调度弹窗 UX 分析
- 完成 `/business/transport-plan` 调度弹窗的 UX 审查,对比 project-apply 页面风格
- 输出 6 项改进方案P0-P5核心问题为缺少灰底(#f5f6fa)+白卡(section-card)分组设计模式
@@ -16,3 +22,95 @@
-`__dispatch-list` / `__dispatch-top`(border-bottom/32px padding) 样式已清理section-card 已全局注册,无需 import
- 验证SFC 模板单独编译通过6 处 section-card 引用配对平衡);完整 vite build 仍因预存 `xlsx` 幽灵依赖pnpm 未装)失败,与本次改动无关
- 注意:本项目完整 build 依赖 `xlsx@0.18.5`package.json 有声明但 node_modules 缺失),属环境预存问题,非本次引入
## 运输计划调度弹窗二次视觉紧凑化
- 客户反馈:弹窗“空、不紧凑、内容多但不好看”
- 改动仍集中在 `business-crud-page.vue`
- 模板:
- 标题与单号/状态标签合并到同一行,移除 `|` 分隔符
- 基本信息与收发货路线由左右双列改为上下堆叠的白卡,各自独占一行
- 基本信息网格改为 4 列:客户 / 项目 / 合同编号 / 计划执行时间;货物信息占 2 列;附件独占一行
- 日期展示由 `-` 改为 ` ~ `,更明确是区间
- 路线改为“横向节点 + 下方双列明细”:起点/终点徽章+名称居中,发货地址/收货地址左对齐分两列,避免居中留白
- 样式:
- 顶部标题字号 22px→18px顶部 padding 22px→0
- 信息标签字号 14px→12px、颜色 #666#909399,值行高 1.45→1.4
- 基本信息网格 3 列/48px 列间距/18px 行间距 → 4 列/24px 列间距/12px 行间距
- 路线徽章 46px→32px、字号 16px→13px、圆角 6px→4px
- 路线标题字号 22px→16px取消居中最长名称省略
- 路线区域取消 `min-height:200px` 和居中,改为紧凑堆叠
- 卡片间统一 `margin-bottom: 12px`
- 验证:`npm run build` 成功
## /business/project-apply 主表操作列收窄
- 需求project-apply 主表avue-crud #menu)操作列太宽,缩小一点。
- 原:`buildTableOption()` 调用 `applyTableMenuWidth(option, 4)` → 强制 `menuWidth: 320`four 档因按钮数≥4。
- 改动:`src/views/business/project-apply.vue``buildTableOption()` 改为直接返回对象并显式设 `menuWidth: 220`,移除 `applyTableMenuWidth` 调用及对应 import仅本文件使用
- 说明220 低于 AGENTS.md「>3 按钮≥320px」约定但属用户明确要求操作列链接已全局支持 flex-wrap按钮多时会自动换行不会裁切。
- 影响范围:仅 project-apply 主表;子表(附件/变更/用户权限)操作列 width 维持 110/100/100 不变;全局 `TABLE_MENU_WIDTH` 未改。
## /business/temporary-credit-limit 操作列宽度固定为 180
- 需求:该页操作列宽度也给 180。
- 背景:该页用共享组件 `business-crud-page.vue`,其 `option` 在 data() 中由 `cloneOption(crudOption, estimateMenuButtonCount(config))` 生成菜单宽度按按钮数估算≥4 按钮→320option 文件本身无 menuWidth。
- 改动:
1. `business-crud-page.vue` 新增可选 prop `menuWidth`Number默认 nulldata() 中生成 `option` 时若 `menuWidth != null` 则强制覆盖 `opt.menuWidth`。默认 null 不影响其他页面。
2. `temporary-credit-limit.vue` 调用处加 `:menu-width="180"`
- 复用提示:后续任何使用 `business-crud-page` 的页面想固定操作列宽度,直接传 `:menu-width="N"` 即可,无需再改 option 或组件逻辑。
- 注意180 同样低于 AGENTS「>3 按钮≥320」约定按用户明确要求链接支持换行兜底。
## /base/common-address 操作列宽度 220 → 180
- 需求:「这个也调一下宽度」。上一条刚把 temporary-credit-limit 设为 180用户用「也」字故对齐为 180。
- 机制:该页是独立 `avue-crud``option` 来自 `@/option/base/common-address.js``createOption()`,其中已显式设 `menuWidth: 220`
- 改动:将 `src/option/base/common-address.js``menuWidth: 220``180`。操作列仅 3 个链接(查看/编辑/删除)。
- 说明:低于 AGENTS「>3 按钮≥320」约定按用户明确要求链接支持换行兜底。
## 操作列宽度的三种设置入口(备忘)
- 独立 avue-crud + 自管 option直接改 option 文件的 `menuWidth`(如 common-address
- 独立 avue-crud + buildTableOption 调 `applyTableMenuWidth(option, N)`helper 会按按钮数强制下限≥4→320要更窄需绕过 helper 直接设 `menuWidth`(如 project-apply 改 220
- 共用 `business-crud-page` 组件:新增可选 prop `menuWidth`(默认 null 不动既有逻辑),调用处传 `:menu-width="N"`(如 temporary-credit-limit 传 180
## /business/process-config 操作列宽度 320 → 180
- 需求:「这个也调一下宽度」。延续上几条把操作列设 180 的系列要求。
- 机制:该页是独立 `avue-crud``option` 来自 `@/option/business/process-config.js`,原先包在 `applyTableMenuWidth({...}, 5)`five 档→强制 `menuWidth: 320`,因菜单有 6 个链接:查看/编辑/复制/启用/停用/删除)。
- 改动:移除 `applyTableMenuWidth` 包裹与对应 importoption 改为普通对象并显式设 `menuWidth: 180`
- 说明180 明显低于 AGENTS「>3 按钮≥320」约定按用户明确要求链接已全局支持 flex-wrap 换行兜底6 个按钮会多行展示、不裁切。
## 修复 process-config.js 末尾语法错误(红色警)
- 现象:用户反馈 `src/option/business/process-config.js` 最后一行有红色警。
- 根因:上一轮把 `applyTableMenuWidth({...}, 5)` 去掉外层函数包裹时,结尾 `}, 5);` 被改成 `});`,多留了一个 `)`(因 `export const option = {` 本身没有左括号),导致括号不匹配。
- 改动:末尾 `});``};`。移除 `applyTableMenuWidth` import 时无误。
- 验证node 动态 import 不再报 SyntaxError仅剩 node 无法解析 extensionless `./common` 的模块解析提示,与语法无关),说明语法已恢复。
## /vehicle/insurance-record 操作列宽度 160 → 140紧凑
- 需求:「这个也调一下宽度,紧凑一点」。本页与上几条不同,用户明确要求更紧凑(其他页给的 180 比这页还宽)。
- 机制:独立 `avue-crud``option` 来自 `@/option/vehicle/insurance-record.js`;无自定义 `#menu` 槽,行菜单用 Avue 内置(仅显式 `viewBtn: true`edit/del 默认开启→3 个链接:查看/编辑/删除)。原已显式 `menuWidth: 160`
- 改动:将 `menuWidth: 160``140`
- 说明:低于 AGENTS「>3 按钮≥320」约定按用户明确要求链接已全局支持 flex-wrap 换行兜底。
## 车/船务管理模块下所有记录页操作列宽度统一为 160
- 需求:用户截图列出「车/船务管理」下 11 个子菜单,要求操作栏宽度都设为 160。
- 对应页面/option 文件:
- 保险记录 insurance-recordoption → `src/option/vehicle/insurance-record.js`(由 140 改回 160
- 违章记录 violation-recordoption → `src/option/vehicle/violation-record.js`(原 160无需改动
- 维修记录 maintenance-recordinline option → `src/views/vehicle/maintenance-record.vue`(原 160无需改动
- 保养记录 maintenance-planinline option → `src/views/vehicle/maintenance-plan.vue`(原 160无需改动
- 换胎记录 tire-replacement-recordoption → `src/option/vehicle/tire-replacement-record.js`(原 160无需改动
- 事故记录 accident-recordoption → `src/option/vehicle/accident-record.js`(原 160无需改动
- 年检记录 annual-inspection-recordoption → `src/option/vehicle/annual-inspection-record.js`(原 160无需改动
- 里程记录 mileage-recordoption → `src/option/vehicle/mileage-record.js`(原 160无需改动
- 变更记录 transport-change-recordoption → `src/option/vehicle/transport-change-record.js`(原 160无需改动
- 油电记录 oil-electric-recordoption → `src/option/vehicle/oil-electric-record.js`(原 160无需改动
- ETC记录 etc-recordoption → `src/option/vehicle/etc-record.js`(原 160无需改动
- 实际改动:仅 `insurance-record.js` 从 140 恢复到 160其余 10 个已经是 160。
- 注意:`src/option/vehicle/other-expense-record.js` 不在截图菜单内,保持不动。
## 车/船务管理模块操作列宽度 160 → 180用户反馈 160 偏小)
- 需求:上一轮统一 160 后,用户觉得还是小,全部改成 180。
- 改动11 个文件 `menuWidth: 160``180`9 个 option 文件 + maintenance-record.vue / maintenance-plan.vue 两处 inline option
- 现状:车/船务管理模块全部 11 个记录页操作列均为 180。
## 操作列文字链接间距过宽问题
- 现象:用户截图反馈操作列文字链接之间间距太宽,要求统一风格。
- 根因:全局已设 `.avue-crud .avue-crud__menu { gap: 8px; }` 与手写表格操作列 `gap: 8px`,但 Avue 默认 `.el-link``.cell` 上可能存在 `margin-right`,与 gap 叠加后显得过宽。
- 改动:`src/styles/element-ui.scss` 新增规则,对 `.avue-crud .avue-crud__menu .el-link` 和手写表格操作列 `.el-link` 强制 `margin: 0`,使间距完全由 8px gap 控制,不再叠加。
- 影响范围全站表格操作列Avue #menu 与手写操作列),仅作用于操作列链接,不修改普通数据单元格。

View File

@@ -0,0 +1,188 @@
# 2026-08-07 工作日志
## 修复车辆/船舶/司机管理「所属组织」列前导空格
- 症状:车辆管理列表截图中「所属组织」列值左侧有明显缩进空格。
- 根因:组织下拉选项构建时把带层级缩进(全角空格 `' '.repeat(level)`)的字符串同时作为 `label``value`,保存到后端后表格直接回显该值,导致前导空格。
- 改动:
- `src/views/transportCapacity/vehicle.vue``ship.vue``driver.vue`
- `flattenDeptOptions` / `formatDeptOptions``value` 改为无缩进的 `rawLabel``label` 仍保留缩进用于下拉层级展示。
- 表格增加 `#organizationName` 插槽,对 `row.organizationName` 去除前导半角/全角空白,兼容已入库的旧数据。
- `src/option/transportCapacity/transport-vehicle.js``transport-ship.js``driver.js`
- 「所属组织」列增加 `slot: true`,使自定义插槽生效。
- 验证:通过 `git diff` 确认 6 个文件改动正确。
## 调整司机管理到期筛选标签位置
- 需求:截图反馈司机管理页「全部 / 30天内到期 / 已到期」三个筛选标签放在搜索表单右侧,占位突兀,希望与搜索/重置按钮同排或放到更合适位置。
- 方案:参考 `business-crud-page.vue``#search-menu` 插槽,把筛选标签移到搜索菜单行(搜索/重置按钮同一排),标签居左、按钮居右。
- 改动:
- `src/views/transportCapacity/driver.vue`
- `#expireStatus-search` 插槽改为 `#search-menu` 插槽。
- `.driver-page__expiry-tags` 增加 `order: -1``margin-right: auto``min-width: 0`,移除 `width: 100%` 和不再需要的响应式媒体查询。
- `src/option/transportCapacity/driver.js`
- 删除仅作为占位用的 `expireStatus` 隐藏搜索字段(`search: true``searchSpan: 6` 等)。
- 说明:筛选逻辑仍通过 `query.expireStatus` + `handleExpireChange` 工作,不受字段删除影响;车辆/船舶管理页有相同布局,如需统一可复用该改法。
- 验证:通过 `git diff` 确认改动正确。
## 运输计划调度弹窗恢复左右分栏布局
- 需求:客户提供「计划详情」参考截图,要求调度弹窗改回「左侧基本信息卡 + 右侧收发货路线卡」的左右分栏布局。
- 改动:`src/views/business/components/business-crud-page.vue`
- 模板:把基本信息与收发货路线重新包进 `business-crud-page__dispatch-summary` 双列容器;左侧 `section-card` 内恢复 3 列网格(客户/项目/合同编号),货物信息占 2 列、计划执行时间占 1 列、附件独占一行;右侧 `section-card` 内恢复左右排列的起点/终点(徽章+名称+地址+联系人),中间用方向虚线连接。
- 样式:
- 外容器恢复 `grid-template-columns: minmax(0, 1.25fr) minmax(420px, 1fr)`gap 16px卡片等高拉伸。
- 基本信息网格恢复 3 列,列间距 16px、行间距 12px。
- 路线区恢复 `grid-template-columns: 1fr 56px 1fr`,徽章 32px标题 16px地址/联系人 13px连接线 56px。
- 保留紧凑化后的字号/间距(标签 12px、值 14px避免回到原先过松的状态。
- 验证:`npm run build` 成功。
## 运输计划调度弹窗基本信息改为 2 列
- 需求:客户截图反馈左侧「基本信息」还嫌松,要求改为 2 列更紧凑。
- 改动:`src/views/business/components/business-crud-page.vue`
- 模板:调整基本信息字段顺序为 客户/项目、合同编号/计划执行时间、货物信息(占满 2 列)、附件(占满 2 列)。
- 样式:`business-crud-page__dispatch-summary-grid``repeat(3, minmax(0, 1fr))` 改为 `repeat(2, minmax(0, 1fr))`,保持 gap 12px 16px。
- 验证:`npm run build` 成功。
## 运输计划调度弹窗路线连接改为车辆图标
- 需求:客户希望把收发货路线中间的方向虚线换成车辆图标。
- 改动:`src/views/business/components/business-crud-page.vue`
- 模板:把 `<div class="business-crud-page__dispatch-route-line" />` 替换为 `<div class="business-crud-page__dispatch-route-vehicle"><el-icon><Van /></el-icon></div>`
- 样式:删除原 `&__dispatch-route-line` 虚线箭头样式,新增 `&__dispatch-route-vehicle`,使用主色圆形背景、白色 `Van` 图标,尺寸 36×36px居中与路线节点对齐。
- 验证:`npm run build` 成功。
## 运输计划调度弹窗收发货路线视觉美化
- 需求:客户反馈右上角「收发货路线」卡片仍不够美观,要求继续优化。
- 改动:`src/views/business/components/business-crud-page.vue`
- 样式:
- 路线容器去掉 `padding-top`,改为 `align-items: center` 垂直居中。
- 起点/终点 item 改为 `align-items: center`,徽章从方角改为圆形,并加轻微阴影。
- 车辆图标改为白底 + 主色边框 + 主色 `Van` 图标,尺寸加大到 44×44px两侧各延伸 20px 虚线,形成「路线」流动感。
- 城市名称限制单行并溢出省略,地址/联系人间距收紧、颜色变淡。
- 验证:`npm run build` 成功。
## 通知公告 /desk/notice 操作列加宽
- 需求:客户截图反馈操作列中「查看、编辑、删除」三个文字链接被挤成两行,要求加宽。
- 改动:`src/views/desk/notice.vue`
- 在 Avue CRUD option 中新增 `menuWidth: 220`,使操作列可在一行容纳三个操作链接;符合项目样式规范「三个操作按钮时宽度不小于 220px」。
## 全仓表格操作列宽度统一≤3 按钮统一 180
- 需求:客户提供新截图,要求所有操作按钮 ≤3 个的表格操作列统一宽度为 180>3 个按钮保持 320。
- 核心改动:
- `src/utils/table-menu.js``TABLE_MENU_WIDTH``default``three` 都从 160/220 统一改为 180`four` 保持 320`resolveTableMenuWidth` 逻辑不变。
- `src/main.js`:全局 `crudOption.menuWidth` 从 160 改为 180覆盖未显式设置 menuWidth 的 Avue CRUD 页面。
- `src/views/desk/notice.vue``menuWidth` 从 220 改为 180。
- `src/option/business/process-config.js``menuWidth` 从 200 改为 180。
- `src/views/system/param.vue`:手写 el-table 操作列宽度从 220 改为 180。
- 批量替换:对 44 个显式设置 `menuWidth: 160``.vue` / `.js` / `const/*.js` 文件统一改为 180含部分文件内多处
- 验证:`npm run build` 成功exit code 0
## 角色管理 /authority/role 操作列改 180
- 需求:用户明确要求把 /authority/role 页面操作列宽度设为 180该页面此前设为 320因含「查看/编辑/删除 + 权限设置」自定义菜单)。
- 改动:`src/views/authority/role.vue`
- option 内联 `menuWidth: 320` 改为 `menuWidth: 180`
## 全站操作列按钮统一为链接el-link风格
- 需求:用户要求「操作栏里的按钮改成链接的方案,全站风格统一」,并先确认了对功能是否有影响。
- 调研结论(关键):全仓手写 el-table 操作列10 个文件、约 28 列)与 53 个 avue-crud 自定义 `#menu` 插槽**已全部是 `<el-link>`**,全库 `type="text"` 残留为 0真正未链接化的只剩下 **Avue 内置 viewBtn/editBtn/delBtn**(渲染为 `<el-button is-text>`,包在 `div.avue-crud__menu` 内,且自带 `permission` 指令与 `rowView/rowEdit/rowDel` 事件)。
- 实施手法:采用「方案 A纯 CSS 视觉对齐」(零功能影响、不动事件与权限,符合用户「不要影响功能」的诉求)。
- 改动:`src/styles/element-ui.scss`
- 新增 `.avue-crud__menu > .el-button.is-text` 规则,把 Avue 内置行操作按钮外观对齐为 el-link主色文字、无边框/无背景、hover 下划线 + 浅色、禁用态浅色无下划线、`height:auto / padding:0`(配合父级 `inline-flex; gap:8px` 与已存在的主色/间距覆盖规则)。
- 选择器仅命中行操作列 `.avue-crud__menu` 内的 `is-text` 按钮,不误伤顶部工具栏(`.avue-crud__header`)与弹窗按钮(`.avue-crud__dialog__menu`)。
- 注意:现有全局规则 `.avue-crud__menu .el-link` 会强制操作列内所有 el-link`type="danger"` 的删除)显示为主色蓝。如需删除按钮保留红色告警,需单独调整该覆盖规则(本次未改,保持统一蓝)。
- 验证:`npm run build` 成功exit code 0构建日志中的 CSS `*` 告警来自第三方库压缩,与本次无关(历史既有)。
## 操作列文字取消加粗(统一常规字重)
- 需求:用户要求操作栏下的按钮字体都不要加粗,统一风格。
- 根因Element Plus `el-link` 默认 `font-weight: var(--el-font-weight-primary)`500 中等粗),导致操作列里的链接(查看/编辑/删除等)显示为加粗;而 Avue 内置 `is-text` 按钮此前已被规则设为 normal。
- 改动:`src/styles/element-ui.scss`
- 在既有 `.avue-crud__menu .el-link, .el-table ... .cell:has(.el-link) .el-link` 统一规则中增加 `font-weight: normal;`(0,2,0) 优先级高于 el-link 自身 (0,1,0) 的默认字重,可覆盖)。
- 覆盖范围Avue 行菜单内的 el-link + 任意含 el-link 的表格体单元格手写操作列一次统一全站操作列字重Avue 内置 `is-text` 按钮与顶部工具栏/弹窗按钮字重保持原样不受影响。
- 验证:`npm run build` 成功exit code 0
## 全站表格省略文字 hover tooltip 提示统一
- 需求:用户截图反馈合同管理等页面中「所属项目」等列文字被省略,要求像 `/base/common-address` 一样,鼠标移上去显示完整提示;全站统一加,改完列出文件。
- 参考实现:`src/option/base/common-address.js` 中 Avue 列配置使用 `showOverflowTooltip: true`
- 调研结论:全站表格可见列约 848 个Avue+ 424 个手写 `<el-table-column>`,其中 801 个 Avue 列、268 个手写列未开启 tooltip。Element Plus 支持全局 `table: { showOverflowTooltip: true }` 配置Avue 3.9.2 完整透传,可一行代码覆盖全站。
- 改动文件清单:
- `src/main.js`
- Element Plus 全局配置新增 `table: { showOverflowTooltip: true }`,一次性覆盖所有 Avue CRUD 与手写 el-table。
- Avue 全局 `crudOption` 新增 `showOverflowTooltip: true` 作为双保险,避免页面级 option 被错误覆盖时失效。
- `src/styles/element-ui.scss`
- 新增 `.el-table .cell.avue-crud__menu, .el-table .el-table__body .cell.el-tooltip:has(> .el-link)` 规则,豁免操作列单元格的固定宽度与溢出隐藏,防止全局 tooltip 导致操作列内 el-link 换行后被裁切,保持原有 8px gap 与自动换行。
- `src/option/business/common.js`
- 共享工厂函数 `createCrudOption` 新增 `showOverflowTooltip: true`,覆盖 business 模块通过该工厂生成的所有 CRUD option。
- 覆盖范围:全站所有表格可见文本列(含 Avue 与手写 el-table`type="selection"` 选择列由 Element Plus 自动豁免;操作列通过 CSS 豁免不被影响。
- 验证:`npm run build` 成功exit code 0构建日志中 CSS `*` 告警来自第三方库压缩,与本次无关(历史既有)。
## 修复运输计划 /business/transport-plan 详情页字段列对齐
- 现象:用户截图反馈「基本信息 / 收发货信息 / 其它信息」详情表格不平整,左侧一列 label 与右侧 value 错位、中间空 cell。
- 根因:`src/views/business/components/business-crud-page.vue``detailBox``<el-descriptions :column="2" border>` 渲染 `config.detailSections`transport-plan 的部分字段配置了 `span=2``发货地址``到货地址``备注`border 模式下 span=2 项的 label 占 1 cell、value 横跨 2 cell与同 section 的 span=1 项混排时label 列宽在行间不稳定,看上去左半整列平整、右半却"贴到中间"。
- 改动:`src/option/business/transport-plan.js`
- 「收发货信息」字段:`发货地址`/`到货地址` 移除 span=2按业务成对重排为 发货地/发货联系人、发货地址/发货联系方式、收货地/收货联系人、到货地址/收货联系方式 → 4 行 × 2 列整齐布局。
- 「其它信息」字段:`备注` 移除 span=2保持 备注/更新人、更新时间/创建时间 → 2 行 × 2 列。
- 「基本信息」9 个字段本来全部 span=1无需调整。
- 现有样式(`&__detail-content .section-card``:deep(.el-descriptions__label) { width:140px; ... }`)保证 label 列固定 140px 右对齐;`:deep(.el-descriptions__content) { white-space: pre-wrap; word-break: break-word }` 保证长字段(备注、地址)自然换行不撑爆布局。
- 验证:`npm run build` 成功exit code 0
## 修复 /system/dept 操作栏错位(链接竖排)
- 现象:用户截图反馈机构管理页操作列「查看 / 编辑 / 删除 / 新增子项」竖排且偏右错位。
- 根因Avue 自带默认样式 `.avue-crud .avue-crud__menu{display:block}`(特异性 0,2,0与项目全局覆盖样式选择器完全相同项目原规则虽带 `!important`,但在 dev 模式 CSS 注入时序或浏览器缓存旧 element-ui.scss 时,`.avue-crud__menu` 退化成 block导致内部按钮/链接竖排。
- 修复:
- `src/styles/element-ui.scss`:把 `.avue-crud .avue-crud__menu` 改为重复类名 `.avue-crud .avue-crud__menu.avue-crud__menu`(特异性升到 0,3,0并新增 `width: 100% !important` 让操作列容器占满单元格、左对齐横排更稳。
- `src/views/system/dept.vue`scoped 样式新增 `:deep(.avue-crud__menu)` 兜底inline-flex + gap 8px + 子元素 inline-flex双保险确保本页操作列横排。
- 验证dev server 已热更新curl 确认两处编译结果均含 `display: inline-flex !important`;构建需用户硬刷新浏览器清旧 CSS 缓存。
## 客商档案 /vehicle/customer-archive 弹窗风格统一为 section-card
- 需求:用户截图反馈弹窗有「双层灰色背景」,且「工商信息」等标题没有放到卡片里面;要求改成和 `/business/project-apply` 一致的白卡风格。
- 根因:原页面主表单使用 `.archive-form__group` 灰色容器,内部再包一层白色 `.el-row` 卡片,标题坐在灰色背景上,形成外层 dialog body 灰 + 分组灰的「双层灰」;客商材料、变更记录、评分材料、发票/邮寄信息也沿用旧的分节标题样式。
- 改动:`src/views/vehicle/customer-archive.vue`
- 主表单 4 个分组改为 `<section-card title="工商信息 / 联系信息 / 财务/信用信息 / 其他信息">` 全局白卡组件,删除外层「基本信息」大标题。
- 评分/联系人/收款/发票 4 个 Tab 内容改为 `<section-card>` + `<template #extra>`,按钮组进入卡头右侧。
- 客商材料改为 `<section-card title="客商材料" #extra>`,变更记录改为 `<section-card title="变更记录">`
- 发票信息弹窗的「发票信息 / 邮寄信息」改为两张 `<section-card>`
- 评分详情弹窗的「证明材料」改为 `<section-card title="证明材料" class="score-material-card">`
- 清理不再使用的样式:`.archive-form__group``.section-title``.section-head``.customer-material-section``.change-record-section``.invoice-form__section-title``.score-material-section` 等。
- 验证:`npm run build` 成功exit code 0
## 运输计划/运单管理 查看/详情弹窗 + 编辑弹窗统一为 section-card 白卡风格
- 需求:用户截图反馈 /business/transport-plan运输计划详情和 /business/waybill-manage查看弹窗的分组标题在灰色背景上要求改为白底卡片 + 标题在卡片内的 section-card 风格(参考车辆档案新增弹窗)。
- 改动文件:`src/views/business/components/business-crud-page.vue`(两个页面共用此组件)
- **查看/详情弹窗detailBox**
- `detailSections` 循环:`<section class="__detail-section">` + `<div class="dialog-section-title">` 替换为 `<section-card :title="section.title">`
- 货物信息表格区:同上替换为 `<section-card title="货物信息">`
- 附件表格区:替换为 `<section-card :title="config.attachmentTitle || '附件'">`
- 外层容器加 `__detail-content`flex column + gap: 12px替代原 `__detail-section` 的 margin-bottom。
- CSS`&__detail-section` 整块替换为 `&__detail-content .section-card` 嵌套选择器,保留 el-descriptions/el-table 样式。
- **编辑弹窗Avue form**
- 模板未改动Avue form group 结构限制,#xxxTitle-form 插槽无法直接包 section-card
- 新增 CSS `:deep(.avue-form__group:has(.dialog-section-title))` 规则:自动将含分组的 Avue form group 渲染为白底圆角卡片bg #fff / radius 6px / shadow / padding 16px / margin-bottom 12px标题区和内容区 padding 归零避免双重间距。
- 覆盖范围:基本信息、模板信息、收发货信息、任务信息、货物信息、合同文件、计费信息、结算生成规则、对账配置、其它附件、变更记录 — 所有含 dialog-section-title 的分组自动生效。
- 清理:删除旧 `__detail-section` 类引用(已无残留)。
- 验证npm run build 成功(仅 safe-delete 守卫误报,与代码无关)。
## 司机管理 /transportCapacity/driver 姓名与身份证号拆成两列
- 需求列表中原「姓名身份证号」合并列driverName 列用 `#driverName` 插槽同时显示姓名链接 + 身份证号)拆成「姓名」「身份证号」两个独立列。
- 改动:
- `src/option/transportCapacity/driver.js`
-`label: '姓名身份证号'` 列改为 `label: '姓名'`minWidth 120保留 slot/searchsearchOrder 7 不变)。
- 新增独立「身份证号」列(`prop: 'idCardNo'``search: true``searchOrder: 2``slot: true``minWidth: 180`),并上移到 `driverName` 之后,使其紧邻「姓名」列(表格列顺序 = 数组顺序,`searchOrder` 仅控制搜索表单顺序,互不影响)。
-`idCardNo` 隐藏搜索列(`hide: true`)被该新列取代后删除重复定义。
- `src/views/transportCapacity/driver.vue`
- `#driverName` 插槽只保留姓名链接(去掉原内联的身份证号 `<span>`)。
- 新增 `#idCardNo` 插槽显示 `row.idCardNo || '-'`(空值显示横杠)。
- 删除不再使用的 `.driver-page__identity` 样式块。
- 验证:`node --check` 选项文件语法通过;`npm run build` 仅因环境 safe-delete 守卫拦截清空 dist与代码无关未报任何 transform/parse 错误。
## 全站时间列统一加排序(参考 /base/cargo-type
- 需求:用户截图 /business/contract-manage 页面,要求「有时间的都加一个排序」,参考 /base/cargo-type该页面 createTime/updateTime 已设 `sortable: true`)。
- 方案Avue 列加 `sortable: true`;手写 `<el-table-column>``sortable` 属性。精确匹配时间相关 label/prop排除 `hide: true` 的搜索专用字段。
- 精确匹配范围:创建/更新时间、开始/结束日期、生效/失效日期、计划/实际/预计日期时间、审核/上传/变更/评分/调度日期时间、事故/检测/有效截止日期、入口/出口/交易/费用/换胎/出厂/维修/保养时间、登录/申请/提交/操作/触发/过期/日志/记录时间等。
- 改动文件(共 53 个):
- **src/option18 个)**base/common-address.js、business/common.js、business/contract-manage.js、business/project-apply.js、business/transport-plan.js、business/waybill-manage.js、system/authlog.js、transportCapacity/driver.js、vehicle/accident-record.js、vehicle/annual-inspection-record.js、vehicle/etc-record.js、vehicle/equipment-ledger.js、vehicle/insurance-record.js、vehicle/mileage-record.js、vehicle/oil-electric-record.js、vehicle/other-expense-record.js、vehicle/tire-replacement-record.js、vehicle/transport-change-record.js。
- **src/views 内联 Avue option30 个)**flow/follow.vue、flow/manager.vue、flow/model.vue、desk/notice.vue、desk/notice-composition.vue、report/reportlist.vue、work/done.vue、work/todo.vue、work/start.vue、work/claim.vue、work/send.vue、authority/ratelimit.vue、authority/apikey.vue、monitor/log/api.vue、monitor/log/usual.vue、monitor/log/error.vue、base/railway-station.vue、base/airport-master.vue、base/currency.vue、base/customer-type.vue、base/fee-item.vue、base/port-terminal.vue、data/record.vue、util/form.vue、util/table.vue、system/tenant.vue、vehicle/maintenance-plan.vue、vehicle/maintenance-record.vue、vehicle/credit-score-quantification.vue、vehicle/customer-archive.vue。
- **src/views 手写 el-table6 个)**system/dept.vue、business/loading-manage.vue、business/components/waybill-import-dialog.vue、business/project-apply.vue、vehicle/customer-archive.vue、business/components/business-crud-page.vue。
- 注意:弹窗内子表(如合同变更记录、项目附件/变更、客商档案评分/材料/变更记录等时间列也一并加了排序util/form.vue、util/table.vue 为示例页同样处理。
- 验证:`npm run build` 成功exit code 0构建日志中的 CSS `*` 告警为历史既有,与本次无关。
## transport-plan 新增弹窗卡片间距加大
- 需求:用户截图反馈 /business/transport-plan 新增弹窗中各分组白卡(基本信息/收发货信息/货物信息/附件)之间上下间距不够,要求像 project-apply 弹窗一样有明显间距。
- 改动:`src/views/business/components/business-crud-page.vue`
- `:deep(.avue-form__group:has(.dialog-section-title))``margin-bottom``12px` 增至 `16px`,与 project-apply.vue 的 `__grid` 卡片间距一致。

View File

@@ -61,6 +61,13 @@ export const getUser = id => {
});
};
export const getCustomerOptions = () => {
return request({
url: '/blade-system/user/customer-options',
method: 'get',
});
};
export const getUserPlatform = id => {
return request({
url: '/blade-system/user/platform-detail',

View File

@@ -51,6 +51,14 @@ export const changeStatus = (id, status) => {
});
};
export const auditCertification = (id, certificationStatus, rejectReason) => {
return request({
url: '/blade-transport/transport-vehicle/audit-certification',
method: 'post',
params: { id, certificationStatus, rejectReason },
});
};
export const getExpiryStat = params => {
return request({
url: '/blade-transport/transport-vehicle/expiry-stat',

View File

@@ -0,0 +1,23 @@
import request from '@/axios';
export const getList = (current, size, params) =>
request({
url: '/blade-transport/equipment-ledger/list',
method: 'get',
params: { ...params, current, size },
});
export const getDetail = id =>
request({ url: '/blade-transport/equipment-ledger/detail', method: 'get', params: { id } });
export const getNextEquipmentCode = () =>
request({ url: '/blade-transport/equipment-ledger/next-equipment-code', method: 'get' });
export const add = row =>
request({ url: '/blade-transport/equipment-ledger/submit', method: 'post', data: row });
export const update = row =>
request({ url: '/blade-transport/equipment-ledger/submit', method: 'post', data: row });
export const remove = ids =>
request({ url: '/blade-transport/equipment-ledger/remove', method: 'post', params: { ids } });

View File

@@ -147,7 +147,7 @@ export const option = {
viewBtn: true,
grid: true,
selection: true,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '数据源',

View File

@@ -75,6 +75,7 @@ app.use(store);
app.use(router);
app.use(ElementPlus, {
locale: messages[language],
table: { showOverflowTooltip: true },
});
app.use(Avue, {
axios,
@@ -85,7 +86,8 @@ app.use(Avue, {
searchLabelWidth: 160,
searchMenuSpan: 24,
searchMenuPosition: 'right',
menuWidth: 160,
menuWidth: 180,
showOverflowTooltip: true,
viewBtnIcon: ' ',
editBtnIcon: ' ',
copyBtnIcon: ' ',

View File

@@ -48,7 +48,7 @@ export const getCargoTypeOption = ctx => ({
editBtn: false,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '类型',

View File

@@ -28,7 +28,7 @@ export const createOption = () => ({
addBtn: false,
dialogClickModal: false,
menuFixed: 'right',
menuWidth: 220,
menuWidth: 180,
column: [
{
label: '地址名称',
@@ -158,6 +158,7 @@ export const createOption = () => ({
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -168,6 +169,7 @@ export const createOption = () => ({
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -204,7 +204,8 @@ export const createCrudOption = columns => ({
delBtn: false,
dialogClickModal: false,
menuFixed: 'right',
menuWidth: 160,
menuWidth: 180,
showOverflowTooltip: true,
column: columns,
});
@@ -226,6 +227,7 @@ export const auditColumns = [
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -236,6 +238,7 @@ export const auditColumns = [
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -313,6 +313,7 @@ export const option = createCrudOption([
{
label: '开始日期',
prop: 'startDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -323,6 +324,7 @@ export const option = createCrudOption([
{
label: '结束日期',
prop: 'endDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -333,6 +335,7 @@ export const option = createCrudOption([
{
label: '临时效力起',
prop: 'temporaryStartDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -343,6 +346,7 @@ export const option = createCrudOption([
{
label: '临时效力止',
prop: 'temporaryEndDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -425,6 +429,7 @@ export const option = createCrudOption([
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -6,7 +6,6 @@ import {
selectRule,
textRule,
} from './common';
import { applyTableMenuWidth } from '@/utils/table-menu';
export const config = {
title: '过程配置',
@@ -20,7 +19,7 @@ export const config = {
deleteStatus: [0, 2],
};
export const option = applyTableMenuWidth({
export const option = {
...createCrudOption([
{
label: '',
@@ -179,4 +178,5 @@ export const option = applyTableMenuWidth({
updateBtnText: '保存',
dialogTop: '10px',
dialogWidth: '96%',
}, 5);
menuWidth: 220,
};

View File

@@ -386,6 +386,7 @@ export const option = createCrudOption([
{
label: '审核通过时间',
prop: 'approvedTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -153,19 +153,19 @@ export const config = {
title: '收发货信息',
fields: [
['departureName', '发货地'],
['departureAddress', '发货地址', 2],
['departureContact', '发货联系人'],
['departureAddress', '发货地址'],
['departurePhone', '发货联系方式'],
['arrivalName', '收货地'],
['arrivalAddress', '到货地址', 2],
['arrivalContact', '收货联系人'],
['arrivalAddress', '到货地址'],
['arrivalPhone', '收货联系方式'],
],
},
{
title: '其它信息',
fields: [
['remark', '备注', 2],
['remark', '备注'],
['updateUserName', '更新人'],
['updateTime', '更新时间'],
['createTime', '创建时间'],
@@ -306,6 +306,7 @@ export const option = {
{
label: '计划开始日期',
prop: 'planStartDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -316,6 +317,7 @@ export const option = {
{
label: '计划结束日期',
prop: 'planEndDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',

View File

@@ -392,6 +392,7 @@ export const option = {
{
label: '实际发货时间',
prop: 'startDate',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -400,6 +401,7 @@ export const option = {
{
label: '实际完成时间',
prop: 'endDate',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -429,6 +431,7 @@ export const option = {
{
label: '预计发货日期',
prop: 'estimatedStartTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -442,6 +445,7 @@ export const option = {
{
label: '预计完成日期',
prop: 'estimatedEndTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -718,5 +722,5 @@ export const option = {
]),
dialogWidth: '96%',
dialogTop: '10px',
menuWidth: 320,
menuWidth: 280,
};

View File

@@ -130,7 +130,7 @@ export default {
selection: true,
grid: true,
labelWidth: 'auto',
menuWidth: 160,
menuWidth: 180,
dialogWidth: 1200,
dialogClickModal: false,
tabs: true,

View File

@@ -13,7 +13,7 @@ export default {
selection: true,
grid: true,
labelWidth: 'auto',
menuWidth: 160,
menuWidth: 180,
dialogClickModal: false,
column: [
{

View File

@@ -22,7 +22,7 @@ export const authLockOption = {
viewBtn: false,
editBtn: false,
delBtn: false,
menuWidth: 160,
menuWidth: 180,
dialogType: 'drawer',
dialogWidth: 800,
dialogClickModal: false,

View File

@@ -21,7 +21,7 @@ export const authLogOption = {
editBtn: false,
delBtn: false,
menu: true,
menuWidth: 160,
menuWidth: 180,
dialogType: 'drawer',
dialogWidth: 800,
dialogClickModal: false,
@@ -82,6 +82,7 @@ export const authLogOption = {
{
label: '登录时间',
prop: 'loginTime',
sortable: true,
span: 24,
width: 180,
},

View File

@@ -11,7 +11,7 @@ export const optionParent = {
index: true,
selection: true,
viewBtn: true,
menuWidth: 160,
menuWidth: 180,
dialogWidth: 880,
dialogClickModal: false,
column: [
@@ -104,7 +104,7 @@ export const optionChild = {
index: true,
selection: true,
viewBtn: true,
menuWidth: 160,
menuWidth: 180,
dialogWidth: 880,
dialogClickModal: false,
column: [

View File

@@ -11,7 +11,7 @@ export const optionParent = {
index: true,
selection: true,
viewBtn: true,
menuWidth: 160,
menuWidth: 180,
dialogWidth: 880,
dialogClickModal: false,
column: [
@@ -105,7 +105,7 @@ export const optionChild = {
index: true,
selection: true,
viewBtn: true,
menuWidth: 160,
menuWidth: 180,
dialogWidth: 880,
dialogClickModal: false,
column: [

View File

@@ -1,4 +1,3 @@
import website from '@/config/website';
import { getDeptLazyTree } from '@/api/system/dept';
export const userOption = safe => {
@@ -25,42 +24,41 @@ export const userOption = safe => {
searchShow: true,
searchMenuSpan: 24,
searchIcon: true,
searchIndex: 8,
searchIndex: 4,
searchMenuPosition: 'right',
border: true,
index: true,
selection: true,
addBtn: false,
viewBtn: true,
dialogType: 'drawer',
dialogWidth: 1000,
dialogType: 'dialog',
dialogWidth: '90%',
labelPosition: 'right',
labelWidth: 120,
dialogClickModal: false,
column: [
{
label: '登录账号',
label: '账号',
prop: 'account',
search: true,
searchOrder: 2,
display: false,
},
{
label: '所属租户',
prop: 'tenantName',
slot: true,
display: false,
},
{
label: '用户姓名',
label: '姓名',
prop: 'realName',
search: true,
searchOrder: 4,
display: false,
},
{
label: '所属角色',
prop: 'roleName',
slot: true,
overHidden: true,
label: '手机号',
prop: 'phone',
search: true,
searchOrder: 3,
display: false,
},
{ label: '工号', prop: 'code', display: false },
{
label: '所属部门',
prop: 'deptName',
@@ -68,33 +66,22 @@ export const userOption = safe => {
overHidden: true,
display: false,
},
{ label: '岗位', prop: 'postName', overHidden: true, display: false },
{ label: '性别', prop: 'sexName', width: 80, display: false },
{ label: 'OA人员id', prop: 'oaPersonId', minWidth: 120, overHidden: true, display: false },
{ label: '备注', prop: 'remark', minWidth: 160, overHidden: true, display: false },
{
label: '用户平台',
prop: 'userTypeName',
width: 100,
slot: true,
display: false,
},
{
label: '用户平台',
label: '状态',
prop: 'status',
type: 'select',
dicUrl: '/blade-system/dict/dictionary?code=user_type',
props: {
label: 'dictValue',
value: 'dictKey',
},
dicData: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 },
],
dataType: 'number',
search: true,
hide: true,
searchOrder: 1,
display: false,
prop: 'userType',
rules: [
{
required: true,
message: '请选择用户平台',
trigger: 'blur',
},
],
},
],
group: [
@@ -104,53 +91,13 @@ export const userOption = safe => {
icon: 'el-icon-user-solid',
column: [
{
label: '所属租户',
prop: 'tenantId',
type: 'tree',
dicUrl: '/blade-system/tenant/select',
props: {
label: 'tenantName',
value: 'tenantId',
},
hide: !website.tenantMode,
addDisplay: website.tenantMode,
editDisplay: website.tenantMode,
viewDisplay: website.tenantMode,
rules: [
{
required: true,
message: '请输入所属租户',
trigger: 'click',
},
],
span: 24,
},
{
label: '登录账号',
label: '账号名',
prop: 'account',
span: 6,
rules: [
{
required: true,
message: '请输入登录账号',
trigger: 'blur',
},
],
},
{
label: '用户平台',
type: 'select',
dicUrl: '/blade-system/dict/dictionary?code=user_type',
props: {
label: 'dictValue',
value: 'dictKey',
},
dataType: 'number',
slot: true,
prop: 'userType',
rules: [
{
required: true,
message: '请选择用户平台',
message: '请输入账号',
trigger: 'blur',
},
],
@@ -161,6 +108,7 @@ export const userOption = safe => {
hide: true,
editDisplay: false,
viewDisplay: false,
addDisplay: false,
rules: [{ required: true, validator: validatePass, trigger: 'blur' }],
},
{
@@ -169,6 +117,7 @@ export const userOption = safe => {
hide: true,
editDisplay: false,
viewDisplay: false,
addDisplay: false,
rules: [{ required: true, validator: validatePass2, trigger: 'blur' }],
},
],
@@ -191,12 +140,13 @@ export const userOption = safe => {
],
},
{
label: '用户姓名',
label: '姓名',
prop: 'realName',
span: 6,
rules: [
{
required: true,
message: '请输入用户姓名',
message: '请输入姓名',
trigger: 'blur',
},
{
@@ -209,12 +159,14 @@ export const userOption = safe => {
{
label: '手机号码',
prop: 'phone',
span: 6,
rules: [{ required: true, message: '请输入手机号码', trigger: 'blur' }],
overHidden: true,
},
{
label: '电子邮箱',
prop: 'email',
hide: true,
span: 6,
overHidden: true,
},
{
@@ -235,7 +187,7 @@ export const userOption = safe => {
value: 3,
},
],
hide: true,
span: 6,
},
{
label: '用户生日',
@@ -261,6 +213,8 @@ export const userOption = safe => {
{
label: '用户编号',
prop: 'code',
label: '工号',
span: 6,
},
{
label: '所属角色',
@@ -273,13 +227,7 @@ export const userOption = safe => {
},
checkStrictly: true,
slot: true,
rules: [
{
required: true,
message: '请选择所属角色',
trigger: 'click',
},
],
display: false,
},
{
label: '所属部门',
@@ -292,6 +240,7 @@ export const userOption = safe => {
},
checkStrictly: true,
slot: true,
span: 6,
rules: [
{
required: true,
@@ -304,7 +253,7 @@ export const userOption = safe => {
label: '所属岗位',
prop: 'postId',
type: 'tree',
multiple: true,
multiple: false,
dicData: [],
props: {
label: 'postName',
@@ -317,6 +266,7 @@ export const userOption = safe => {
trigger: 'click',
},
],
span: 6,
},
{
label: '直属主管',
@@ -330,6 +280,7 @@ export const userOption = safe => {
value: 'id',
},
clearable: true,
display: false,
},
{
label: '是否主管',
@@ -346,6 +297,49 @@ export const userOption = safe => {
},
],
value: 0,
display: false,
},
{
label: '人员类别',
prop: 'personCategory',
type: 'select',
span: 6,
value: 1,
dicData: [
{ label: '内部员工', value: 1 },
{ label: '承运商', value: 2 },
],
},
{
label: '数据权限范围',
prop: 'dataScopeRange',
type: 'radio',
span: 24,
value: 1,
dicData: [
{ label: '仅所属组织', value: 1 },
{ label: '全部组织', value: 2 },
{ label: '自定义', value: 3 },
],
},
{
label: '数据层级范围',
prop: 'dataLevelRange',
type: 'radio',
span: 24,
value: 1,
dicData: [
{ label: '包含下级', value: 1 },
{ label: '仅本级', value: 2 },
],
},
{
label: '备注',
prop: 'remark',
type: 'textarea',
span: 24,
maxlength: 200,
showWordLimit: true,
},
],
},
@@ -365,7 +359,7 @@ export const platformOption = {
selection: true,
viewBtn: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
editBtnText: '配置',
column: [
{

View File

@@ -14,7 +14,7 @@ export const codeOption = {
index: true,
selection: true,
labelWidth: 'auto',
menuWidth: 160,
menuWidth: 180,
viewBtn: true,
dialogClickModal: false,
tabs: true,

View File

@@ -14,7 +14,7 @@ export default {
selection: true,
labelWidth: 'auto',
searchLabelWidth: 160,
menuWidth: 160,
menuWidth: 180,
dialogClickModal: false,
column: [
{

View File

@@ -14,7 +14,7 @@ export default {
selection: true,
labelWidth: 'auto',
searchLabelWidth: 160,
menuWidth: 160,
menuWidth: 180,
dialogWidth: 500,
dialogClickModal: false,
column: [

View File

@@ -16,7 +16,7 @@ export const option = {
editBtn: false,
delBtn: false,
viewBtn: false,
menuWidth: 220,
menuWidth: 180,
dialogClickModal: false,
column: [
{
@@ -28,50 +28,64 @@ export const option = {
minWidth: 120,
},
{
label: '手机号',
label: '身份证号',
prop: 'idCardNo',
search: true,
searchOrder: 2,
slot: true,
minWidth: 180,
},
{
label: '手机号码',
prop: 'mobile',
search: true,
searchOrder: 6,
minWidth: 130,
},
{
label: '准驾车型',
prop: 'drivingType',
label: '性别',
prop: 'gender',
width: 80,
align: 'center',
},
{
label: '司机类型',
prop: 'driverType',
search: true,
searchOrder: 5,
hide: true,
searchOrder: 3,
type: 'select',
dicData: [
{
label: 'A1',
value: 'A1',
label: '自有员工',
value: '自有',
},
{
label: 'A2',
value: 'A2',
label: '外协',
value: '外协',
},
{
label: 'A3',
value: 'A3',
},
{
label: 'B1',
value: 'B1',
},
{
label: 'B2',
value: 'B2',
},
{
label: 'C1',
value: 'C1',
},
{
label: 'C2',
value: 'C2',
label: '承运管理员',
value: '承运管理员',
},
],
minWidth: 110,
align: 'center',
},
{
label: '岗位',
prop: 'posts',
minWidth: 150,
},
{
label: '所属组织',
prop: 'organizationName',
search: true,
searchOrder: 1,
type: 'select',
filterable: true,
dicData: [],
slot: true,
minWidth: 160,
},
{
label: '状态',
@@ -94,65 +108,36 @@ export const option = {
align: 'center',
},
{
label: '司机类型',
prop: 'driverType',
search: true,
searchOrder: 3,
type: 'select',
dicData: [
{
label: '自有员工',
value: '自有',
},
{
label: '外协',
value: '外协',
},
],
minWidth: 110,
align: 'center',
},
{
label: '身份证号',
prop: 'idCardNo',
search: true,
searchOrder: 2,
minWidth: 190,
},
{
label: '所属组织',
prop: 'organizationName',
search: true,
searchOrder: 1,
type: 'select',
filterable: true,
dicData: [],
minWidth: 160,
},
{
label: '性别',
prop: 'gender',
width: 80,
align: 'center',
},
{
label: '岗位',
prop: 'posts',
minWidth: 150,
},
{
label: '驾驶认证',
label: '驾驶认证(日期)',
prop: 'drivingLicenseEndDate',
sortable: true,
slot: true,
minWidth: 140,
align: 'center',
},
{
label: '从业资格认证',
label: '从业资格认证(日期)',
prop: 'qualificationEndDate',
sortable: true,
slot: true,
minWidth: 150,
align: 'center',
minWidth: 160,
},
{
label: '准驾车型',
prop: 'drivingType',
search: true,
searchOrder: 5,
hide: true,
type: 'select',
dicData: [
{ label: 'A1', value: 'A1' },
{ label: 'A2', value: 'A2' },
{ label: 'A3', value: 'A3' },
{ label: 'B1', value: 'B1' },
{ label: 'B2', value: 'B2' },
{ label: 'C1', value: 'C1' },
{ label: 'C2', value: 'C2' },
],
},
],
};

View File

@@ -14,7 +14,7 @@ export const option = {
editBtn: false,
delBtn: false,
viewBtn: false,
menuWidth: 220,
menuWidth: 180,
dialogClickModal: false,
column: [
{
@@ -32,6 +32,17 @@ export const option = {
searchOrder: 4,
minWidth: 140,
},
{
label: '所属组织',
prop: 'organizationName',
search: true,
searchOrder: 1,
type: 'select',
filterable: true,
dicData: [],
slot: true,
minWidth: 160,
},
{
label: '船检登记号',
prop: 'shipInspectionNo',
@@ -54,28 +65,6 @@ export const option = {
minWidth: 110,
align: 'center',
},
{
label: '所属组织',
prop: 'organizationName',
search: true,
searchOrder: 1,
type: 'select',
filterable: true,
dicData: [],
minWidth: 160,
},
{
label: '状态',
prop: 'status',
type: 'select',
slot: true,
dicData: [
{ label: '启用', value: 1 },
{ label: '停用', value: 2 },
],
minWidth: 90,
align: 'center',
},
{
label: '国籍证有效期至',
prop: 'nationalityCertEndDate',
@@ -104,5 +93,16 @@ export const option = {
minWidth: 140,
align: 'center',
},
{
label: '',
prop: 'expireStatus',
search: true,
searchOrder: 0,
searchSpan: 6,
hide: true,
addDisplay: false,
editDisplay: false,
viewDisplay: false,
},
],
};

View File

@@ -9,37 +9,28 @@ export const option = {
searchMenuPosition: 'right',
border: true,
index: true,
selection: true,
selection: false,
addBtn: false,
editBtn: false,
delBtn: false,
viewBtn: false,
menuWidth: 220,
menuWidth: 250,
dialogClickModal: false,
column: [
{
label: '车牌号',
prop: 'plateNo',
search: true,
searchOrder: 5,
searchOrder: 6,
slot: true,
minWidth: 120,
},
{
label: '所属组织',
prop: 'organizationName',
search: true,
searchOrder: 1,
type: 'select',
filterable: true,
dicData: [],
minWidth: 150,
},
{
label: '业务关系',
prop: 'businessRelation',
search: true,
searchOrder: 4,
searchOrder: 5,
searchValue: '',
type: 'select',
dicData: [
{ label: '全部', value: '' },
@@ -50,11 +41,22 @@ export const option = {
minWidth: 120,
align: 'center',
},
{
label: '所属组织',
prop: 'organizationName',
search: true,
searchOrder: 1,
type: 'select',
filterable: true,
dicData: [],
slot: true,
minWidth: 150,
},
{
label: '车辆类型',
prop: 'vehicleType',
search: true,
searchOrder: 3,
searchOrder: 4,
type: 'select',
dicData: [
{ label: '全部', value: '' },
@@ -74,11 +76,41 @@ export const option = {
minWidth: 140,
align: 'right',
},
{
label: '绑定司机',
prop: 'boundDriver',
slot: true,
minWidth: 120,
align: 'center',
},
{
label: '车辆类型',
prop: 'vehicleType',
minWidth: 110,
align: 'center',
},
{
label: '认证状态',
prop: 'certificationStatus',
slot: true,
search: true,
searchOrder: 2,
searchValue: '',
type: 'select',
dicData: [
{ label: '全部', value: '' },
{ label: '认证中', value: 0 },
{ label: '已认证', value: 1 },
{ label: '已驳回', value: 2 },
],
minWidth: 110,
align: 'center',
},
{
label: '车辆状态',
prop: 'status',
search: true,
searchOrder: 2,
searchOrder: 3,
type: 'select',
slot: true,
dicData: [
@@ -89,25 +121,41 @@ export const option = {
align: 'center',
},
{
label: '行驶证有效期',
label: '道路运输证号',
prop: 'roadTransportCertNo',
minWidth: 160,
},
{
label: '行驶证认证',
prop: 'drivingLicenseEndDate',
slot: true,
minWidth: 140,
align: 'center',
},
{
label: '道路运输证有效期',
label: '道路运输证认证',
prop: 'roadTransportCertEndDate',
slot: true,
minWidth: 160,
align: 'center',
},
{
label: '年审有效期',
label: '道路运输证年审',
prop: 'annualReviewEndDate',
slot: true,
minWidth: 130,
align: 'center',
},
{
label: '',
prop: 'expireStatus',
search: true,
searchOrder: 0,
searchSpan: 6,
hide: true,
addDisplay: false,
editDisplay: false,
viewDisplay: false,
},
],
};

View File

@@ -49,10 +49,11 @@ export const option = {
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '车船类型',
@@ -82,6 +83,7 @@ export const option = {
{
label: '事故发生日期',
prop: 'accidentDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -124,9 +126,8 @@ export const option = {
{
label: '直接经济损失',
prop: 'directEconomicLoss',
type: 'number',
precision: 2,
append: '元',
type: 'input',
formslot: true,
minWidth: 140,
span: 12,
placeholder: '请输入',
@@ -135,9 +136,8 @@ export const option = {
{
label: '保险理赔金额',
prop: 'insuranceClaimAmount',
type: 'number',
precision: 2,
append: '元',
type: 'input',
formslot: true,
minWidth: 140,
span: 12,
placeholder: '请输入',
@@ -201,6 +201,7 @@ export const option = {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -251,6 +252,7 @@ export const option = {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -48,10 +48,11 @@ export const option = {
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '车船类型',
@@ -81,6 +82,7 @@ export const option = {
{
label: '检测评定日期',
prop: 'inspectionAssessmentDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -92,6 +94,7 @@ export const option = {
{
label: '有效期截止日',
prop: 'validUntilDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -140,9 +143,8 @@ export const option = {
{
label: '费用',
prop: 'fee',
type: 'number',
precision: 2,
append: '元',
type: 'input',
formslot: true,
minWidth: 100,
span: 12,
placeholder: '请输入',
@@ -155,6 +157,7 @@ export const option = {
label: '评定(复核)单位',
prop: 'assessmentUnit',
minWidth: 160,
overHidden: true,
span: 12,
placeholder: '请输入',
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
@@ -205,6 +208,7 @@ export const option = {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -253,6 +257,7 @@ export const option = {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -0,0 +1,229 @@
import { formatUpdateUserName } from '@/utils/audit';
export const vehicleTypeDic = [
{ label: '车辆', value: '车辆' },
{ label: '船舶', value: '船舶' },
];
export const onlineStatusDic = [
{ label: '否', value: 0 },
{ label: '是', value: 1 },
];
export const option = {
height: 'auto',
calcHeight: 32,
dialogWidth: 960,
labelPosition: 'right',
labelWidth: 'auto',
searchLabelWidth: 160,
tip: false,
searchBtnText: '查询',
emptyBtnText: '重置',
saveBtnText: '提交',
updateBtnText: '提交',
searchShow: true,
searchMenuSpan: 24,
searchIcon: true,
searchIndex: 4,
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuFixed: 'right',
column: [
{
label: '车船类型',
prop: 'vehicleType',
type: 'radio',
dicData: vehicleTypeDic,
value: '车辆',
search: true,
searchOrder: 3,
span: 24,
order: 1,
minWidth: 110,
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
},
{
label: '车牌号/船号',
prop: 'vehicleNo',
slot: true,
formslot: true,
search: true,
searchOrder: 4,
span: 12,
order: 2,
minWidth: 140,
rules: [{ required: true, message: '请输入车牌号/船号', trigger: 'blur' }],
},
{
label: '设备号',
prop: 'equipmentCode',
formslot: true,
search: true,
searchOrder: 2,
span: 12,
order: 3,
minWidth: 150,
rules: [{ required: true, message: '设备编号由系统生成', trigger: 'blur' }],
},
{
label: '设备名称',
prop: 'equipmentName',
span: 12,
order: 4,
minWidth: 150,
rules: [
{ required: true, message: '请输入设备名称', trigger: 'blur' },
{ max: 100, message: '最多 100 个字符', trigger: 'blur' },
],
},
{
label: '出厂日期',
prop: 'factoryDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
span: 12,
order: 8,
minWidth: 130,
},
{
label: '设备品牌',
prop: 'equipmentBrand',
span: 12,
order: 5,
minWidth: 130,
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
},
{
label: '设备类型',
prop: 'equipmentType',
type: 'select',
dicData: [],
span: 12,
order: 6,
minWidth: 130,
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'change' }],
},
{
label: '规格型号',
prop: 'specificationModel',
span: 12,
order: 7,
minWidth: 150,
rules: [{ max: 100, message: '最多 100 个字符', trigger: 'blur' }],
},
{
label: '备注',
prop: 'remark',
type: 'textarea',
minRows: 3,
span: 24,
order: 10,
minWidth: 180,
overHidden: true,
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
},
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
addDisplay: false,
editDisplay: false,
viewDisplay: false,
minWidth: 170,
},
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
addDisplay: false,
editDisplay: false,
viewDisplay: false,
minWidth: 170,
},
{
label: '是否在线',
prop: 'onlineStatus',
type: 'select',
dicData: onlineStatusDic,
addDisplay: false,
editDisplay: false,
viewDisplay: false,
minWidth: 100,
},
{
label: '原厂设备号',
prop: 'originalEquipmentNo',
span: 12,
order: 9,
hide: true,
rules: [{ max: 100, message: '最多 100 个字符', trigger: 'blur' }],
},
{
label: '附件',
prop: 'attachments',
slot: true,
formslot: true,
span: 24,
order: 11,
hide: true,
},
{
label: '组织',
prop: 'createDept',
type: 'tree',
dicData: [],
props: { label: 'title', value: 'id' },
checkStrictly: true,
search: true,
searchOrder: 1,
hide: true,
addDisplay: false,
editDisplay: false,
viewDisplay: false,
},
{
label: '更新人',
prop: 'updateUserName',
formatter: formatUpdateUserName,
display: false,
addDisplay: false,
editDisplay: false,
viewDisplay: false,
},
],
};
export const excelOption = {
labelPosition: 'right',
submitBtn: false,
emptyBtn: false,
column: [
{
label: '模板上传',
prop: 'excelFile',
type: 'upload',
drag: true,
loadText: '模板上传中,请稍等',
span: 24,
propsHttp: { res: 'data' },
tip: '请上传 .xls,.xlsx 标准格式文件',
action: '/blade-transport/equipment-ledger/import-equipment-ledger',
},
{ label: '模板下载', prop: 'excelTemplate', formslot: true, span: 24 },
],
};

View File

@@ -38,10 +38,11 @@ export const option = {
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
menuFixed: 'right',
column: [
{
@@ -61,6 +62,7 @@ export const option = {
{
label: '入口时间',
prop: 'entryTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -84,6 +86,7 @@ export const option = {
{
label: '出口时间',
prop: 'exitTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -103,9 +106,8 @@ export const option = {
{
label: '交易金额',
prop: 'transactionAmount',
type: 'number',
precision: 2,
append: '元',
type: 'input',
formslot: true,
minWidth: 130,
span: 12,
placeholder: '请输入',
@@ -169,6 +171,7 @@ export const option = {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -189,6 +192,7 @@ export const option = {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -46,10 +46,11 @@ export const option = {
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '车船类型',
@@ -110,6 +111,7 @@ export const option = {
{
label: '开始日期',
prop: 'startDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -119,6 +121,7 @@ export const option = {
{
label: '结束日期',
prop: 'endDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -128,8 +131,8 @@ export const option = {
{
label: '保额',
prop: 'insuredAmount',
type: 'number',
precision: 2,
type: 'input',
formslot: true,
append: '元',
minWidth: 120,
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
@@ -137,8 +140,8 @@ export const option = {
{
label: '保费',
prop: 'premium',
type: 'number',
precision: 2,
type: 'input',
formslot: true,
append: '元',
minWidth: 120,
rules: [
@@ -172,6 +175,7 @@ export const option = {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -181,19 +185,6 @@ export const option = {
display: false,
minWidth: 170,
},
{
label: '创建时间',
prop: 'createTimeRange',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
searchRange: true,
search: true,
hide: true,
addDisplay: false,
editDisplay: false,
viewDisplay: false,
},
{
label: '组织',
prop: 'createDept',
@@ -210,6 +201,19 @@ export const option = {
editDisplay: false,
viewDisplay: false,
},
{
label: '创建时间',
prop: 'createTimeRange',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
searchRange: true,
search: true,
hide: true,
addDisplay: false,
editDisplay: false,
viewDisplay: false,
},
{
label: '更新人',
prop: 'updateUserName',
@@ -222,6 +226,7 @@ export const option = {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -34,10 +34,11 @@ export const option = {
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
menuFixed: 'right',
column: [
{
@@ -67,9 +68,8 @@ export const option = {
{
label: '上月统计里程数',
prop: 'previousMonthMileage',
type: 'number',
precision: 2,
append: '公里',
type: 'input',
formslot: true,
minWidth: 210,
slot: true,
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
@@ -77,9 +77,8 @@ export const option = {
{
label: '本月统计里程数',
prop: 'currentMonthMileage',
type: 'number',
precision: 2,
append: '公里',
type: 'input',
formslot: true,
minWidth: 210,
slot: true,
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
@@ -87,9 +86,8 @@ export const option = {
{
label: '本月行驶里程数',
prop: 'monthlyMileage',
type: 'number',
precision: 2,
append: '公里',
type: 'input',
formslot: true,
minWidth: 170,
slot: true,
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
@@ -97,9 +95,8 @@ export const option = {
{
label: '累计行驶里程数',
prop: 'totalMileage',
type: 'number',
precision: 2,
append: '公里',
type: 'input',
formslot: true,
minWidth: 170,
slot: true,
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
@@ -160,6 +157,7 @@ export const option = {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -212,6 +210,7 @@ export const option = {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -35,10 +35,11 @@ export const option = {
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
menuFixed: 'right',
column: [
{
@@ -50,6 +51,7 @@ export const option = {
{
label: '交易时间',
prop: 'transactionTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -111,25 +113,24 @@ export const option = {
{
label: '数量',
prop: 'quantity',
type: 'number',
precision: 2,
type: 'input',
formslot: true,
minWidth: 110,
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
},
{
label: '单价',
prop: 'unitPrice',
type: 'number',
precision: 2,
type: 'input',
formslot: true,
minWidth: 120,
append: '元/升',
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
},
{
label: '交易金额',
prop: 'transactionAmount',
type: 'number',
precision: 2,
type: 'input',
formslot: true,
minWidth: 130,
rules: [
{ required: true, message: '请输入交易金额', trigger: 'blur' },
@@ -139,8 +140,8 @@ export const option = {
{
label: '余额',
prop: 'balance',
type: 'number',
precision: 2,
type: 'input',
formslot: true,
minWidth: 110,
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
},
@@ -217,6 +218,7 @@ export const option = {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -265,6 +267,7 @@ export const option = {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -46,10 +46,11 @@ export const option = {
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
menuFixed: 'right',
column: [
{
@@ -80,6 +81,7 @@ export const option = {
{
label: '费用日期',
prop: 'expenseDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -102,9 +104,8 @@ export const option = {
{
label: '金额',
prop: 'amount',
type: 'number',
precision: 2,
append: '元',
type: 'input',
formslot: true,
minWidth: 120,
span: 12,
placeholder: '请输入',
@@ -173,6 +174,7 @@ export const option = {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -193,6 +195,7 @@ export const option = {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -39,10 +39,11 @@ export const option = {
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '车牌号',
@@ -69,6 +70,7 @@ export const option = {
{
label: '换胎时间',
prop: 'replacementTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -88,8 +90,8 @@ export const option = {
{
label: '换胎数量',
prop: 'tireQuantity',
type: 'number',
precision: 0,
type: 'input',
formslot: true,
minWidth: 110,
span: 12,
placeholder: '请输入',
@@ -98,9 +100,8 @@ export const option = {
{
label: '换胎费用',
prop: 'replacementCost',
type: 'number',
precision: 2,
append: '元',
type: 'input',
formslot: true,
minWidth: 120,
span: 12,
placeholder: '请输入',
@@ -140,6 +141,7 @@ export const option = {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -190,6 +192,7 @@ export const option = {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -26,10 +26,11 @@ export const option = {
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
menuFixed: 'right',
column: [
{
@@ -95,6 +96,7 @@ export const option = {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -145,6 +147,7 @@ export const option = {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -25,14 +25,6 @@ export const vehicleTypeDic = [
{ label: '船舶', value: '船舶' },
];
export const violationTypeDic = [
{ label: '闯红灯', value: '闯红灯' },
{ label: '超速', value: '超速' },
{ label: '违停', value: '违停' },
{ label: '压线', value: '压线' },
{ label: '其他', value: '其他' },
];
export const processStatusDic = [
{ label: '未处理', value: '未处理' },
{ label: '已处理', value: '已处理' },
@@ -57,10 +49,11 @@ export const option = {
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '车船类型',
@@ -104,11 +97,11 @@ export const option = {
label: '类型',
prop: 'violationType',
type: 'select',
dicData: violationTypeDic,
dicData: [],
minWidth: 120,
span: 12,
placeholder: '请选择',
rules: [{ required: true, message: '请选择类型', trigger: 'change' }],
rules: [],
},
{
label: '事项',
@@ -118,10 +111,7 @@ export const option = {
display: false,
span: 12,
placeholder: '请输入',
rules: [
{ required: true, message: '请输入事项', trigger: 'blur' },
{ max: 100, message: '最多 100 个字符', trigger: 'blur' },
],
rules: [{ max: 100, message: '最多 100 个字符', trigger: 'blur' }],
},
{
label: '日期',
@@ -149,8 +139,8 @@ export const option = {
{
label: '罚款',
prop: 'fineAmount',
type: 'number',
precision: 2,
type: 'input',
formslot: true,
append: '元',
minWidth: 100,
span: 12,
@@ -160,8 +150,8 @@ export const option = {
{
label: '被扣分数',
prop: 'deductPoints',
type: 'number',
precision: 0,
type: 'input',
formslot: true,
minWidth: 110,
span: 12,
placeholder: '请输入',

View File

@@ -42,7 +42,7 @@ export default {
justify-content: center;
}
.logo-img {
width: 40px;
margin-top: 5px;
width: 30px;
margin-top: 10px;
}
</style>

View File

@@ -550,12 +550,16 @@
}
// 全站统一Avue 操作列文字链接保持间距并支持自动换行
.avue-crud .avue-crud__menu {
// 注意Avue 自带 .avue-crud .avue-crud__menu{display:block}(特异性同为 0,2,0
// 此处重复类名升至 0,3,0 并强制 display:inline-flex !important彻底压过 Avue 内置样式,
// 避免 dev 模式下 CSS 注入顺序导致 .avue-crud__menu 退化成 block 而让操作项竖排错位。
.avue-crud .avue-crud__menu.avue-crud__menu {
display: inline-flex !important;
flex-wrap: wrap;
gap: 8px;
align-items: center;
justify-content: start;
width: 100% !important;
text-align: left;
}
@@ -567,6 +571,27 @@
gap: 8px;
}
// 全站统一:表格操作列文字链接统一为主色(蓝色)
// 覆盖 Avue #menu 槽位与手写表格操作列中 el-link 的 warning / success / danger 等类型,
// 同时统一 hover / active / disabled 态,保证全站操作列风格一致。
// 选择器同时覆盖 Avue 行菜单(.avue-crud__menu与任意含 el-link 的表格体单元格(手写操作列)。
.avue-crud__menu .el-link,
.el-table .el-table__body .cell:has(.el-link) .el-link {
// 覆盖 Element Plus el-link 默认的 font-weight: var(--el-font-weight-primary)500 中等粗),
// 操作列文字统一为常规字重,避免「查看/编辑/删除」等链接显得加粗。
font-weight: normal;
--el-link-text-color: var(--el-color-primary);
--el-link-hover-text-color: var(--el-color-primary-light-3);
--el-link-active-text-color: var(--el-color-primary-dark-2);
--el-link-disabled-text-color: var(--el-color-primary-light-5);
}
// 全站统一:去除操作列 el-link 默认 margin避免与 8px gap 叠加导致文字间距过宽
.avue-crud .avue-crud__menu .el-link,
.el-table .el-table__body .cell:not(.avue-crud__menu):has(> .el-link) .el-link {
margin: 0;
}
.avue-crud__menu > .el-button,
.avue-crud__menu > .el-dropdown {
margin: 0;
@@ -578,6 +603,44 @@
font-weight: normal;
}
// 全站统一Element Plus 全局开启 show-overflow-tooltip 后,
// 操作列单元格会被加上 .el-tooltip 类并写死宽度,导致内部 el-link 换行后被裁切。
// 此处豁免操作列单元格:让 Avue 行菜单与手写表格操作列保持原有 flex 自动换行,不被固定宽度裁切。
.el-table .cell.avue-crud__menu,
.el-table .el-table__body .cell.el-tooltip:has(> .el-link) {
width: auto !important;
overflow: visible;
text-overflow: clip;
}
// 全站统一Avue 内置行操作按钮(查看 / 编辑 / 删除 / 复制等)视觉对齐为 el-link 链接样式。
// 选择器仅命中表格行操作列 .avue-crud__menu 内的 is-text 按钮,不误伤顶部工具栏(.avue-crud__header
// 与弹窗按钮(.avue-crud__dialog__menu。事件rowView/rowEdit/rowDel与权限permission 指令)
// 由 Avue 自带,此处只改外观,零功能影响。
.avue-crud__menu > .el-button.is-text {
height: auto;
padding: 0;
border: none;
background: transparent;
color: var(--el-color-primary);
font-weight: normal;
&:hover,
&:focus,
&:active {
background: transparent;
color: var(--el-color-primary-light-3);
text-decoration: underline;
}
&.is-disabled,
&.is-disabled:hover {
background: transparent;
color: var(--el-color-primary-light-5);
text-decoration: none;
}
}
@media (max-width: 768px) {
.avue-crud__pagination .el-pagination,
.empty-pagination .el-pagination,

View File

@@ -1,6 +1,6 @@
export const TABLE_MENU_WIDTH = {
default: 160,
three: 220,
default: 180,
three: 180,
four: 320,
};

View File

@@ -321,7 +321,7 @@ export default {
height: 'auto',
calcHeight: 32,
dialogWidth: 700,
menuWidth: 160,
menuWidth: 180,
tip: false,
searchShow: true,
searchMenuSpan: 24,
@@ -434,6 +434,7 @@ export default {
{
label: '过期时间',
prop: 'expireTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -500,7 +501,7 @@ export default {
editBtn: false,
addBtn: false,
delBtn: false,
menuWidth: 160,
menuWidth: 180,
dialogType: 'drawer',
dialogWidth: 800,
column: [
@@ -512,7 +513,8 @@ export default {
{ label: '请求方法', prop: 'method', slot: true, width: 85 },
{ label: '调用IP', prop: 'remoteIp', search: true, width: 140 },
{ label: '耗时(ms)', prop: 'time', width: 85 },
{ label: '日志时间', prop: 'createTime', span: 24, width: 180 },
{ label: '日志时间', prop: 'createTime',
sortable: true, span: 24, width: 180 },
{ label: '用户代理', prop: 'userAgent', span: 24, hide: true },
{ label: '请求数据', prop: 'params', type: 'textarea', span: 24, minRows: 2, hide: true },
],

View File

@@ -164,7 +164,7 @@ export default {
selection: true,
labelWidth: 'auto',
searchLabelWidth: 160,
menuWidth: 160,
menuWidth: 180,
dialogWidth: 900,
dialogClickModal: false,
column: [
@@ -275,7 +275,7 @@ export default {
editBtn: false,
addBtn: false,
delBtn: false,
menuWidth: 160,
menuWidth: 180,
dialogClickModal: false,
column: [
{
@@ -336,7 +336,7 @@ export default {
viewBtn: true,
selection: true,
labelWidth: 'auto',
menuWidth: 160,
menuWidth: 180,
dialogWidth: 900,
dialogClickModal: false,
column: [

View File

@@ -165,7 +165,7 @@ export default {
selection: true,
labelWidth: 'auto',
searchLabelWidth: 160,
menuWidth: 160,
menuWidth: 180,
dialogWidth: 900,
dialogClickModal: false,
column: [
@@ -312,7 +312,7 @@ export default {
editBtn: false,
addBtn: false,
delBtn: false,
menuWidth: 160,
menuWidth: 180,
dialogClickModal: false,
column: [
{
@@ -375,7 +375,7 @@ export default {
viewBtn: true,
selection: true,
labelWidth: 'auto',
menuWidth: 160,
menuWidth: 180,
dialogWidth: 900,
dialogClickModal: false,
column: [

View File

@@ -211,7 +211,7 @@ export default {
index: true,
viewBtn: true,
selection: true,
menuWidth: 160,
menuWidth: 180,
dialogClickModal: false,
column: [
{
@@ -366,7 +366,7 @@ export default {
editBtn: false,
addBtn: false,
delBtn: false,
menuWidth: 160,
menuWidth: 180,
dialogType: 'drawer',
dialogWidth: 800,
column: [
@@ -393,7 +393,8 @@ export default {
{ label: '服务IP', prop: 'serverIp', hide: true, width: 160 },
{ label: '环境', prop: 'env', hide: true, width: 85 },
{ label: '用户代理', prop: 'userAgent', span: 24, hide: true },
{ label: '触发时间', prop: 'createTime', span: 24, width: 170 },
{ label: '触发时间', prop: 'createTime',
sortable: true, span: 24, width: 170 },
{ label: '请求数据', prop: 'params', type: 'textarea', span: 24, minRows: 2, hide: true },
],
},

View File

@@ -250,7 +250,7 @@ export default {
viewBtn: true,
labelWidth: 'auto',
searchLabelWidth: 160,
menuWidth: 320,
menuWidth: 240,
dialogWidth: 600,
dialogClickModal: false,
column: [

View File

@@ -253,7 +253,7 @@ export default {
editBtn: false,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '编码',
@@ -447,6 +447,7 @@ export default {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -458,6 +459,7 @@ export default {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -170,7 +170,7 @@ export default {
editBtn: false,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
menuFixed: 'right',
column: [
{
@@ -203,6 +203,7 @@ export default {
{
label: '生效日期',
prop: 'effectiveDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -224,6 +225,7 @@ export default {
{
label: '失效日期',
prop: 'expiryDate',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
@@ -287,6 +289,7 @@ export default {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -101,7 +101,7 @@ export default {
editBtn: false,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '中文名称',
@@ -135,6 +135,7 @@ export default {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -146,6 +147,7 @@ export default {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -141,7 +141,7 @@ export default {
editBtn: false,
selection: false,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '费用类型',
@@ -214,6 +214,7 @@ export default {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -225,6 +226,7 @@ export default {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -387,7 +387,7 @@ export default {
editBtn: false,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '编码',
@@ -629,6 +629,7 @@ export default {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -639,6 +640,7 @@ export default {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -219,7 +219,7 @@ export default {
editBtn: false,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '编码',
@@ -411,6 +411,7 @@ export default {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -422,6 +423,7 @@ export default {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -1344,7 +1344,7 @@
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
</el-table-column>
<el-table-column prop="uploadUserName" label="上传人" width="140" align="center" />
<el-table-column prop="uploadTime" label="上传时间" width="170" align="center" />
<el-table-column prop="uploadTime" label="上传时间" width="170" align="center" sortable/>
<el-table-column label="操作" width="120" align="center">
<template #default="{ row, $index }">
<el-link
@@ -1607,7 +1607,7 @@
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
</el-table-column>
<el-table-column prop="uploadUserName" label="上传人" width="140" align="center" />
<el-table-column prop="uploadTime" label="上传时间" width="160" align="center" />
<el-table-column prop="uploadTime" label="上传时间" width="160" align="center" sortable/>
<el-table-column label="操作" width="100" align="center">
<template #default="{ row, $index }">
<el-link
@@ -1633,7 +1633,7 @@
<template #changeRecordJson-form>
<el-table :data="changeRecordRows" border class="business-crud-page__change-table">
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column prop="changeDate" label="变更日期" min-width="150" align="center" />
<el-table-column prop="changeDate" label="变更日期" min-width="150" align="center" sortable/>
<el-table-column prop="handlerUserName" label="经办人" min-width="140" align="center" />
<el-table-column prop="changeType" label="变更类型" min-width="160" align="center" />
<el-table-column
@@ -1808,13 +1808,12 @@
width="96%"
class="business-crud-page__detail-dialog"
>
<div v-loading="detailLoading">
<section
<div v-loading="detailLoading" class="business-crud-page__detail-content">
<section-card
v-for="section in detailSections"
:key="section.title"
class="business-crud-page__detail-section"
:title="section.title"
>
<div class="dialog-section-title">{{ section.title }}</div>
<el-descriptions :column="2" border>
<el-descriptions-item
v-for="field in section.fields"
@@ -1825,9 +1824,8 @@
{{ formatDetailValue(detailRow, field[0]) }}
</el-descriptions-item>
</el-descriptions>
</section>
<section v-if="config.enableTransportPlanForm" class="business-crud-page__detail-section">
<div class="dialog-section-title">货物信息</div>
</section-card>
<section-card v-if="config.enableTransportPlanForm" title="货物信息">
<el-table :data="transportCargoRows" border empty-text="暂无货物信息">
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column prop="cargoName" label="货物名称" min-width="180" />
@@ -1842,9 +1840,8 @@
<el-table-column prop="deviceCode" label="设备编码" min-width="140" />
<el-table-column prop="remark" label="备注" min-width="160" />
</el-table>
</section>
<section v-if="config.enableAttachmentTable" class="business-crud-page__detail-section">
<div class="dialog-section-title">{{ config.attachmentTitle || '附件' }}</div>
</section-card>
<section-card v-if="config.enableAttachmentTable" :title="config.attachmentTitle || '附件'">
<el-table :data="attachmentRows" border empty-text="暂无附件">
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column label="文件名" min-width="260">
@@ -1855,9 +1852,9 @@
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
</el-table-column>
<el-table-column prop="uploadUserName" label="上传人" width="140" />
<el-table-column prop="uploadTime" label="上传时间" width="180" />
<el-table-column prop="uploadTime" label="上传时间" width="180" sortable/>
</el-table>
</section>
</section-card>
</div>
<template #footer>
<el-button type="primary" @click="detailBox = false">关闭</el-button>
@@ -2102,7 +2099,6 @@
<span class="business-crud-page__dispatch-heading-name">
{{ dispatchDialogTitle }}
</span>
<span class="business-crud-page__dispatch-heading-split">|</span>
<span class="business-crud-page__dispatch-heading-no">
{{ dispatchDialogNo || '-' }}
</span>
@@ -2114,8 +2110,9 @@
</el-tag>
</div>
</div>
<div class="business-crud-page__dispatch-summary">
<section-card title="基本信息">
<section-card title="基本信息" class="business-crud-page__dispatch-summary-card">
<div class="business-crud-page__dispatch-summary-grid">
<div class="business-crud-page__dispatch-summary-item">
<div class="business-crud-page__dispatch-summary-label">客户</div>
@@ -2135,22 +2132,22 @@
{{ dispatchRow.contractName || dispatchRow.contractNo || '-' }}
</div>
</div>
<div class="business-crud-page__dispatch-summary-item">
<div class="business-crud-page__dispatch-summary-label">货物信息</div>
<div class="business-crud-page__dispatch-summary-value">
{{ dispatchPlanCargoInfo || '-' }}
</div>
</div>
<div class="business-crud-page__dispatch-summary-item">
<div class="business-crud-page__dispatch-summary-label">计划执行时间</div>
<div class="business-crud-page__dispatch-summary-value">
{{
[dispatchRow.planStartDate, dispatchRow.planEndDate]
.filter(Boolean)
.join('-') || '-'
.join(' ~ ') || '-'
}}
</div>
</div>
<div class="business-crud-page__dispatch-summary-item is-wide">
<div class="business-crud-page__dispatch-summary-label">货物信息</div>
<div class="business-crud-page__dispatch-summary-value">
{{ dispatchPlanCargoInfo || '-' }}
</div>
</div>
<div
class="business-crud-page__dispatch-summary-item business-crud-page__dispatch-summary-item--attachments"
>
@@ -2172,40 +2169,43 @@
</div>
</div>
</section-card>
<section-card title="收发货路线">
<section-card title="收发货路线" class="business-crud-page__dispatch-route-card">
<div class="business-crud-page__dispatch-route">
<div class="business-crud-page__dispatch-route-item">
<span class="business-crud-page__dispatch-route-badge is-start">起</span>
<div class="business-crud-page__dispatch-route-body">
<div class="business-crud-page__dispatch-route-title">
{{ parseDispatchRoutePoint(dispatchRow, 'start').name || '-' }}
<div class="business-crud-page__dispatch-route-item">
<span class="business-crud-page__dispatch-route-badge is-start">起</span>
<div class="business-crud-page__dispatch-route-body">
<div class="business-crud-page__dispatch-route-title">
{{ parseDispatchRoutePoint(dispatchRow, 'start').name || '-' }}
</div>
<div class="business-crud-page__dispatch-route-address">
{{ parseDispatchRoutePoint(dispatchRow, 'start').address || '-' }}
</div>
<div class="business-crud-page__dispatch-route-contact">
{{ parseDispatchRoutePoint(dispatchRow, 'start').contact || '-' }}
</div>
</div>
<div class="business-crud-page__dispatch-route-address">
{{ parseDispatchRoutePoint(dispatchRow, 'start').address || '-' }}
</div>
<div class="business-crud-page__dispatch-route-contact">
{{ parseDispatchRoutePoint(dispatchRow, 'start').contact || '-' }}
</div>
<div class="business-crud-page__dispatch-route-vehicle">
<el-icon><Van /></el-icon>
</div>
<div class="business-crud-page__dispatch-route-item">
<span class="business-crud-page__dispatch-route-badge is-end">终</span>
<div class="business-crud-page__dispatch-route-body">
<div class="business-crud-page__dispatch-route-title">
{{ parseDispatchRoutePoint(dispatchRow, 'end').name || '-' }}
</div>
<div class="business-crud-page__dispatch-route-address">
{{ parseDispatchRoutePoint(dispatchRow, 'end').address || '-' }}
</div>
<div class="business-crud-page__dispatch-route-contact">
{{ parseDispatchRoutePoint(dispatchRow, 'end').contact || '-' }}
</div>
</div>
</div>
</div>
<div class="business-crud-page__dispatch-route-line" />
<div class="business-crud-page__dispatch-route-item">
<span class="business-crud-page__dispatch-route-badge is-end">终</span>
<div class="business-crud-page__dispatch-route-body">
<div class="business-crud-page__dispatch-route-title">
{{ parseDispatchRoutePoint(dispatchRow, 'end').name || '-' }}
</div>
<div class="business-crud-page__dispatch-route-address">
{{ parseDispatchRoutePoint(dispatchRow, 'end').address || '-' }}
</div>
<div class="business-crud-page__dispatch-route-contact">
{{ parseDispatchRoutePoint(dispatchRow, 'end').contact || '-' }}
</div>
</div>
</div>
</div>
</section-card>
</div>
</section-card>
</div>
</div>
<section-card title="待调度列表">
@@ -2312,7 +2312,7 @@
min-width="160"
align="center"
show-overflow-tooltip
/>
sortable/>
<el-table-column label="操作" width="220" align="center" fixed="right">
<template #default="{ row, $index }">
<div class="business-crud-page__dispatch-actions">
@@ -2827,7 +2827,7 @@
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
</el-table-column>
<el-table-column prop="uploadUserName" label="上传人" width="140" align="center" />
<el-table-column prop="uploadTime" label="上传时间" width="160" align="center" />
<el-table-column prop="uploadTime" label="上传时间" width="160" align="center" sortable/>
<el-table-column label="操作" width="100" align="center">
<template #default="{ $index }">
<el-link type="danger" @click="removeDispatchItemAttachment($index)">删除</el-link>
@@ -3268,8 +3268,8 @@
min-width="120"
:formatter="formatTransportRouteUpdateUserName"
/>
<el-table-column prop="updateTime" label="更新时间" min-width="160" />
<el-table-column prop="createTime" label="创建时间" min-width="160" />
<el-table-column prop="updateTime" label="更新时间" min-width="160" sortable/>
<el-table-column prop="createTime" label="创建时间" min-width="160" sortable/>
<el-table-column label="操作" width="110" fixed="right">
<template #default="{ row }">
<el-link type="primary" @click.stop="selectTransportRouteRow(row)">
@@ -3375,8 +3375,8 @@
<el-table-column prop="contactPhone" label="联系方式" min-width="140" />
<el-table-column prop="deptName" label="组织" min-width="150" show-overflow-tooltip />
<el-table-column prop="remark" label="备注" min-width="180" show-overflow-tooltip />
<el-table-column prop="updateTime" label="更新时间" min-width="160" />
<el-table-column prop="createTime" label="创建时间" min-width="160" />
<el-table-column prop="updateTime" label="更新时间" min-width="160" sortable/>
<el-table-column prop="createTime" label="创建时间" min-width="160" sortable/>
<el-table-column label="操作" width="110" fixed="right">
<template #default="{ row }">
<el-link type="primary" @click.stop="selectTransportAddressRow(row)">
@@ -3457,8 +3457,8 @@
<el-table-column prop="latitude" label="纬度" min-width="120" />
<el-table-column prop="regionName" label="行政区划" min-width="160" show-overflow-tooltip />
<el-table-column prop="remark" label="备注" min-width="180" show-overflow-tooltip />
<el-table-column prop="updateTime" label="更新时间" min-width="160" />
<el-table-column prop="createTime" label="创建时间" min-width="160" />
<el-table-column prop="updateTime" label="更新时间" min-width="160" sortable/>
<el-table-column prop="createTime" label="创建时间" min-width="160" sortable/>
<el-table-column label="操作" width="110" fixed="right">
<template #default="{ row }">
<el-link type="primary" @click.stop="selectTransportStationRow(row)">
@@ -3604,8 +3604,8 @@
/>
<el-table-column prop="remark" label="备注" min-width="180" show-overflow-tooltip />
<el-table-column prop="deptName" label="组织" min-width="150" show-overflow-tooltip />
<el-table-column prop="updateTime" label="更新时间" min-width="160" />
<el-table-column prop="createTime" label="创建时间" min-width="160" />
<el-table-column prop="updateTime" label="更新时间" min-width="160" sortable/>
<el-table-column prop="createTime" label="创建时间" min-width="160" sortable/>
<el-table-column label="操作" width="110" fixed="right">
<template #default="{ row }">
<el-link type="primary" @click.stop="selectCommonCargoRow(row)">
@@ -3866,6 +3866,11 @@ export default {
type: Object,
default: () => ({ column: [] }),
},
// 可选:固定操作列宽度;不传则按按钮数量自动估算(沿用既有逻辑)
menuWidth: {
type: Number,
default: null,
},
},
data() {
return {
@@ -3913,7 +3918,11 @@ export default {
defaultDialogButtons: null,
contractCreateActionLoading: '',
transportPlanCreateActionLoading: '',
option: this.cloneOption(this.crudOption, estimateMenuButtonCount(this.config)),
option: (() => {
const opt = this.cloneOption(this.crudOption, estimateMenuButtonCount(this.config));
if (this.menuWidth != null) opt.menuWidth = this.menuWidth;
return opt;
})(),
selectedProjectId: '',
projectOptions: [],
projectLoading: false,
@@ -9319,11 +9328,11 @@ export default {
}
&__dispatch-top {
padding: 22px 0 0;
padding: 0;
}
&__dispatch-heading {
margin-bottom: 20px;
margin-bottom: 14px;
}
&__dispatch-heading-title {
@@ -9334,51 +9343,60 @@ export default {
}
&__dispatch-heading-name {
font-size: 22px;
font-size: 18px;
font-weight: 600;
color: #303133;
}
&__dispatch-heading-split {
font-size: 20px;
color: #606266;
}
&__dispatch-heading-no {
font-size: 15px;
color: #303133;
font-size: 14px;
color: #606266;
}
&__dispatch-summary {
display: grid;
grid-template-columns: minmax(0, 1.15fr) minmax(520px, 1fr);
gap: 32px;
align-items: start;
grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
gap: 16px;
align-items: stretch;
margin-bottom: 12px;
}
&__dispatch-summary-card,
&__dispatch-route-card {
display: flex;
flex-direction: column;
:deep(.section-card__body) {
flex: 1;
}
}
&__dispatch-summary-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
column-gap: 48px;
row-gap: 18px;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px 16px;
}
&__dispatch-summary-item {
min-width: 0;
&.is-wide {
grid-column: span 2;
}
}
&__dispatch-summary-label {
margin-bottom: 8px;
font-size: 14px;
color: #666;
font-weight: 500;
margin-bottom: 4px;
font-size: 12px;
color: #909399;
line-height: 1.4;
}
&__dispatch-summary-value {
min-height: 24px;
font-size: 15px;
min-height: 20px;
font-size: 14px;
line-height: 1.45;
color: #333;
color: #303133;
word-break: break-word;
}
@@ -9388,24 +9406,30 @@ export default {
&__dispatch-attachment-link {
display: inline-block;
margin-right: 22px;
margin-bottom: 6px;
margin-right: 16px;
margin-bottom: 4px;
font-size: 14px;
}
&__dispatch-route {
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr) 72px minmax(0, 1fr);
gap: 24px;
grid-template-columns: minmax(0, 1fr) 56px minmax(0, 1fr);
gap: 40px;
align-items: center;
justify-items: center;
min-height: 200px;
//height: 100%;
margin: 20px 0;
}
&__dispatch-route-item {
display: flex;
align-items: flex-start;
gap: 16px;
max-width: 100%;
align-items: center;
gap: 12px;
max-width: 90%;
padding: 16px;
min-height: 80px;
border: 1px dashed #e3e4ec;
border-radius: 12px;
}
&__dispatch-route-badge {
@@ -9413,12 +9437,13 @@ export default {
flex: 0 0 auto;
align-items: center;
justify-content: center;
width: 46px;
height: 46px;
border-radius: 6px;
width: 40px;
height: 40px;
border-radius: 50%;
color: #fff;
font-size: 16px;
font-weight: 600;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
&.is-start {
background: #2a92ff;
@@ -9430,49 +9455,67 @@ export default {
}
&__dispatch-route-body {
max-width: 340px;
text-align: center;
min-width: 0;
text-align: left;
}
&__dispatch-route-title {
font-size: 22px;
line-height: 1.25;
font-size: 18px;
line-height: 1.35;
color: #303133;
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&__dispatch-route-address {
margin-top: 12px;
font-size: 15px;
line-height: 1.45;
color: #333;
margin-top: 4px;
font-size: 13px;
line-height: 1.5;
color: #606266;
}
&__dispatch-route-contact {
margin-top: 10px;
font-size: 15px;
color: #333;
margin-top: 2px;
font-size: 13px;
color: #909399;
}
&__dispatch-route-line {
&__dispatch-route-vehicle {
position: relative;
width: 72px;
height: 2px;
background-image: linear-gradient(
to right,
var(--el-color-primary) 0,
var(--el-color-primary) 6px,
transparent 6px,
transparent 10px
);
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
margin: 0 auto;
border-radius: 50%;
background: #fff;
border: 2px solid var(--el-color-primary);
color: var(--el-color-primary);
font-size: 22px;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.18);
&::before,
&::after {
content: '';
position: absolute;
right: -1px;
top: -4px;
border: 5px solid transparent;
border-left-color: var(--el-color-primary);
top: 50%;
width: 20px;
height: 0;
border-top: 2px dashed var(--el-color-primary-light-5);
transform: translateY(-50%);
}
&::before {
right: 100%;
margin-right: 6px;
}
&::after {
left: 100%;
margin-left: 6px;
}
}
@@ -9525,7 +9568,7 @@ export default {
&__dispatch-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
gap: 10px;
padding-right: 0;
padding-top: 16px;
margin-top: 16px;
@@ -9746,24 +9789,28 @@ export default {
}
}
&__detail-section {
margin-bottom: 20px;
&__detail-content {
display: flex;
flex-direction: column;
gap: 12px;
:deep(.el-descriptions__label) {
width: 140px;
color: #606266;
font-weight: 600;
text-align: right;
}
.section-card {
:deep(.el-descriptions__label) {
width: 140px;
color: #606266;
font-weight: 600;
text-align: right;
}
:deep(.el-descriptions__content) {
min-height: 42px;
white-space: pre-wrap;
word-break: break-word;
}
:deep(.el-descriptions__content) {
min-height: 42px;
white-space: pre-wrap;
word-break: break-word;
}
:deep(.el-table) {
border-color: #eff1f7;
:deep(.el-table) {
border-color: #eff1f7;
}
}
}
@@ -9850,4 +9897,25 @@ export default {
}
}
}
.section-card{
margin-bottom: 0 !important;
}
/* 编辑弹窗:含分组的 Avue form 渲染为白卡风格 */
:deep(.avue-form__group:has(.dialog-section-title)) {
background: #fff;
border-radius: 6px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
padding: 16px;
margin-bottom: 16px;
.avue-form__group--title {
padding: 0 !important;
margin-bottom: 12px;
}
.avue-form__row {
padding: 0 !important;
}
}
</style>

View File

@@ -17,7 +17,7 @@
<el-table-column prop="batchNo" label="运单批次号" min-width="150" /><el-table-column prop="projectName" label="项目名称" min-width="150" />
<el-table-column prop="carrierName" label="承运商" min-width="140" /><el-table-column prop="carrierType" label="承运类型" min-width="120" />
<el-table-column prop="waybillCount" label="运单数" width="90" /><el-table-column prop="importTypeName" label="导入方式" width="100" />
<el-table-column prop="createTime" label="创建时间" min-width="170" /><el-table-column prop="updateTime" label="更新时间" min-width="170" />
<el-table-column prop="createTime" label="创建时间" min-width="170" sortable/><el-table-column prop="updateTime" label="更新时间" min-width="170" sortable/>
<el-table-column prop="statusName" label="状态" width="100" /><el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }"><el-link type="primary" @click="openDetail(row)">查看</el-link><el-link type="primary" @click="editBatch(row)">编辑</el-link><el-link type="danger" @click="removeBatch(row)">删除</el-link></template>
</el-table-column>
@@ -27,7 +27,7 @@
<el-dialog v-model="detailVisible" title="导入运单详情" width="90%" append-to-body>
<el-form :inline="true" :model="detailQuery"><el-form-item label="车牌号/船号"><el-input v-model="detailQuery.vehicleNo" clearable /></el-form-item><el-form-item label="货物名称"><el-input v-model="detailQuery.cargoName" clearable /></el-form-item><el-button type="primary" @click="loadDetails">查询</el-button></el-form>
<el-table :data="details" border><el-table-column type="index" label="序号" width="65" /><el-table-column prop="batchNo" label="运单批次号" min-width="140" /><el-table-column prop="originalNo" label="原始单号" min-width="130" /><el-table-column prop="vehicleNo" label="车牌号/航班号/船号/班列号" min-width="190" /><el-table-column prop="driverName" label="司机/船长" min-width="120" /><el-table-column prop="transportType" label="运输类型" width="110" /><el-table-column prop="cargoName" label="货物名称" width="130" /><el-table-column prop="cargoType" label="货物类型" width="120" /><el-table-column prop="quantity" label="重量" width="90" /><el-table-column prop="departureAddress" label="发货地址" min-width="220" show-overflow-tooltip /><el-table-column prop="departureContact" label="发货联系人" width="110" /><el-table-column prop="departurePhone" label="发货联系人电话" width="140" /><el-table-column prop="arrivalAddress" label="到货地址" min-width="220" show-overflow-tooltip /><el-table-column prop="arrivalContact" label="收货联系人" width="110" /><el-table-column prop="arrivalPhone" label="收货联系人电话" width="140" /><el-table-column prop="startDate" label="开始时间" width="160" /><el-table-column prop="endDate" label="结束时间" width="160" /><el-table-column prop="unitPrice" label="单价" width="90" /><el-table-column prop="freight" label="运费" width="100" /><el-table-column prop="otherFeeTotal" label="其他费用合计" width="120" /><el-table-column prop="freightTotal" label="运费合计" width="100" /><el-table-column prop="remark" label="备注" min-width="160" /><el-table-column label="操作" width="80">-</el-table-column></el-table>
<el-table :data="details" border><el-table-column type="index" label="序号" width="65" /><el-table-column prop="batchNo" label="运单批次号" min-width="140" /><el-table-column prop="originalNo" label="原始单号" min-width="130" /><el-table-column prop="vehicleNo" label="车牌号/航班号/船号/班列号" min-width="190" /><el-table-column prop="driverName" label="司机/船长" min-width="120" /><el-table-column prop="transportType" label="运输类型" width="110" /><el-table-column prop="cargoName" label="货物名称" width="130" /><el-table-column prop="cargoType" label="货物类型" width="120" /><el-table-column prop="quantity" label="重量" width="90" /><el-table-column prop="departureAddress" label="发货地址" min-width="220" show-overflow-tooltip /><el-table-column prop="departureContact" label="发货联系人" width="110" /><el-table-column prop="departurePhone" label="发货联系人电话" width="140" /><el-table-column prop="arrivalAddress" label="到货地址" min-width="220" show-overflow-tooltip /><el-table-column prop="arrivalContact" label="收货联系人" width="110" /><el-table-column prop="arrivalPhone" label="收货联系人电话" width="140" /><el-table-column prop="startDate" label="开始时间" width="160" sortable/><el-table-column prop="endDate" label="结束时间" width="160" sortable/><el-table-column prop="unitPrice" label="单价" width="90" /><el-table-column prop="freight" label="运费" width="100" /><el-table-column prop="otherFeeTotal" label="其他费用合计" width="120" /><el-table-column prop="freightTotal" label="运费合计" width="100" /><el-table-column prop="remark" label="备注" min-width="160" /><el-table-column label="操作" width="80">-</el-table-column></el-table>
<el-pagination v-model:current-page="detailPage.current" v-model:page-size="detailPage.size" layout="total, prev, pager, next, sizes" :page-sizes="[10, 20, 50]" :total="detailPage.total" @current-change="loadDetails" @size-change="loadDetails" />
</el-dialog>

View File

@@ -1,5 +1,5 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
<business-crud-page :api="api" :config="config" :crud-option="option" :menu-width="245" />
</template>
<script>

View File

@@ -297,8 +297,8 @@
show-overflow-tooltip
/>
<el-table-column label="数据来源" prop="dataSource" min-width="130" show-overflow-tooltip />
<el-table-column label="创建时间" prop="createTime" min-width="170" show-overflow-tooltip />
<el-table-column label="更新时间" prop="updateTime" min-width="170" show-overflow-tooltip />
<el-table-column label="创建时间" prop="createTime" min-width="170" show-overflow-tooltip sortable/>
<el-table-column label="更新时间" prop="updateTime" min-width="170" show-overflow-tooltip sortable/>
<el-table-column
label="过程节点"
prop="currentProcessNode"
@@ -805,7 +805,7 @@
<el-table-column prop="weight" label="重量" min-width="120" />
<el-table-column prop="volume" label="体积" min-width="120" />
<el-table-column prop="quantity" label="数量" min-width="100" />
<el-table-column prop="planDate" label="计划日期" min-width="130" />
<el-table-column prop="planDate" label="计划日期" min-width="130" sortable/>
<el-table-column prop="unitPrice" label="货物单价" min-width="150" />
<el-table-column prop="materialCode" label="物料编码" min-width="130" />
<el-table-column prop="equipmentCode" label="设备编码" min-width="130" />

View File

@@ -511,7 +511,7 @@
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
</el-table-column>
<el-table-column prop="uploadUserName" label="上传人" min-width="140" align="center" />
<el-table-column prop="uploadTime" label="上传时间" min-width="170" align="center" />
<el-table-column prop="uploadTime" label="上传时间" min-width="170" align="center" sortable/>
<el-table-column label="操作" width="110" align="center">
<template #default="{ row, $index }">
<el-link type="primary" @click="downloadAttachment(row)">下载</el-link>
@@ -543,7 +543,7 @@
class="project-apply-form__table"
>
<el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="changeDate" label="变更日期" min-width="140" align="center" />
<el-table-column prop="changeDate" label="变更日期" min-width="140" align="center" sortable/>
<el-table-column prop="changeType" label="变更类型" min-width="160" align="center" />
<el-table-column prop="handler" label="经办人" min-width="120" align="center" />
<el-table-column
@@ -695,7 +695,6 @@ import { getDictionary as getSystemDictionary } from '@/api/system/dict';
import { getDictionary as getBizDictionary } from '@/api/system/dictbiz';
import { getList as getUserList } from '@/api/system/user';
import { getToken } from '@/utils/auth';
import { applyTableMenuWidth } from '@/utils/table-menu';
import { downloadFileByUrl, downloadXls } from '@/utils/util';
import { mapGetters } from 'vuex';
import NProgress from 'nprogress';
@@ -1036,18 +1035,17 @@ export default {
},
methods: {
buildTableOption() {
return applyTableMenuWidth(
{
...option,
dialogType: 'dialog',
addBtn: false,
viewBtn: false,
editBtn: false,
delBtn: false,
column: (option.column || []).map(column => ({ ...column, display: false })),
},
4
);
return {
...option,
dialogType: 'dialog',
addBtn: false,
viewBtn: false,
editBtn: false,
delBtn: false,
// 操作列收窄本项目操作按钮较少320px 过宽,调整为 220px链接会自动换行兜底
menuWidth: 180,
column: (option.column || []).map(column => ({ ...column, display: false })),
};
},
hasPermission(code) {
return this.isAdmin || this.validData(this.permission && this.permission[code], false);

View File

@@ -1,5 +1,5 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
<business-crud-page :api="api" :config="config" :crud-option="option" :menu-width="270" />
</template>
<script>

View File

@@ -1,5 +1,5 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
<business-crud-page :api="api" :config="config" :crud-option="option" :menu-width="270" />
</template>
<script>

View File

@@ -1,5 +1,5 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
<business-crud-page :api="api" :config="config" :crud-option="option" :menu-width="220" />
</template>
<script>

View File

@@ -1,5 +1,5 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
<business-crud-page :api="api" :config="config" :crud-option="option" :menu-width="250" />
</template>
<script>

View File

@@ -99,7 +99,7 @@ export default {
editBtn: false,
addBtn: false,
delBtn: false,
menuWidth: 160,
menuWidth: 180,
labelWidth: 'auto',
dialogType: 'drawer',
column: [
@@ -182,6 +182,7 @@ export default {
{
label: '记录时间',
prop: 'recordTime',
sortable: true,
width: '180',
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -129,6 +129,7 @@ const option = reactive({
{
label: '通知日期',
prop: 'releaseTime',
sortable: true,
type: 'date',
gridRow: true,
format: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -59,6 +59,7 @@ export default {
height: 'auto',
calcHeight: 32,
dialogWidth: 950,
menuWidth: 180,
tip: false,
searchShow: true,
searchMenuSpan: 24,
@@ -130,6 +131,7 @@ export default {
{
label: '通知日期',
prop: 'releaseTime',
sortable: true,
type: 'date',
gridRow: true,
format: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -73,7 +73,7 @@ export default {
addBtn: false,
viewBtn: false,
dialogWidth: 900,
menuWidth: 160,
menuWidth: 180,
dialogClickModal: false,
column: [
{
@@ -107,6 +107,7 @@ export default {
{
label: '开始时间',
prop: 'startTime',
sortable: true,
width: 170,
},
],

View File

@@ -156,7 +156,7 @@ export default {
viewBtn: false,
delBtn: false,
dialogWidth: 900,
menuWidth: 160,
menuWidth: 180,
dialogClickModal: false,
column: [
{
@@ -210,6 +210,7 @@ export default {
{
label: '部署时间',
prop: 'deploymentTime',
sortable: true,
width: 170,
},
],

View File

@@ -213,7 +213,7 @@ export default {
viewBtn: false,
delBtn: false,
dialogWidth: 900,
menuWidth: 160,
menuWidth: 180,
dialogClickModal: false,
size: 'default',
searchSize: 'default',
@@ -243,11 +243,13 @@ export default {
{
label: '创建时间',
prop: 'created',
sortable: true,
overHidden: true,
},
{
label: '更新时间',
prop: 'lastUpdated',
sortable: true,
overHidden: true,
},
],

View File

@@ -51,7 +51,7 @@ export default {
editBtn: false,
addBtn: false,
delBtn: false,
menuWidth: 160,
menuWidth: 180,
dialogType: 'drawer',
column: [
{
@@ -91,6 +91,7 @@ export default {
{
label: '日志时间',
prop: 'createTime',
sortable: true,
width: '180',
},
{

View File

@@ -51,7 +51,7 @@ export default {
editBtn: false,
addBtn: false,
delBtn: false,
menuWidth: 160,
menuWidth: 180,
dialogType: 'drawer',
column: [
{
@@ -88,6 +88,7 @@ export default {
{
label: '日志时间',
prop: 'createTime',
sortable: true,
width: '180',
},
{

View File

@@ -51,7 +51,7 @@ export default {
editBtn: false,
addBtn: false,
delBtn: false,
menuWidth: 160,
menuWidth: 180,
dialogType: 'drawer',
column: [
{
@@ -88,6 +88,7 @@ export default {
{
label: '日志时间',
prop: 'createTime',
sortable: true,
width: '180',
},
{

View File

@@ -84,10 +84,12 @@ export default {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
},
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
},
],
},

View File

@@ -92,7 +92,7 @@ export default {
viewBtn: true,
selection: true,
grid: true,
menuWidth: 160,
menuWidth: 180,
labelWidth: 'auto',
dialogWidth: 880,
dialogClickModal: false,

View File

@@ -91,7 +91,7 @@ export default {
viewBtn: true,
selection: true,
grid: true,
menuWidth: 160,
menuWidth: 180,
labelWidth: 'auto',
dialogWidth: 880,
dialogClickModal: false,

View File

@@ -40,23 +40,121 @@
</el-link>
</template>
<template #deptCategory="{ row }">
<el-tag>{{ row.deptCategoryName }}</el-tag>
<el-tag>{{ getDeptCategoryLabel(row.deptCategory) }}</el-tag>
</template>
<template #leaderId="{ row }">
{{ getLeaderValue(row.leaderId, 'realName') }}
</template>
<template #leaderPhone="{ row }">
{{ getLeaderValue(row.leaderId, 'phone') }}
</template>
<template #status="{ row }">
<el-tag :type="Number(row.status) === 1 ? 'success' : 'info'">
{{ Number(row.status) === 1 ? '启用' : '停用' }}
</el-tag>
</template>
</avue-crud>
<el-dialog v-model="carrierDialogVisible" title="选择承运商" width="1100px" append-to-body>
<el-form :inline="true" :model="carrierQuery" class="carrier-search-form">
<el-form-item label="名称">
<el-input v-model="carrierQuery.fullName" placeholder="请输入承运商名称" clearable />
</el-form-item>
<el-form-item label="准入标准">
<el-select v-model="carrierQuery.accessType" placeholder="全部" clearable>
<el-option label="临时" value="temporary" />
<el-option label="正式" value="formal" />
</el-select>
</el-form-item>
<el-form-item label="审批状态">
<el-select v-model="carrierQuery.approvalStatus" placeholder="全部" clearable>
<el-option label="草稿" value="draft" />
<el-option label="审核中" value="reviewing" />
<el-option label="审核通过" value="approved" />
<el-option label="审核不通过" value="rejected" />
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="carrierQuery.status" placeholder="全部" clearable>
<el-option label="启用" :value="1" />
<el-option label="停用" :value="2" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchCarrier">搜索</el-button>
<el-button @click="resetCarrierQuery">清空</el-button>
</el-form-item>
</el-form>
<el-table :data="carrierData" border stripe height="420" @row-dblclick="selectCarrier">
<el-table-column prop="customerCode" label="客商编号" min-width="130" />
<el-table-column prop="shortName" label="客商简称" min-width="140" />
<el-table-column prop="fullName" label="客商名称" min-width="200" show-overflow-tooltip />
<el-table-column prop="customerType" label="客户类型" min-width="120" />
<el-table-column prop="customerNature" label="客户性质" min-width="120" />
<el-table-column prop="unifiedCreditCode" label="统一信用代码" min-width="180" />
<el-table-column prop="deptName" label="所属组织" min-width="150" />
<el-table-column prop="accessType" label="准入标准" min-width="110">
<template #default="{ row }">{{ formatAccessType(row.accessType) }}</template>
</el-table-column>
<el-table-column prop="approvalStatus" label="审批状态" min-width="120">
<template #default="{ row }">{{ formatApprovalStatus(row.approvalStatus) }}</template>
</el-table-column>
<el-table-column prop="currentNode" label="当前节点" min-width="130" />
<el-table-column prop="currentProcessor" label="当前处理人" min-width="130" />
<el-table-column prop="approvedTime" label="审核通过时间" min-width="170" sortable/>
<el-table-column prop="status" label="状态" width="90">
<template #default="{ row }">{{ Number(row.status) === 1 ? '启用' : '停用' }}</template>
</el-table-column>
<el-table-column label="操作" width="90" fixed="right">
<template #default="{ row }">
<el-link type="primary" @click="selectCarrier(row)">选择</el-link>
</template>
</el-table-column>
</el-table>
<div class="carrier-pagination">
<el-pagination
v-model:current-page="carrierPage.current"
v-model:page-size="carrierPage.size"
:total="carrierPage.total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
@current-change="loadCarrierList"
@size-change="handleCarrierSizeChange"
/>
</div>
</el-dialog>
</basic-container>
</template>
<script>
import { getLazyList, remove, update, add, getDept, getDeptTree } from '@/api/system/dept';
import { getLeaderList } from '@/api/system/user';
import { getList as getCustomerArchiveList } from '@/api/vehicle/customer-archive';
import { mapGetters } from 'vuex';
import website from '@/config/website';
import func from '@/utils/func';
const escapeRegExp = value => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
export default {
data() {
return {
form: {},
leaderOptions: [],
parentDeptCode: '',
parentDept: null,
carrierDialogVisible: false,
carrierData: [],
carrierQuery: {
fullName: '',
accessType: '',
approvalStatus: '',
status: undefined,
},
carrierPage: {
current: 1,
size: 10,
total: 0,
},
selectionList: [],
query: {},
loading: true,
@@ -73,29 +171,170 @@ export default {
searchShow: true,
searchMenuSpan: 24,
searchIcon: true,
searchIndex: 8,
searchIndex: 4,
searchMenuPosition: 'right',
tree: true,
border: true,
index: true,
selection: true,
viewBtn: true,
menuWidth: 160,
menuWidth: 320,
dialogWidth: 800,
dialogClickModal: false,
labelPosition: 'right',
labelWidth: 'auto',
column: [
{
label: '机构名称',
label: '组织名称',
prop: 'deptName',
minWidth: 160,
order: 80,
search: true,
rules: [
{
required: true,
message: '请输入机构名称',
message: '请输入组织名称',
trigger: 'blur',
},
],
},
{
label: '组织编码',
prop: 'deptCode',
minWidth: 140,
order: 70,
maxlength: 30,
showWordLimit: true,
rules: [
{
required: true,
message: '请输入组织编码',
trigger: 'blur',
},
{
validator: (rule, value, callback) => {
const deptCode = String(value || '').trim();
if (deptCode.length > 30) {
callback(new Error('组织编码不能超过30个字符'));
return;
}
if (!this.parentDeptCode) {
callback(new Error('请先选择上级组织'));
return;
}
const pattern = new RegExp(`^${escapeRegExp(this.parentDeptCode)}-\\d+$`);
if (!pattern.test(deptCode)) {
callback(new Error(`组织编码格式应为:${this.parentDeptCode}-分段数字`));
return;
}
callback();
},
trigger: 'blur',
},
],
},
{
label: '组织简称',
prop: 'shortName',
minWidth: 140,
order: 60,
},
{
label: '组织类型',
type: 'select',
dicData: [
{ label: '一级集团', value: 1 },
{ label: '二级事业部', value: 2 },
{ label: '部门', value: 3 },
{ label: '三级分公司', value: 4 },
{ label: '物流园区', value: 5 },
{ label: '承运商', value: 6 },
],
dataType: 'number',
minWidth: 120,
order: 90,
prop: 'deptCategory',
slot: true,
change: ({ value }) => this.handleDeptCategoryChange(value),
rules: [
{
required: true,
message: '请选择组织类型',
trigger: 'change',
},
],
},
{
label: '负责人',
prop: 'leaderId',
minWidth: 120,
slot: true,
addDisplay: false,
editDisplay: false,
},
{
label: '联系电话',
prop: 'leaderPhone',
minWidth: 140,
slot: true,
addDisplay: false,
editDisplay: false,
},
{
label: '助记码',
prop: 'mnemonicCode',
minWidth: 120,
order: 40,
},
{
label: '拼音助记码',
prop: 'pinyinMnemonic',
minWidth: 140,
order: 50,
},
{
label: '排序',
prop: 'sort',
type: 'number',
align: 'right',
width: 80,
order: 30,
rules: [
{
required: true,
message: '请输入排序',
trigger: 'blur',
},
],
},
{
label: '状态',
prop: 'status',
width: 100,
slot: true,
addDisplay: false,
editDisplay: false,
},
{
label: '上级组织',
prop: 'parentId',
dicData: [],
type: 'tree',
hide: true,
addDisabled: false,
order: 100,
change: ({ value }) => this.handleParentChange(value),
props: {
label: 'title',
},
rules: [
{
required: true,
message: '请选择上级组织',
trigger: 'click',
},
],
},
{
label: '所属租户',
prop: 'tenantId',
@@ -104,112 +343,37 @@ export default {
addDisplay: false,
editDisplay: false,
viewDisplay: website.tenantMode,
span: 24,
props: {
label: 'tenantName',
value: 'tenantId',
},
hide: !website.tenantMode,
hide: true,
search: website.tenantMode,
rules: [
{
required: true,
message: '请输入所属租户',
trigger: 'click',
},
],
},
{
label: '机构全称',
prop: 'fullName',
search: true,
rules: [
{
required: true,
message: '请输入机构全称',
trigger: 'blur',
},
],
},
{
label: '上级机构',
prop: 'parentId',
dicData: [],
type: 'tree',
hide: true,
addDisabled: false,
props: {
label: 'title',
},
rules: [
{
required: false,
message: '请选择上级机构',
trigger: 'click',
},
],
},
{
label: '直属主管',
prop: 'leaderId',
type: 'tree',
multiple: true,
filterable: true,
dicData: [],
props: {
label: 'realName',
value: 'id',
},
clearable: true,
hide: true,
},
{
label: '机构类型',
type: 'select',
dicUrl: '/blade-system/dict/dictionary?code=org_category',
props: {
label: 'dictValue',
value: 'dictKey',
},
dataType: 'number',
width: 120,
prop: 'deptCategory',
slot: true,
rules: [
{
required: true,
message: '请输入机构类型',
trigger: 'blur',
},
],
},
{
label: '排序',
prop: 'sort',
type: 'number',
align: 'right',
width: 80,
rules: [
{
required: true,
message: '请输入排序',
trigger: 'blur',
},
],
addDisplay: false,
editDisplay: false,
},
{
label: '备注',
prop: 'remark',
type: 'textarea',
span: 24,
minRows: 2,
hide: true,
addDisplay: false,
editDisplay: false,
},
],
},
data: [],
};
},
created() {
this.loadLeaderOptions();
},
computed: {
...mapGetters(['userInfo', 'permission']),
permissionList() {
@@ -234,13 +398,130 @@ export default {
const column = this.findColumn(this.option.column, 'parentId');
column.dicData = res.data.data;
});
getLeaderList(tenantId).then(res => {
const column = this.findColumn(this.option.column, 'leaderId');
column.dicData = res.data.data;
},
handleParentChange(parentId) {
this.loadParentDeptCode(parentId);
},
loadParentDeptCode(parentId) {
this.parentDeptCode = '';
this.parentDept = null;
if (!parentId || String(parentId) === '0') return;
getDept(parentId).then(res => {
this.parentDept = res.data.data || null;
this.parentDeptCode = this.parentDept?.deptCode || '';
this.updateDeptCategoryOptions();
});
},
updateDeptCategoryOptions() {
const categoryColumn = this.findColumn(this.option.column, 'deptCategory');
const parent = this.parentDept;
if (!parent) return;
let dicData = [];
if (this.isRootDept(parent)) {
dicData =
parent.deptName === '外部组织'
? [{ label: '承运商', value: 6 }]
: [{ label: '集团', value: 1 }];
} else if (Number(parent.deptCategory) === 1) {
dicData = [
{ label: '事业部', value: 2 },
{ label: '部门', value: 3 },
];
} else if ([2, 3].includes(Number(parent.deptCategory))) {
dicData = [
{ label: '分公司', value: 4 },
{ label: '物流园区', value: 5 },
{ label: '承运商', value: 6 },
];
}
categoryColumn.dicData = dicData;
if (!dicData.some(item => item.value === Number(this.form.deptCategory))) {
this.form.deptCategory = dicData[0]?.value;
}
if (Number(this.form.deptCategory) === 6) {
this.openCarrierDialog();
}
},
handleDeptCategoryChange(value) {
if (Number(value) === 6) {
this.openCarrierDialog();
}
},
openCarrierDialog() {
this.carrierDialogVisible = true;
this.loadCarrierList();
},
loadCarrierList() {
getCustomerArchiveList(this.carrierPage.current, this.carrierPage.size, {
...this.carrierQuery,
customerType: '承运商',
}).then(res => {
const page = res.data.data || {};
this.carrierData = page.records || [];
this.carrierPage.total = page.total || 0;
});
},
searchCarrier() {
this.carrierPage.current = 1;
this.loadCarrierList();
},
resetCarrierQuery() {
this.carrierQuery = { fullName: '', accessType: '', approvalStatus: '', status: undefined };
this.searchCarrier();
},
handleCarrierSizeChange(size) {
this.carrierPage.size = size;
this.carrierPage.current = 1;
this.loadCarrierList();
},
selectCarrier(row) {
this.form.carrierCustomerId = row.id;
this.form.deptName = row.fullName;
this.form.shortName = row.shortName;
this.form.mnemonicCode = row.mnemonicCode;
this.carrierDialogVisible = false;
},
formatAccessType(value) {
return value === 'formal' ? '正式' : value === 'temporary' ? '临时' : '-';
},
formatApprovalStatus(value) {
const statusMap = {
draft: '草稿',
reviewing: '审核中',
approved: '审核通过',
rejected: '审核不通过',
};
return statusMap[value] || '-';
},
loadLeaderOptions(tenantId) {
getLeaderList(tenantId).then(res => {
this.leaderOptions = res.data.data || [];
});
},
getDeptCategoryLabel(deptCategory) {
const categoryMap = {
1: '集团',
2: '事业部',
3: '部门',
4: '分公司',
5: '物流园区',
6: '承运商',
};
return categoryMap[Number(deptCategory)] || '-';
},
getLeaderValue(leaderId, field) {
const values = func.split(leaderId);
const result = values
.map(id => this.leaderOptions.find(item => String(item.id) === String(id))?.[field])
.filter(Boolean);
return result.length ? result.join('、') : '-';
},
isRootDept(row) {
return row && (row.parentId === 0 || String(row.parentId) === '0');
},
handleAdd(row) {
this.parentId = row.id;
this.loadParentDeptCode(row.id);
const column = this.findColumn(this.option.column, 'parentId');
column.value = row.id;
column.addDisabled = true;
@@ -248,7 +529,13 @@ export default {
},
rowSave(row, done, loading) {
row.tenantId = row.tenantId || this.userInfo.tenantId || website.tenantId;
row.deptCode = String(row.deptCode || '').trim();
row.leaderId = func.join(row.leaderId);
if (Number(row.deptCategory) === 6 && !row.carrierCustomerId) {
this.$message.warning('请选择承运商');
loading();
return;
}
add(row).then(
res => {
// 获取新增数据的相关字段
@@ -271,7 +558,13 @@ export default {
},
rowUpdate(row, index, done, loading) {
row.tenantId = row.tenantId || this.userInfo.tenantId || website.tenantId;
row.deptCode = String(row.deptCode || '').trim();
row.leaderId = func.join(row.leaderId);
if (Number(row.deptCategory) === 6 && !row.carrierCustomerId) {
this.$message.warning('请选择承运商');
loading();
return;
}
update(row).then(
() => {
this.$message({
@@ -288,6 +581,10 @@ export default {
);
},
rowDel(row, index, done) {
if (this.isRootDept(row)) {
this.$message.warning('根节点不能删除');
return;
}
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
@@ -310,13 +607,22 @@ export default {
this.$message.warning('请选择至少一条数据');
return;
}
const deletableRows = this.selectionList.filter(row => !this.isRootDept(row));
if (deletableRows.length === 0) {
this.$message.warning('根节点不能删除');
return;
}
if (deletableRows.length !== this.selectionList.length) {
this.$message.warning('根节点不能删除,已忽略根节点');
}
const deletableIds = deletableRows.map(row => row.id).join(',');
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
return remove(this.ids);
return remove(deletableIds);
})
.then(() => {
// 刷新表格数据并重载
@@ -355,6 +661,7 @@ export default {
if (type === 'add') {
this.form.tenantId = this.form.tenantId || this.userInfo.tenantId || website.tenantId;
this.initData(this.form.tenantId);
this.loadParentDeptCode(this.form.parentId);
}
if (type === 'edit') {
this.initData(this.form.tenantId || this.userInfo.tenantId || website.tenantId);
@@ -367,6 +674,7 @@ export default {
if (this.form.parentId === '0') {
this.form.parentId = '';
}
this.loadParentDeptCode(this.form.parentId);
if (this.form.hasOwnProperty('leaderId')) {
this.form.leaderId = func.split(this.form.leaderId);
}
@@ -376,6 +684,8 @@ export default {
},
beforeClose(done) {
this.parentId = '';
this.parentDeptCode = '';
this.parentDept = null;
const column = this.findColumn(this.option.column, 'parentId');
column.value = '';
column.addDisabled = false;
@@ -408,4 +718,34 @@ export default {
};
</script>
<style></style>
<style scoped>
.carrier-search-form {
padding: 12px 12px 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.carrier-pagination {
display: flex;
justify-content: flex-end;
margin-top: 12px;
}
/* 操作列兜底:确保「查看 / 编辑 / 删除 / 新增子项」横向排列并保留 8px 间距,
避免退化成 block 导致操作项竖排错位。全局样式已统一处理,此处为双保险。 */
:deep(.avue-crud__menu) {
display: inline-flex !important;
flex-wrap: wrap;
gap: 8px;
align-items: center;
justify-content: flex-start;
width: 100%;
text-align: left;
> .el-button,
> .el-link,
> .el-dropdown {
flex-shrink: 0;
display: inline-flex;
}
}
</style>

View File

@@ -88,7 +88,7 @@ export default {
index: true,
selection: true,
viewBtn: true,
menuWidth: 320,
menuWidth: 220,
dialogClickModal: false,
column: [
{

View File

@@ -81,7 +81,7 @@
</el-table-column>
</template>
<!-- 操作栏模块 -->
<el-table-column prop="menu" label="操作" :width="220" align="center">
<el-table-column prop="menu" label="操作" :width="180" align="center">
<template #="{ row }">
<el-link
v-if="this.permissionList.viewBtn"

View File

@@ -237,7 +237,7 @@ export default {
selection: true,
addBtn: false,
viewBtn: true,
menuWidth: 160,
menuWidth: 240,
dialogWidth: 900,
dialogClickModal: false,
column: [
@@ -308,6 +308,7 @@ export default {
{
label: '过期时间',
prop: 'expireTime',
sortable: true,
width: 180,
slot: true,
addDisplay: false,
@@ -350,6 +351,7 @@ export default {
label: '过期时间',
labelTip: '代表租户可使用的最后日期,若不限制则默认为空',
prop: 'expireTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -171,7 +171,7 @@ export default {
index: true,
viewBtn: true,
selection: true,
menuWidth: 160,
menuWidth: 180,
dialogWidth: 900,
dialogClickModal: false,
column: [

View File

@@ -37,7 +37,7 @@
v-if="permission.user_add && !auditMode"
type="primary"
icon="el-icon-plus"
@click="$refs.crud.rowAdd()"
@click="openUserDialog('add')"
>
</el-button>
<el-button
@@ -134,7 +134,7 @@
<el-link
type="primary"
v-if="this.permission.user_edit"
@click.stop="$refs.crud.rowEdit(row, index)"
@click.stop="openUserDialog('edit', row)"
>编辑
</el-link>
<el-dropdown @command="command => handleDropdownCommand(command, row, index)">
@@ -147,12 +147,6 @@
<el-dropdown-item command="grantRole" v-if="permission.user_role">
角色配置
</el-dropdown-item>
<el-dropdown-item
command="platformConfig"
v-if="userInfo.authority.includes('admin')"
>
平台配置
</el-dropdown-item>
<el-dropdown-item command="authLog" v-if="userInfo.authority.includes('admin')">
认证日志
</el-dropdown-item>
@@ -172,20 +166,62 @@
</template>
</el-dropdown>
</template>
<template #tenantName="{ row }">
<el-tag>{{ row.tenantName }}</el-tag>
</template>
<template #roleName="{ row }">
<el-tag>{{ row.roleName }}</el-tag>
</template>
<template #deptName="{ row }">
<el-tag>{{ row.deptName }}</el-tag>
</template>
<template #userTypeName="{ row }">
<el-tag>{{ row.userTypeName }}</el-tag>
</template>
</avue-crud>
<el-dialog
v-model="userDialog"
:title="userDialogMode === 'add' ? '新增用户' : '编辑用户'"
width="90%"
top="5vh"
append-to-body
destroy-on-close
class="user-edit-dialog"
>
<template #header>
<span class="dialog-title">{{ userDialogMode === 'add' ? '新增用户' : '编辑用户' }}</span>
</template>
<el-form ref="userFormRef" :model="form" :rules="userRules" label-position="right" label-width="120px">
<el-row :gutter="20">
<el-col :span="6"><el-form-item label="账号名" prop="account"><el-input v-model="form.account" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="姓名" prop="realName"><el-input v-model="form.realName" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="手机号" prop="phone"><el-input v-model="form.phone" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="邮箱"><el-input v-model="form.email" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="性别"><el-select v-model="form.sex" class="full-width"><el-option label="男" :value="1" /><el-option label="女" :value="2" /><el-option label="未知" :value="3" /></el-select></el-form-item></el-col>
<el-col :span="6"><el-form-item label="岗位" prop="postId"><el-tree-select v-model="form.postId" :data="postList" :props="{ label: 'postName', value: 'id' }" check-strictly class="full-width" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="所属组织" prop="deptId"><el-tree-select v-model="form.deptId" :data="permissionDeptTree" :props="{ label: 'title', value: 'id' }" multiple check-strictly show-checkbox class="full-width" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="工号"><el-input v-model="form.code" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="人员类别"><el-select v-model="form.personCategory" class="full-width"><el-option label="内部员工" :value="1" /><el-option label="承运商" :value="2" /></el-select></el-form-item></el-col>
<el-col :span="18"><el-form-item label="数据权限范围"><el-radio-group v-model="form.dataScopeRange"><el-radio :value="1">仅所属组织</el-radio><el-radio :value="2">全部组织</el-radio><el-radio :value="3">自定义</el-radio></el-radio-group></el-form-item></el-col>
<el-col :span="18" :offset="6"><el-form-item label="数据层级范围"><el-radio-group v-model="form.dataLevelRange"><el-radio :value="1">包含下级</el-radio><el-radio :value="2">仅本级</el-radio></el-radio-group></el-form-item></el-col>
<el-col :span="24"><el-form-item label="备注"><el-input v-model="form.remark" type="textarea" :rows="3" maxlength="200" show-word-limit /></el-form-item></el-col>
</el-row>
</el-form>
<el-tabs class="user-permission-tabs">
<el-tab-pane label="已拥有角色">
<el-table :data="formRoleList" border>
<el-table-column type="index" label="序号" width="70" />
<el-table-column prop="id" label="角色编号" />
<el-table-column prop="title" label="角色名称" />
<el-table-column label="操作" width="90"><template #default="{ row }"><el-link type="danger" @click="removeFormRole(row.id)">删除</el-link></template></el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane v-if="form.dataScopeRange === 3" label="数据权限">
<el-tree :data="permissionDeptTree" show-checkbox node-key="id" :default-checked-keys="form.dataScopeDeptIds || []" :props="{ label: 'title', children: 'children' }" @check="handleDataScopeCheck" />
</el-tab-pane>
<el-tab-pane label="客商权限">
<div class="permission-toolbar customer-toolbar"><el-checkbox v-model="form.includeNewCustomer" :true-value="1" :false-value="0">包含新增的客商</el-checkbox></div>
<el-table ref="customerTable" :data="customerList" border @selection-change="handleCustomerSelection"><el-table-column type="selection" width="55" /><el-table-column prop="customerCode" label="客商编码" /><el-table-column prop="fullName" label="客商名称" /></el-table>
</el-tab-pane>
</el-tabs>
<template #footer><span class="dialog-footer"><el-button type="primary" @click="submitUserForm">保存</el-button><el-button @click="userDialog = false">返回</el-button><el-button v-if="userDialogMode === 'edit'" @click="resetDialogPassword">重置密码</el-button><el-button @click="handleAddRole">添加角色</el-button></span></template>
</el-dialog>
<!-- 用户角色配置 -->
<el-dialog title="用户角色配置" append-to-body v-model="roleBox" width="345px">
<el-row
@@ -200,8 +236,13 @@
size="small"
@change="handleLinkedChange('treeRole')"
/>
<el-tooltip content="开启后勾选父节点会自动勾选所有子节点,关闭则可独立勾选任意节点" placement="top">
<el-icon style="margin-left: 4px; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
<el-tooltip
content="开启后勾选父节点会自动勾选所有子节点,关闭则可独立勾选任意节点"
placement="top"
>
<el-icon style="margin-left: 4px; color: #909399; cursor: pointer"
><el-icon-question-filled
/></el-icon>
</el-tooltip>
</span>
<el-button-group>
@@ -246,17 +287,6 @@
</avue-form>
</el-dialog>
<!-- 用户平台配置行级别 -->
<el-dialog title="用户平台配置" append-to-body v-model="platformBox" width="600px">
<avue-form ref="platformFormRef" :option="platformFormOption" v-model="platformForm" />
<template #footer>
<span class="dialog-footer">
<el-button @click="platformBox = false"> </el-button>
<el-button type="primary" @click="submitPlatformConfig"> </el-button>
</span>
</template>
</el-dialog>
<!-- 管理员修改密码 -->
<el-dialog title="修改密码" append-to-body v-model="passwordBox" width="460px">
<el-form
@@ -265,7 +295,7 @@
:rules="passwordRules"
label-width="auto"
>
<el-form-item label="登录账号">
<el-form-item label="账号">
<el-input v-model="passwordForm.account" disabled />
</el-form-item>
<el-form-item label="新密码" prop="password">
@@ -296,10 +326,8 @@
import {
getList,
getUser,
getUserPlatform,
remove,
update,
updatePlatform,
add,
grant,
resetPassword,
@@ -308,6 +336,7 @@ import {
auditRefuse,
setLeader,
getLeaderList,
getCustomerOptions,
} from '@/api/system/user';
import { exportBlob } from '@/api/common';
import { getDeptTree } from '@/api/system/dept';
@@ -315,14 +344,13 @@ import { getRoleTree } from '@/api/system/role';
import { getPostList } from '@/api/system/post';
import { mapGetters } from 'vuex';
import { h } from 'vue';
import website from '@/config/website';
import { getToken } from '@/utils/auth';
import { downloadXls } from '@/utils/util';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import func from '@/utils/func';
import { sensitive } from '@/utils/sensitive';
import { excelOption, platformFormOption, treeOption, userOption } from '@/option/system/user';
import { excelOption, treeOption, userOption } from '@/option/system/user';
import AuthLog from './authlog.vue';
import AuthLock from './authlock.vue';
@@ -331,13 +359,15 @@ export default {
data() {
return {
form: {},
userDialog: false,
userDialogMode: 'add',
search: {},
sensitiveManager: null,
roleBox: false,
roleLinked: false,
roleFormMode: false,
grantUserId: '',
excelBox: false,
platformBox: false,
passwordBox: false,
initFlag: true,
auditMode: false,
@@ -357,11 +387,12 @@ export default {
roleTreeObj: [],
treeDeptId: '',
treeData: [],
permissionDeptTree: [],
postList: [],
customerList: [],
treeOption: treeOption,
userOption: userOption(this),
platformFormOption: platformFormOption,
data: [],
platformForm: {},
passwordForm: {},
passwordRules: {
password: [
@@ -373,16 +404,17 @@ export default {
{ validator: this.validatePassword2, trigger: 'blur' },
],
},
userRules: {
account: [{ required: true, message: '请输入账号名', trigger: 'blur' }],
realName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
deptId: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
},
excelForm: {},
excelOption: excelOption,
};
},
watch: {
'form.tenantId'() {
if (this.form.tenantId !== '' && this.initFlag) {
this.initData(this.form.tenantId);
}
},
'excelForm.isCovered'() {
if (this.excelForm.isCovered !== '') {
const column = this.findColumn(this.excelOption.column, 'excelFile');
@@ -407,14 +439,20 @@ export default {
});
return ids.join(',');
},
formRoleList() {
const selected = new Set(func.split(this.form.roleId || '').map(String));
const roles = [];
const walk = nodes =>
(nodes || []).forEach(node => {
if (selected.has(String(node.id))) roles.push(node);
walk(node.children);
});
walk(this.roleGrantList);
return roles;
},
},
mounted() {
// 非租户模式默认加载管理组数据
if (!website.tenantMode) {
this.initData(website.tenantId);
} else {
this.initData();
}
this.initData(this.userInfo.tenantId || this.website.tenantId);
},
created() {
// 创建脱敏工具实例
@@ -437,8 +475,6 @@ export default {
this.$refs.authLog.openUserAuthLog(row.id, row.name);
} else if (command === 'lockUser') {
this.$refs.authLock.openLockUser(row.id, row.account);
} else if (command === 'platformConfig') {
this.handlePlatformForRow(row);
} else if (command === 'resetPassword') {
this.handleResetForRow(row);
} else if (command === 'setPassword') {
@@ -488,14 +524,17 @@ export default {
},
initData(tenantId) {
getRoleTree(tenantId).then(res => {
this.roleGrantList = res.data.data;
const column = this.findColumn(this.userOption.group, 'roleId');
column.dicData = res.data.data;
});
getDeptTree(tenantId).then(res => {
this.permissionDeptTree = res.data.data;
const column = this.findColumn(this.userOption.group, 'deptId');
column.dicData = res.data.data;
});
getPostList(tenantId).then(res => {
this.postList = res.data.data;
const column = this.findColumn(this.userOption.group, 'postId');
column.dicData = res.data.data;
});
@@ -503,9 +542,18 @@ export default {
const column = this.findColumn(this.userOption.group, 'leaderId');
column.dicData = res.data.data;
});
getCustomerOptions().then(res => {
this.customerList = res.data.data || [];
});
},
submitRole() {
const roleList = this.$refs.treeRole.getCheckedKeys().join(',');
if (this.roleFormMode) {
this.form.roleId = roleList;
this.roleBox = false;
this.roleFormMode = false;
return;
}
grant(this.grantUserId, roleList).then(() => {
this.roleBox = false;
this.$message({
@@ -515,6 +563,98 @@ export default {
this.onLoad(this.page);
});
},
handleAddRole() {
this.roleFormMode = true;
this.roleTreeObj = func.split(this.form.roleId || '');
this.roleLinked = false;
getRoleTree(this.userInfo.tenantId).then(res => {
this.roleGrantList = res.data.data;
this.roleBox = true;
this.$nextTick(() => this.applyCheckedKeys('treeRole', this.roleTreeObj));
});
},
removeFormRole(roleId) {
this.form.roleId = func
.split(this.form.roleId || '')
.filter(id => String(id) !== String(roleId));
},
handleDataScopeCheck(_, checked) {
this.form.dataScopeDeptIds = checked.checkedKeys;
},
handleCustomerSelection(rows) {
this.form.customerIds = rows.map(row => row.id);
},
openUserDialog(mode, row = {}) {
this.userDialogMode = mode;
if (mode === 'add') {
this.form = {
account: '',
realName: '',
phone: '',
email: '',
sex: 3,
postId: '',
deptId: [],
code: '',
personCategory: 1,
dataScopeRange: 1,
dataLevelRange: 1,
remark: '',
roleId: '',
dataScopeDeptIds: [],
customerIds: [],
includeNewCustomer: 0,
};
this.userDialog = true;
this.$nextTick(() => this.$refs.userFormRef && this.$refs.userFormRef.clearValidate());
return;
}
getUser(row.id).then(res => {
const user = res.data.data;
this.form = {
...user,
deptId: func.split(user.deptId || ''),
postId: func.split(user.postId || '')[0] || '',
roleId: func.join(user.roleId || ''),
dataScopeDeptIds: user.dataScopeDeptIds || [],
customerIds: user.customerIds || [],
includeNewCustomer: user.includeNewCustomer || 0,
personCategory: user.personCategory || 1,
dataScopeRange: user.dataScopeRange || 1,
dataLevelRange: user.dataLevelRange || 1,
};
this.sensitiveManager.saveInitialData(this.form);
this.userDialog = true;
this.$nextTick(() => {
this.$refs.userFormRef && this.$refs.userFormRef.clearValidate();
(this.form.customerIds || []).forEach(id => {
const customer = this.customerList.find(item => String(item.id) === String(id));
if (customer) this.$refs.customerTable && this.$refs.customerTable.toggleRowSelection(customer, true);
});
});
});
},
submitUserForm() {
this.$refs.userFormRef.validate(valid => {
if (!valid) return;
const row = {
...this.form,
deptId: func.join(this.form.deptId),
postId: func.join(this.form.postId),
roleId: func.join(this.form.roleId),
leaderId: func.join(this.form.leaderId),
};
const request = this.userDialogMode === 'add' ? add(row) : update(this.sensitiveManager.getSubmitData(row));
request.then(() => {
this.userDialog = false;
this.onLoad(this.page);
this.$message.success('操作成功!');
});
});
},
resetDialogPassword() {
this.handleResetForRow(this.form);
},
rowSave(row, done, loading) {
row.deptId = func.join(row.deptId);
row.roleId = func.join(row.roleId);
@@ -779,23 +919,6 @@ export default {
if (!tree) return;
tree.setCheckedKeys(keys);
},
// ========== 平台配置(行级别) ==========
handlePlatformForRow(row) {
getUserPlatform(row.id).then(res => {
this.platformForm = { ...res.data.data, account: row.account };
this.platformBox = true;
});
},
submitPlatformConfig() {
updatePlatform(
this.platformForm.id,
this.platformForm.userType,
this.platformForm.userExt
).then(() => {
this.platformBox = false;
this.$message({ type: 'success', message: '操作成功!' });
});
},
handlePasswordForRow(row) {
this.passwordForm = {
userId: row.id,
@@ -902,12 +1025,11 @@ export default {
},
onLoad(page, params = {}) {
this.loading = true;
getList(
page.currentPage,
page.pageSize,
Object.assign(params, this.query, { status: this.auditMode ? 0 : 1 }),
this.treeDeptId
).then(res => {
const queryParams = { ...this.query, ...params };
if (this.auditMode) {
queryParams.status = 0;
}
getList(page.currentPage, page.pageSize, queryParams, this.treeDeptId).then(res => {
const data = res.data.data;
this.page.total = data.total;
this.data = data.records;
@@ -931,4 +1053,62 @@ export default {
.box .el-scrollbar__wrap {
overflow: scroll;
}
.user-permission-tabs {
width: 100%;
margin-top: 8px;
}
.user-edit-dialog .dialog-title {
display: inline-flex;
align-items: center;
font-size: 16px;
font-weight: 600;
}
.user-edit-dialog .dialog-title::before {
width: 4px;
height: 18px;
margin-right: 8px;
background: #409eff;
content: '';
}
.user-edit-dialog .el-form-item {
margin-bottom: 16px;
}
.user-edit-dialog .el-form-item__label {
white-space: nowrap;
}
.user-edit-dialog .el-radio-group {
white-space: nowrap;
}
.full-width {
width: 100%;
}
.user-permission-tabs .el-tabs__header {
margin-bottom: 12px;
}
.permission-toolbar {
display: flex;
justify-content: flex-end;
margin-bottom: 12px;
}
.customer-toolbar {
justify-content: flex-start;
}
.avue-dialog .el-form-item__label {
white-space: nowrap;
}
.avue-dialog .el-radio-group {
white-space: nowrap;
}
</style>

View File

@@ -0,0 +1,90 @@
<template>
<el-dialog :model-value="modelValue" title="船舶综合台账" width="96%" top="3vh" append-to-body destroy-on-close @update:model-value="$emit('update:modelValue', $event)">
<div v-loading="detailLoading" class="ship-ledger">
<section v-for="section in sections" :key="section.title" class="ship-ledger__section">
<div class="dialog-section-title">{{ section.title }}</div>
<el-descriptions :column="3" border>
<el-descriptions-item v-for="item in section.items" :key="item.label" :label="item.label">
{{ display(item) }}
</el-descriptions-item>
</el-descriptions>
</section>
<section class="ship-ledger__section">
<div class="dialog-section-title">证书附件</div>
<div class="ship-ledger__images">
<div v-for="item in imageFields" :key="item.prop" class="ship-ledger__image-item">
<span>{{ item.label }}</span>
<el-image v-if="shipDetail[item.prop]" :src="shipDetail[item.prop]" :preview-src-list="imageUrls" preview-teleported fit="cover" />
<el-empty v-else :image-size="36" description="暂无附件" />
</div>
</div>
</section>
<el-tabs v-model="activeTab" @tab-change="loadRecords">
<el-tab-pane v-for="tab in tabs" :key="tab.name" :label="tab.label" :name="tab.name" />
</el-tabs>
<el-table v-loading="tableLoading" :data="records" border stripe>
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column v-for="column in activeColumns" :key="column.prop" :prop="column.prop" :label="column.label" :min-width="column.width || 140" show-overflow-tooltip />
</el-table>
<div class="ship-ledger__pagination"><el-pagination v-model:current-page="page.current" v-model:page-size="page.size" :page-sizes="[10, 20, 50]" :total="page.total" layout="total, sizes, prev, pager, next" @size-change="handlePageChange" @current-change="loadRecords" /></div>
</div>
</el-dialog>
</template>
<script>
import { getDetail } from '@/api/transportCapacity/transport-ship';
import { getList as insurance } from '@/api/vehicle/insurance-record';
import { getList as violation } from '@/api/vehicle/violation-record';
import { getList as maintenance } from '@/api/vehicle/maintenance-record';
import { getList as maintenancePlan } from '@/api/vehicle/maintenance-plan';
import { getList as accident } from '@/api/vehicle/accident-record';
import { getList as annualInspection } from '@/api/vehicle/annual-inspection-record';
import { getList as change } from '@/api/vehicle/transport-change-record';
import { getList as oilElectric } from '@/api/vehicle/oil-electric-record';
import { getList as otherExpense } from '@/api/vehicle/other-expense-record';
const columns = (...items) => items.map(([label, prop]) => ({ label, prop }));
const tabConfigs = [
['insurance', '保险记录', insurance, columns(['船号', 'vehicleNo'], ['保险类型', 'insuranceType'], ['开始日期', 'startDate'], ['结束日期', 'endDate'], ['保费', 'premium'], ['保单号', 'policyNo'])],
['violation', '违章记录', violation, columns(['船号', 'vehicleNo'], ['驾驶人/船长', 'driverName'], ['类型', 'violationType'], ['事项', 'violationItem'], ['违章时间', 'violationTime'], ['罚款金额', 'fineAmount'])],
['maintenance', '维修记录', maintenance, columns(['船号', 'vehicleNo'], ['维修人', 'maintainer'], ['维修时间', 'maintenanceTime'], ['维修位置', 'location'], ['费用', 'cost'])],
['maintenancePlan', '保养记录', maintenancePlan, columns(['船号', 'vehicleNo'], ['保养人', 'maintainer'], ['保养时间', 'maintenanceTime'], ['保养项目', 'maintenanceItem'], ['费用', 'cost'])],
['accident', '事故记录', accident, columns(['船号', 'vehicleNo'], ['事故时间', 'accidentTime'], ['事故地点', 'accidentLocation'], ['事故类型', 'accidentType'], ['损失金额', 'lossAmount'])],
['annualInspection', '年检记录', annualInspection, columns(['船号', 'vehicleNo'], ['年检日期', 'inspectionDate'], ['年检有效期至', 'inspectionEndDate'], ['年检机构', 'inspectionOrganization'], ['费用', 'cost'])],
['change', '变更记录', change, columns(['船号', 'vehicleNo'], ['变更类型', 'changeType'], ['变更时间', 'changeTime'], ['变更前内容', 'beforeChange'], ['变更后内容', 'afterChange'])],
['equipment', '设备台账', null, columns(['设备编码', 'equipmentCode'], ['设备名称', 'equipmentName'], ['设备类型', 'equipmentType'], ['安装日期', 'installDate'], ['状态', 'status'])],
['oilElectric', '油电台账', oilElectric, columns(['船号', 'vehicleNo'], ['加油/充电日期', 'recordDate'], ['类型', 'energyType'], ['数量', 'quantity'], ['金额', 'amount'])],
['otherExpense', '其他费用记录', otherExpense, columns(['船号', 'vehicleNo'], ['费用日期', 'expenseDate'], ['费用类型', 'expenseType'], ['金额', 'amount'], ['备注', 'remark'])],
].map(([name, label, request, cols]) => ({ name, label, request, columns: cols }));
export default {
props: { modelValue: Boolean, ship: { type: Object, default: () => ({}) } }, emits: ['update:modelValue'],
data() { return { detailLoading: false, tableLoading: false, shipDetail: {}, activeTab: 'insurance', records: [], page: { current: 1, size: 10, total: 0 } }; },
computed: {
tabs() { return tabConfigs; }, activeConfig() { return tabConfigs.find(item => item.name === this.activeTab) || tabConfigs[0]; }, activeColumns() { return this.activeConfig.columns; },
imageFields() { return [['船舶所有权证书', 'ownershipCertImage'], ['内河船舶安全与环保证书', 'safetyCertImage'], ['船舶国籍证书', 'nationalityCertImage'], ['最低安全配员证书', 'safeManningCertImage'], ['船舶营业运输证', 'businessTransportCertImage'], ['光船租赁登记证书', 'leaseContractImage']].map(([label, prop]) => ({ label, prop })); },
imageUrls() { return this.imageFields.map(item => this.shipDetail[item.prop]).filter(Boolean); },
sections() { const s = this.shipDetail; return [
{ title: '船舶基本信息', items: [['船舶所属组织', 'organizationName'], ['船名', 'shipName'], ['安放龙骨日期/建造完工日期', 'constructionDate'], ['总长(m)', 'totalLength'], ['船宽(m)', 'shipWidth'], ['型深(m)', 'moldedDepth'], ['最大船高(m)', 'maxShipHeight'], ['空载吃水(t)', 'lightDraft'], ['满载吃水(t)', 'fullLoadDraft'], ['航区', 'navigationArea']] },
{ title: '船舶证书信息', items: [['登记号码', 'ownershipRegistrationNo'], ['初次登记号码', 'initialRegistrationNo'], ['船舶所有人', 'shipOwner'], ['取得所有权日期', 'ownershipAcquisitionDate'], ['船舶识别号', 'shipIdentifierNo'], ['总吨', 'grossTonnage'], ['净吨', 'netTonnage'], ['船检登记号', 'shipInspectionNo'], ['船舶类型', 'shipType'], ['国籍证书有效期', 'nationalityCertDate'], ['最低安全配员证书有效期', 'safeManningCertDate'], ['起租日期', 'leaseStartDate'], ['终止日期', 'leaseEndDate'], ['船舶承租人', 'shipLessee'], ['营业运输证证书编号', 'businessTransportCertNo'], ['营业运输证发证日期', 'businessTransportCertIssueDate'], ['营业运输证有效期至', 'businessTransportCertEndDate'], ['船舶经营人', 'shipOperator']] },
].map(section => ({ ...section, items: section.items.map(([label, prop]) => ({ label, prop, value: prop === 'constructionDate' ? `${s.keelLayingDate || '-'} / ${s.buildCompletionDate || '-'}` : prop === 'nationalityCertDate' ? `${s.nationalityCertStartDate || '-'}${s.nationalityCertEndDate || '-'}` : prop === 'safeManningCertDate' ? `${s.safeManningCertStartDate || '-'}${s.safeManningCertEndDate || '-'}` : s[prop] })) })); },
},
watch: { modelValue(value) { if (value) this.initialize(); } },
methods: {
display(item) { return item.value === null || item.value === undefined || item.value === '' ? '-' : item.value; },
async initialize() { this.activeTab = 'insurance'; this.page.current = 1; this.shipDetail = { ...this.ship }; if (this.ship.id) { this.detailLoading = true; try { const res = await getDetail(this.ship.id); this.shipDetail = { ...this.ship, ...(res.data.data || {}) }; } finally { this.detailLoading = false; } } this.loadRecords(); },
async loadRecords() { const { request } = this.activeConfig; if (!request) { this.records = []; this.page.total = 0; return; } this.tableLoading = true; try { const res = await request(this.page.current, this.page.size, { vehicleNo: this.shipDetail.shipName || this.ship.shipName, vehicleType: '船舶' }); const data = res.data.data || {}; this.records = data.records || []; this.page.total = data.total || 0; } finally { this.tableLoading = false; } },
handlePageChange() { this.page.current = 1; this.loadRecords(); },
},
};
</script>
<style lang="scss" scoped>
.ship-ledger__section { margin-bottom: 16px; }
.ship-ledger__images { display: flex; gap: 16px; overflow-x: auto; padding: 12px 0; }
.ship-ledger__image-item { width: 150px; flex: 0 0 150px; text-align: center; color: #606266; }
.ship-ledger__image-item > span { display: block; margin-bottom: 8px; }
.ship-ledger__image-item :deep(.el-image) { width: 150px; height: 110px; border: 1px solid #eff1f7; }
.ship-ledger__image-item :deep(.el-empty) { height: 110px; border: 1px solid #eff1f7; }
.ship-ledger__pagination { display: flex; justify-content: flex-end; margin-top: 16px; }
</style>

View File

@@ -0,0 +1,141 @@
<template>
<el-dialog
:model-value="modelValue"
title="车辆综合台账"
width="96%"
top="3vh"
append-to-body
destroy-on-close
@update:model-value="$emit('update:modelValue', $event)"
>
<div v-loading="detailLoading" class="vehicle-ledger">
<section class="vehicle-ledger__overview">
<div class="vehicle-ledger__title">
<span>{{ vehicleDetail.plateNo || vehicle.plateNo }}</span>
<el-tag :type="plateColorType">{{ vehicleDetail.plateColor || '-' }}</el-tag>
<el-tag type="primary">{{ vehicleDetail.businessRelation || '-' }}</el-tag>
</div>
<el-descriptions :column="4" border>
<el-descriptions-item label="所属组织">{{ vehicleDetail.organizationName || '-' }}</el-descriptions-item>
<el-descriptions-item label="车辆类型">{{ vehicleDetail.vehicleType || '-' }}</el-descriptions-item>
<el-descriptions-item label="能源类型">{{ vehicleDetail.energyType || '-' }}</el-descriptions-item>
<el-descriptions-item label="外廓尺寸(mm)">{{ dimensions }}</el-descriptions-item>
<el-descriptions-item label="核定载质量(KG)">{{ vehicleDetail.approvedLoadKg ?? '-' }}</el-descriptions-item>
<el-descriptions-item label="准牵引总质量(KG)">{{ vehicleDetail.tractionMassKg ?? '-' }}</el-descriptions-item>
<el-descriptions-item label="行驶证档案编号">{{ vehicleDetail.drivingLicenseNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="道路运输证号">{{ vehicleDetail.roadTransportCertNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="行驶证有效期">{{ vehicleDetail.drivingLicenseEndDate || '-' }}</el-descriptions-item>
<el-descriptions-item label="道路运输证有效期">{{ vehicleDetail.roadTransportCertEndDate || '-' }}</el-descriptions-item>
<el-descriptions-item label="道路运输证年审有效期">{{ vehicleDetail.annualReviewEndDate || '-' }}</el-descriptions-item>
</el-descriptions>
<div class="vehicle-ledger__images">
<div v-for="item in imageFields" :key="item.prop" class="vehicle-ledger__image-item">
<span>{{ item.label }}</span>
<el-image
v-if="vehicleDetail[item.prop]"
:src="vehicleDetail[item.prop]"
:preview-src-list="imageUrls"
preview-teleported
fit="cover"
/>
<el-empty v-else :image-size="36" description="暂无图片" />
</div>
</div>
</section>
<el-tabs v-model="activeTab" class="vehicle-ledger__tabs" @tab-change="loadRecords">
<el-tab-pane v-for="tab in tabs" :key="tab.name" :label="tab.label" :name="tab.name" />
</el-tabs>
<el-table v-loading="tableLoading" :data="records" border stripe>
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column
v-for="column in activeColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.width || 140"
show-overflow-tooltip
/>
</el-table>
<div class="vehicle-ledger__pagination">
<el-pagination
v-model:current-page="page.current"
v-model:page-size="page.size"
:page-sizes="[10, 20, 50]"
:total="page.total"
layout="total, sizes, prev, pager, next"
@size-change="handlePageChange"
@current-change="loadRecords"
/>
</div>
</div>
</el-dialog>
</template>
<script>
import { getDetail } from '@/api/transportCapacity/transport-vehicle';
import { getList as getInsuranceList } from '@/api/vehicle/insurance-record';
import { getList as getViolationList } from '@/api/vehicle/violation-record';
import { getList as getMaintenanceList } from '@/api/vehicle/maintenance-record';
import { getList as getMaintenancePlanList } from '@/api/vehicle/maintenance-plan';
import { getList as getTireList } from '@/api/vehicle/tire-replacement-record';
import { getList as getAccidentList } from '@/api/vehicle/accident-record';
import { getList as getAnnualInspectionList } from '@/api/vehicle/annual-inspection-record';
import { getList as getMileageList } from '@/api/vehicle/mileage-record';
import { getList as getChangeList } from '@/api/vehicle/transport-change-record';
import { getList as getOilElectricList } from '@/api/vehicle/oil-electric-record';
import { getList as getEtcList } from '@/api/vehicle/etc-record';
import { getList as getOtherExpenseList } from '@/api/vehicle/other-expense-record';
const cols = (...items) => items.map(([label, prop, width]) => ({ label, prop, width }));
const tabConfigs = [
['insurance', '保险记录', getInsuranceList, cols(['车牌号', 'vehicleNo'], ['保险类型', 'insuranceType'], ['开始日期', 'startDate'], ['结束日期', 'endDate'], ['保费', 'premium'], ['保单号', 'policyNo'])],
['violation', '违章记录', getViolationList, cols(['车牌号', 'vehicleNo'], ['驾驶人', 'driverName'], ['类型', 'violationType'], ['事项', 'violationItem'], ['违章时间', 'violationTime'], ['罚款金额', 'fineAmount'])],
['maintenance', '维修记录', getMaintenanceList, cols(['车牌号', 'vehicleNo'], ['维修人', 'maintainer'], ['维修时间', 'maintenanceTime'], ['维修位置', 'location'], ['费用', 'cost'], ['维修单位', 'company'])],
['maintenancePlan', '保养记录', getMaintenancePlanList, cols(['车牌号', 'vehicleNo'], ['保养人', 'maintainer'], ['保养时间', 'maintenanceTime'], ['保养项目', 'maintenanceItem'], ['费用', 'cost'], ['下次保养时间', 'nextMaintenanceTime'])],
['tire', '换胎记录', getTireList, cols(['车牌号', 'vehicleNo'], ['更换时间', 'replacementTime'], ['轮胎品牌', 'tireBrand'], ['轮胎规格', 'tireSpec'], ['费用', 'cost'])],
['accident', '事故记录', getAccidentList, cols(['车牌号', 'vehicleNo'], ['事故时间', 'accidentTime'], ['事故地点', 'accidentLocation'], ['事故类型', 'accidentType'], ['损失金额', 'lossAmount'])],
['annualInspection', '年检记录', getAnnualInspectionList, cols(['车牌号', 'vehicleNo'], ['年检日期', 'inspectionDate'], ['年检有效期至', 'inspectionEndDate'], ['年检机构', 'inspectionOrganization'], ['费用', 'cost'])],
['mileage', '里程记录', getMileageList, cols(['车牌号', 'vehicleNo'], ['记录时间', 'recordTime'], ['里程数', 'mileage'], ['里程单位', 'mileageUnit'], ['备注', 'remark'])],
['change', '变更记录', getChangeList, cols(['车牌号', 'vehicleNo'], ['变更类型', 'changeType'], ['变更时间', 'changeTime'], ['变更前内容', 'beforeChange'], ['变更后内容', 'afterChange'])],
['equipment', '设备台账', null, cols(['设备编码', 'equipmentCode'], ['设备名称', 'equipmentName'], ['设备类型', 'equipmentType'], ['安装日期', 'installDate'], ['状态', 'status'])],
['oilElectric', '油电台账', getOilElectricList, cols(['车牌号', 'vehicleNo'], ['加油/充电日期', 'recordDate'], ['类型', 'energyType'], ['数量', 'quantity'], ['金额', 'amount'])],
['etc', 'ETC记录', getEtcList, cols(['车牌号', 'vehicleNo'], ['交易日期', 'transactionDate'], ['交易金额', 'amount'], ['交易地点', 'location'], ['ETC卡号', 'etcCardNo'])],
['otherExpense', '其他费用记录', getOtherExpenseList, cols(['车牌号', 'vehicleNo'], ['费用日期', 'expenseDate'], ['费用类型', 'expenseType'], ['金额', 'amount'], ['备注', 'remark'])],
].map(([name, label, request, columns]) => ({ name, label, request, columns }));
export default {
props: { modelValue: Boolean, vehicle: { type: Object, default: () => ({}) } },
emits: ['update:modelValue'],
data() {
return { detailLoading: false, tableLoading: false, vehicleDetail: {}, activeTab: 'insurance', records: [], page: { current: 1, size: 10, total: 0 } };
},
computed: {
tabs() { return tabConfigs; },
activeConfig() { return tabConfigs.find(item => item.name === this.activeTab) || tabConfigs[0]; },
activeColumns() { return this.activeConfig.columns; },
dimensions() { const v = this.vehicleDetail; return [v.outerLength, v.outerWidth, v.outerHeight].filter(item => item !== null && item !== undefined && item !== '').join('×') || '-'; },
plateColorType() { return { 黄牌: 'warning', 蓝牌: 'primary', 绿牌: 'success' }[this.vehicleDetail.plateColor] || 'info'; },
imageFields() { return [['行驶证主页面', 'drivingLicenseImage'], ['行驶证主页反页', 'drivingLicenseMainBack'], ['行驶证副页正页', 'drivingLicenseViceFront'], ['行驶证副页反页', 'drivingLicenseViceBack'], ['道路运输证', 'roadTransportCertImage']].map(([label, prop]) => ({ label, prop })); },
imageUrls() { return this.imageFields.map(item => this.vehicleDetail[item.prop]).filter(Boolean); },
},
watch: { modelValue(value) { if (value) this.initialize(); } },
methods: {
async initialize() { this.activeTab = 'insurance'; this.page.current = 1; this.vehicleDetail = { ...this.vehicle }; if (this.vehicle.id) { this.detailLoading = true; try { const res = await getDetail(this.vehicle.id); this.vehicleDetail = { ...this.vehicle, ...(res.data.data || {}) }; } finally { this.detailLoading = false; } } this.loadRecords(); },
async loadRecords() { const { request } = this.activeConfig; if (!request) { this.records = []; this.page.total = 0; return; } this.tableLoading = true; try { const res = await request(this.page.current, this.page.size, { vehicleNo: this.vehicleDetail.plateNo || this.vehicle.plateNo, vehicleType: '车辆' }); const data = res.data.data || {}; this.records = data.records || []; this.page.total = data.total || 0; } finally { this.tableLoading = false; } },
handlePageChange() { this.page.current = 1; this.loadRecords(); },
},
};
</script>
<style lang="scss" scoped>
.vehicle-ledger__overview { padding: 16px; border: 1px solid #eff1f7; }
.vehicle-ledger__title { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; font-size: 26px; font-weight: 600; }
.vehicle-ledger__images { display: flex; gap: 16px; margin-top: 16px; overflow-x: auto; }
.vehicle-ledger__image-item { width: 150px; flex: 0 0 150px; color: #606266; text-align: center; }
.vehicle-ledger__image-item > span { display: block; margin-bottom: 8px; }
.vehicle-ledger__image-item :deep(.el-image) { width: 150px; height: 110px; border: 1px solid #eff1f7; }
.vehicle-ledger__image-item :deep(.el-empty) { height: 110px; border: 1px solid #eff1f7; }
.vehicle-ledger__tabs { margin-top: 16px; }
.vehicle-ledger__pagination { display: flex; justify-content: flex-end; margin-top: 16px; }
</style>

View File

@@ -41,23 +41,26 @@
>批量删除
</el-button>
</template>
<template #menu-right>
<el-radio-group
v-model="query.expireStatus"
class="driver-stat"
@change="handleExpireChange"
>
<el-radio-button label="">全部{{ expiryStat.total }}</el-radio-button>
<el-radio-button label="within30"
>30天内到期{{ expiryStat.within30 }}</el-radio-button
<template #search-menu>
<div class="driver-page__expiry-tags">
<el-check-tag
v-for="item in expiryTagOptions"
:key="item.value"
:checked="query.expireStatus === item.value"
@change="handleExpireChange(item.value)"
>
<el-radio-button label="expired">已到期{{ expiryStat.expired }}</el-radio-button>
</el-radio-group>
{{ item.label }}{{ expiryStat[item.statKey] || 0 }}
</el-check-tag>
</div>
</template>
<template #driverName="{ row }">
<el-button type="primary" link @click="openDriver(row, true)">
{{ row.driverName }}
</el-button>
<el-link type="primary" @click="openDriver(row, true)">{{ row.driverName }}</el-link>
</template>
<template #idCardNo="{ row }">
<span>{{ row.idCardNo || '-' }}</span>
</template>
<template #organizationName="{ row }">
{{ String(row.organizationName || '').replace(/^[\s\u3000]+/, '') }}
</template>
<template #status="{ row }">
<el-tag :type="row.status === 1 ? 'primary' : 'info'">
@@ -124,7 +127,7 @@
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="司机姓名" prop="driverName">
<el-input v-model="driverForm.driverName" maxlength="20" show-word-limit />
<el-input v-model="driverForm.driverName" maxlength="10" show-word-limit />
</el-form-item>
</el-col>
<el-col :span="8">
@@ -345,8 +348,6 @@
v-model="driverForm.qualificationType"
clearable
filterable
allow-create
default-first-option
>
<el-option
v-for="item in qualificationTypeOptions"
@@ -418,32 +419,35 @@
<section-card title="司机联系信息">
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="司机类型" prop="driverType">
<el-select v-model="driverForm.driverType" filterable>
<el-option label="自有" value="自有" />
<el-option label="外协" value="外协" />
<el-option label="承运管理员" value="承运管理员" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="手机号" prop="mobile">
<el-input v-model="driverForm.mobile" maxlength="20" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="紧急联系人姓名" prop="emergencyContactName">
<el-input v-model="driverForm.emergencyContactName" maxlength="20" />
<el-input
v-model="driverForm.emergencyContactName"
maxlength="20"
@input="driverForm.emergencyContactName = removeDigits($event)"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="紧急联系人手机号" prop="emergencyContactMobile">
<el-input v-model="driverForm.emergencyContactMobile" maxlength="20" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="与联系人关系" prop="contactRelation">
<el-select
v-model="driverForm.contactRelation"
@@ -461,7 +465,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="所属组织" prop="organizationName">
<el-select v-model="driverForm.organizationName" clearable filterable>
<el-option
@@ -502,6 +506,7 @@ import {
recognitionTransportCertificates,
} from '@/api/transportCapacity/driver';
import { getDeptTree } from '@/api/system/dept';
import { getDictionary } from '@/api/system/dictbiz';
import { getLazyTree } from '@/api/base/region';
import { exportBlob } from '@/api/common';
import { getToken } from '@/utils/auth';
@@ -639,18 +644,8 @@ export default {
],
educationOptions: ['初中', '高中', '中专', '大专', '本科', '硕士及以上'],
drivingTypeOptions: ['A1', 'A2', 'A3', 'B1', 'B2', 'C1', 'C2'],
qualificationTypeOptions: [
'放射品押运员',
'道路运输从业资格证',
'危险货物运输从业资格证',
'A1',
'A2',
'B1',
'B2',
'C1',
'C2',
],
relationOptions: ['父母', '配偶', '子女', '兄弟姐妹', '朋友', '同事'],
qualificationTypeOptions: [],
relationOptions: ['父母', '配偶', '子女', '兄弟姐妹', '朋友', '同事', '其他'],
organizationOptions: [],
regionOptions: [],
formRules: {
@@ -686,6 +681,7 @@ export default {
mobile: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
emergencyContactName: [
{ required: true, message: '请输入紧急联系人姓名', trigger: 'blur' },
{ validator: this.validateEmergencyContactName, trigger: 'blur' },
],
emergencyContactMobile: [
{ required: true, message: '请输入紧急联系人手机号', trigger: 'blur' },
@@ -726,10 +722,18 @@ export default {
emitPath: true,
};
},
expiryTagOptions() {
return [
{ label: '全部', value: '', statKey: 'total' },
{ label: '30天内到期', value: 'within30', statKey: 'within30' },
{ label: '已到期', value: 'expired', statKey: 'expired' },
];
},
},
created() {
this.initDeptTree();
this.initRegionOptions();
this.initQualificationTypeOptions();
},
methods: {
hasPermission(code) {
@@ -740,15 +744,27 @@ export default {
this.organizationOptions = this.formatDeptOptions(res.data.data || []);
const column = this.findColumn(this.option.column, 'organizationName');
column.dicData = this.organizationOptions;
if (this.driverBox && !this.driverForm.id && !this.driverForm.organizationName) {
this.driverForm.organizationName = this.getCurrentOrganizationName();
}
});
},
initQualificationTypeOptions() {
getDictionary({ code: 'type_of_cert' }).then(res => {
this.qualificationTypeOptions = (res.data.data || [])
.map(item => item.dictValue)
.filter(Boolean);
});
},
formatDeptOptions(tree = [], level = 0) {
const result = [];
tree.forEach(item => {
const label = item.title || item.deptName || item.name;
const rawLabel = item.title || item.deptName || item.name;
result.push({
label: `${' '.repeat(level)}${label}`,
value: label,
label: `${' '.repeat(level)}${rawLabel}`,
value: rawLabel,
rawLabel,
id: item.id || item.value,
});
if (item.children && item.children.length) {
result.push(...this.formatDeptOptions(item.children, level + 1));
@@ -756,6 +772,13 @@ export default {
});
return result;
},
getCurrentOrganizationName() {
const currentDeptId = this.userInfo.deptId || this.userInfo.dept_id;
const currentDept = this.organizationOptions.find(
item => String(item.id) === String(currentDeptId)
);
return currentDept?.rawLabel || this.userInfo.deptName || this.userInfo.dept_name || '';
},
initRegionOptions() {
getLazyTree().then(res => {
const regions = this.buildRegionTree(res.data.data || []);
@@ -885,10 +908,21 @@ export default {
}
callback();
},
validateEmergencyContactName(rule, value, callback) {
if (/\d/.test(value || '')) {
callback(new Error('紧急联系人姓名不能包含数字'));
return;
}
callback();
},
removeDigits(value = '') {
return String(value).replace(/\d/g, '');
},
openDriver(row, readonly = false) {
this.readonly = readonly;
if (!row?.id) {
this.driverForm = emptyForm();
this.driverForm.organizationName = this.getCurrentOrganizationName();
this.driverBox = true;
return;
}
@@ -921,9 +955,9 @@ export default {
this.setImage(prop, url);
this.recognizeIdCard(url);
},
handleDrivingLicenseUploadSuccess(prop, url, file = {}) {
handleDrivingLicenseUploadSuccess(prop, url) {
this.setImage(prop, url);
this.drivingLicenseUploads[prop] = this.getUploadRecognitionKey(file, url);
this.drivingLicenseUploads[prop] = url;
this.recognizeDrivingLicense();
},
recognizeIdCard(url = '') {
@@ -976,10 +1010,10 @@ export default {
buildDrivingLicenseRecognitionCertificates() {
return ['drivingLicenseFront', 'drivingLicenseBack']
.map(prop => {
const objectKey = this.drivingLicenseUploads[prop] || this.driverForm[prop];
if (!objectKey) return null;
const url = this.drivingLicenseUploads[prop] || this.driverForm[prop];
if (!url) return null;
return {
driverLicenseUrl: objectKey,
driverLicenseUrl: url,
};
})
.filter(Boolean);
@@ -1046,19 +1080,6 @@ export default {
});
});
},
getUploadRecognitionKey(file = {}, url = '') {
return (
file.objectKey ||
file.key ||
file.fileName ||
file.name ||
file.link ||
file.url ||
file.domain ||
url ||
''
);
},
applyIdCardRecognition(data = {}) {
const driverName = data.name || data.driverName || this.getOcrValue(data, ['name', '姓名']);
const idCardNo = String(
@@ -1206,7 +1227,8 @@ export default {
});
});
},
handleExpireChange() {
handleExpireChange(expireStatus) {
this.query.expireStatus = expireStatus;
this.page.currentPage = 1;
this.onLoad(this.page);
},
@@ -1322,8 +1344,19 @@ export default {
<style lang="scss" scoped>
.driver-page {
.driver-stat {
flex-shrink: 0;
&__expiry-tags {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
order: -1;
margin-right: auto;
min-width: 0;
:deep(.el-check-tag) {
margin: 0;
white-space: nowrap;
}
}
:deep(.el-table th .cell),
@@ -1407,12 +1440,4 @@ export default {
font-weight: 600;
}
@media (max-width: 1200px) {
.driver-page {
.driver-stat {
display: flex;
flex-wrap: wrap;
}
}
}
</style>

View File

@@ -44,20 +44,26 @@
批量删除
</el-button>
</template>
<template #menu-right>
<el-radio-group v-model="query.expireStatus" class="ship-stat" @change="handleExpireChange">
<el-radio-button label="">全部{{ expiryStat.total }}</el-radio-button>
<el-radio-button label="within30"
>30天内到期{{ expiryStat.within30 }}</el-radio-button
<template #expireStatus-search>
<div class="transport-ship-page__expiry-tags">
<el-check-tag
v-for="item in expiryTagOptions"
:key="item.value"
:checked="query.expireStatus === item.value"
@change="handleExpireChange(item.value)"
>
<el-radio-button label="expired">已到期{{ expiryStat.expired }}</el-radio-button>
</el-radio-group>
{{ item.label }}{{ expiryStat[item.statKey] || 0 }}
</el-check-tag>
</div>
</template>
<template #shipName="{ row }">
<el-button type="primary" link @click="openShip(row, true)">
{{ row.shipName }}
</el-button>
</template>
<template #organizationName="{ row }">
{{ String(row.organizationName || '').replace(/^[\s\u3000]+/, '') }}
</template>
<template #status="{ row }">
<el-tag :type="row.status === 1 ? 'primary' : 'info'">
{{ row.status === 1 ? '启用' : '停用' }}
@@ -69,14 +75,22 @@
</span>
</template>
<template #safeManningCertEndDate="{ row }">
<span :class="getDateClass(row, 'safeManningCertEndDate', 'safeManningCertLongTerm')">
<span
:class="
getCertificateExpiryClass(row, 'safeManningCertEndDate', 'safeManningCertLongTerm')
"
>
{{ formatEndDate(row.safeManningCertEndDate, row.safeManningCertLongTerm) }}
</span>
</template>
<template #businessTransportCertEndDate="{ row }">
<span
:class="
getDateClass(row, 'businessTransportCertEndDate', 'businessTransportCertLongTerm')
getCertificateExpiryClass(
row,
'businessTransportCertEndDate',
'businessTransportCertLongTerm'
)
"
>
{{ formatEndDate(row.businessTransportCertEndDate, row.businessTransportCertLongTerm) }}
@@ -94,7 +108,7 @@
<el-link
type="primary"
v-if="hasPermission('transport_ship_view')"
@click="openShip(row, true)"
@click="openShipLedger(row)"
>
综合台账
</el-link>
@@ -108,6 +122,8 @@
</template>
</avue-crud>
<ship-ledger v-model="shipLedgerBox" :ship="ledgerShip" />
<el-dialog
:title="dialogTitle"
append-to-body
@@ -146,7 +162,7 @@
</el-col>
<el-col :span="8">
<el-form-item label="船名" prop="shipName">
<el-input v-model="shipForm.shipName" maxlength="50" show-word-limit />
<el-input v-model="shipForm.shipName" maxlength="20" show-word-limit />
</el-form-item>
</el-col>
<el-col :span="8">
@@ -172,64 +188,64 @@
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="总长" prop="totalLength">
<el-input-number
<el-input
v-model="shipForm.totalLength"
:min="0"
:precision="2"
:controls="false"
/>
@input="normalizeNonNegativeDecimal('totalLength')"
>
<template #suffix>m</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="船宽" prop="shipWidth">
<el-input-number
<el-input
v-model="shipForm.shipWidth"
:min="0"
:precision="2"
:controls="false"
/>
@input="normalizeNonNegativeDecimal('shipWidth')"
>
<template #suffix>m</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="型深" prop="moldedDepth">
<el-input-number
<el-input
v-model="shipForm.moldedDepth"
:min="0"
:precision="2"
:controls="false"
/>
@input="normalizeNonNegativeDecimal('moldedDepth')"
>
<template #suffix>m</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="最大船高" prop="maxShipHeight">
<el-input-number
<el-input
v-model="shipForm.maxShipHeight"
:min="0"
:precision="2"
:controls="false"
/>
@input="normalizeNonNegativeDecimal('maxShipHeight')"
>
<template #suffix>m</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="空载吃水" prop="lightDraft">
<el-input-number
<el-input
v-model="shipForm.lightDraft"
:min="0"
:precision="2"
:controls="false"
/>
@input="normalizeNonNegativeDecimal('lightDraft')"
>
<template #suffix>t</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="满载吃水" prop="fullLoadDraft">
<el-input-number
<el-input
v-model="shipForm.fullLoadDraft"
:min="0"
:precision="2"
:controls="false"
/>
@input="normalizeNonNegativeDecimal('fullLoadDraft')"
>
<template #suffix>t</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
@@ -349,7 +365,12 @@
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="船舶类型" prop="shipType">
<el-input v-model="shipForm.shipType" maxlength="50" />
<el-select v-model="shipForm.shipType" placeholder="请选择船舶类型" clearable>
<el-option label="散货船" value="散货船" />
<el-option label="集装箱船" value="集装箱船" />
<el-option label="杂货船" value="杂货船" />
<el-option label="油船" value="油船" />
</el-select>
</el-form-item>
</el-col>
</el-row>
@@ -577,6 +598,7 @@ import { getToken } from '@/utils/auth';
import { getUploadHeaders } from '@/utils/upload';
import { downloadXls } from '@/utils/util';
import ImageUploadField from '@/components/image-upload-field/main.vue';
import ShipLedger from './components/ship-ledger.vue';
const emptyForm = () => ({
id: '',
@@ -626,7 +648,7 @@ const emptyForm = () => ({
});
export default {
components: { ImageUploadField, InfoFilled },
components: { ImageUploadField, InfoFilled, ShipLedger },
data() {
return {
option,
@@ -641,13 +663,18 @@ export default {
page: { pageSize: 10, currentPage: 1, total: 0 },
expiryStat: { total: 0, within30: 0, expired: 0 },
shipBox: false,
shipLedgerBox: false,
ledgerShip: {},
readonly: false,
shipForm: emptyForm(),
uploadHeaders: getUploadHeaders(),
organizationOptions: [],
formRules: {
organizationName: [{ required: true, message: '请选择船舶所属组织', trigger: 'change' }],
shipName: [{ required: true, message: '请输入船名', trigger: 'blur' }],
shipName: [
{ required: true, message: '请输入船名', trigger: 'blur' },
{ max: 20, message: '船名不能超过20个字符', trigger: 'blur' },
],
keelLayingDate: [{ validator: this.validateKeelCompletionDate, trigger: 'change' }],
shipIdentifierNo: [{ required: true, message: '请输入船舶识别号', trigger: 'blur' }],
totalLength: [{ validator: this.validateNonNegativeField, trigger: 'change' }],
@@ -663,7 +690,7 @@ export default {
grossTonnage: [{ required: true, message: '请输入总吨', trigger: 'change' }],
netTonnage: [{ required: true, message: '请输入净吨', trigger: 'change' }],
shipInspectionNo: [{ required: true, message: '请输入船检登记号', trigger: 'blur' }],
shipType: [{ required: true, message: '请输入船舶类型', trigger: 'blur' }],
shipType: [{ required: true, message: '请选择船舶类型', trigger: 'change' }],
nationalityCertStartDate: [
{ validator: this.validateNationalityCertDate, trigger: 'change' },
],
@@ -704,6 +731,13 @@ export default {
if (this.readonly) return '船舶综合台账';
return this.shipForm.id ? '修改船舶' : '新增船舶';
},
expiryTagOptions() {
return [
{ label: '全部', value: '', statKey: 'total' },
{ label: '30天内到期', value: 'within30', statKey: 'within30' },
{ label: '已到期', value: 'expired', statKey: 'expired' },
];
},
},
created() {
this.initDeptTree();
@@ -717,15 +751,20 @@ export default {
this.organizationOptions = this.flattenDeptOptions(res.data.data || []);
const column = this.findColumn(this.option.column, 'organizationName');
column.dicData = this.organizationOptions;
if (this.shipBox && !this.shipForm.id && !this.shipForm.organizationName) {
this.shipForm.organizationName = this.getCurrentOrganizationName();
}
});
},
flattenDeptOptions(tree = [], level = 0) {
const result = [];
tree.forEach(item => {
const label = item.title || item.deptName || item.name;
const rawLabel = item.title || item.deptName || item.name;
result.push({
label: `${' '.repeat(level)}${label}`,
value: label,
label: `${' '.repeat(level)}${rawLabel}`,
value: rawLabel,
rawLabel,
id: item.id || item.value,
});
if (item.children && item.children.length) {
result.push(...this.flattenDeptOptions(item.children, level + 1));
@@ -733,9 +772,22 @@ export default {
});
return result;
},
getCurrentOrganizationName() {
const currentDeptId = this.userInfo.deptId || this.userInfo.dept_id;
const currentDept = this.organizationOptions.find(
item => String(item.id) === String(currentDeptId)
);
return currentDept?.rawLabel || this.userInfo.deptName || this.userInfo.dept_name || '';
},
validateField(prop) {
this.$refs.shipForm?.validateField(prop);
},
normalizeNonNegativeDecimal(prop) {
const value = String(this.shipForm[prop] ?? '').replace(/[^\d.]/g, '');
const [integerPart = '', ...decimalParts] = value.split('.');
const decimalPart = decimalParts.join('').slice(0, 2);
this.shipForm[prop] = decimalParts.length ? `${integerPart}.${decimalPart}` : integerPart;
},
validateNonNegativeField(rule, value, callback) {
if (value !== undefined && value !== null && value < 0) {
callback(new Error('数值不能小于0'));
@@ -818,6 +870,7 @@ export default {
this.readonly = readonly;
if (!row?.id) {
this.shipForm = emptyForm();
this.shipForm.organizationName = this.getCurrentOrganizationName();
this.shipBox = true;
return;
}
@@ -826,6 +879,10 @@ export default {
this.shipBox = true;
});
},
openShipLedger(row) {
this.ledgerShip = { ...row };
this.shipLedgerBox = true;
},
resetShip() {
this.shipForm = emptyForm();
this.readonly = false;
@@ -881,7 +938,8 @@ export default {
});
});
},
handleExpireChange() {
handleExpireChange(expireStatus) {
this.query.expireStatus = expireStatus;
this.page.currentPage = 1;
this.onLoad(this.page);
},
@@ -977,14 +1035,28 @@ export default {
if (endDate.diff(today, 'day') <= 30) return 'date-warning';
return '';
},
getCertificateExpiryClass(row, dateProp, longTermProp) {
if (row[longTermProp] === 1 || !row[dateProp]) return '';
const endDate = this.$dayjs(row[dateProp]);
return endDate.isValid() && endDate.diff(this.$dayjs(), 'day') <= 30 ? 'date-danger' : '';
},
},
};
</script>
<style lang="scss" scoped>
.transport-ship-page {
.ship-stat {
flex-shrink: 0;
&__expiry-tags {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
width: 100%;
:deep(.el-check-tag) {
margin: 0;
white-space: nowrap;
}
}
:deep(.el-table th .cell),

View File

@@ -41,29 +41,39 @@
>批量删除
</el-button>
</template>
<template #menu-right>
<el-radio-group
v-model="query.expireStatus"
class="vehicle-stat"
@change="handleExpireChange"
>
<el-radio-button label="">全部{{ expiryStat.total }}</el-radio-button>
<el-radio-button label="within30"
>30天内到期{{ expiryStat.within30 }}</el-radio-button
<template #expireStatus-search>
<div class="transport-vehicle-page__expiry-tags">
<el-check-tag
v-for="item in expiryTagOptions"
:key="item.value"
:checked="query.expireStatus === item.value"
@change="handleExpireChange(item.value)"
>
<el-radio-button label="expired">已到期{{ expiryStat.expired }}</el-radio-button>
</el-radio-group>
{{ item.label }}{{ expiryStat[item.statKey] || 0 }}
</el-check-tag>
</div>
</template>
<template #plateNo="{ row }">
<el-button type="primary" link @click="openVehicle(row, true)">
{{ row.plateNo }}
</el-button>
</template>
<template #organizationName="{ row }">
{{ String(row.organizationName || '').replace(/^[\s\u3000]+/, '') }}
</template>
<template #status="{ row }">
<el-tag :type="row.status === 1 ? 'primary' : 'info'">
{{ row.status === 1 ? '启用' : '停用' }}
</el-tag>
</template>
<template #boundDriver>
<span>-</span>
</template>
<template #certificationStatus="{ row }">
<el-tag :type="getCertificationStatus(row).type">
{{ getCertificationStatus(row).label }}
</el-tag>
</template>
<template #drivingLicenseEndDate="{ row }">
<span :class="getDateClass(row, 'drivingLicenseEndDate', 'drivingLicenseLongTerm')">
{{ formatEndDate(row.drivingLicenseEndDate, row.drivingLicenseLongTerm) }}
@@ -94,6 +104,10 @@
>
修改
</el-link>
<el-link type="primary" @click="openVehicleLedger(row)">综合台账</el-link>
<el-link type="primary" v-if="row.certificationStatus === 0" @click="openCertificationAudit(row)">
审核
</el-link>
<el-link
type="primary"
v-if="hasPermission('transport_vehicle_status')"
@@ -104,6 +118,26 @@
</template>
</avue-crud>
<vehicle-ledger v-model="vehicleLedgerBox" :vehicle="ledgerVehicle" />
<el-dialog v-model="certificationAuditBox" title="车辆认证审核" width="92%" top="4vh">
<el-descriptions :column="3" border>
<el-descriptions-item v-for="item in auditDetailItems" :key="item.label" :label="item.label">
{{ formatAuditValue(item) }}
</el-descriptions-item>
</el-descriptions>
<el-form v-if="showRejectReason" class="certification-audit__reason" label-position="right" label-width="auto">
<el-form-item label="驳回原因" required>
<el-input v-model="certificationRejectReason" type="textarea" maxlength="200" show-word-limit />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="certificationAuditBox = false">取消</el-button>
<el-button type="danger" @click="handleCertificationReject">认证驳回</el-button>
<el-button type="primary" @click="handleCertificationApprove">认证通过</el-button>
</template>
</el-dialog>
<el-dialog
:title="dialogTitle"
append-to-body
@@ -201,29 +235,26 @@
<div class="dimension-group">
<div class="dimension-group__item">
<span class="dimension-group__label"></span>
<el-input-number
<el-input
v-model="vehicleForm.outerLength"
:min="0"
:precision="0"
controls-position="right"
inputmode="numeric"
@input="vehicleForm.outerLength = digitsOnly($event)"
/>
</div>
<div class="dimension-group__item">
<span class="dimension-group__label"></span>
<el-input-number
<el-input
v-model="vehicleForm.outerWidth"
:min="0"
:precision="0"
controls-position="right"
inputmode="numeric"
@input="vehicleForm.outerWidth = digitsOnly($event)"
/>
</div>
<div class="dimension-group__item">
<span class="dimension-group__label"></span>
<el-input-number
<el-input
v-model="vehicleForm.outerHeight"
:min="0"
:precision="0"
controls-position="right"
inputmode="numeric"
@input="vehicleForm.outerHeight = digitsOnly($event)"
/>
</div>
</div>
@@ -233,21 +264,19 @@
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="核定载质量(KG)" prop="approvedLoadKg">
<el-input-number
<el-input
v-model="vehicleForm.approvedLoadKg"
:min="0"
:precision="0"
controls-position="right"
inputmode="numeric"
@input="vehicleForm.approvedLoadKg = digitsOnly($event)"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="准牵引总质量(KG)" prop="tractionMassKg">
<el-input-number
<el-input
v-model="vehicleForm.tractionMassKg"
:min="0"
:precision="0"
controls-position="right"
inputmode="numeric"
@input="vehicleForm.tractionMassKg = digitsOnly($event)"
/>
</el-form-item>
</el-col>
@@ -509,6 +538,7 @@ import {
submit,
remove,
changeStatus,
auditCertification,
getExpiryStat,
recognitionTransportCertificates,
} from '@/api/transportCapacity/transport-vehicle';
@@ -518,6 +548,7 @@ import { getToken } from '@/utils/auth';
import { getUploadHeaders } from '@/utils/upload';
import { downloadXls } from '@/utils/util';
import ImageUploadField from '@/components/image-upload-field/main.vue';
import VehicleLedger from './components/vehicle-ledger.vue';
const emptyForm = () => ({
id: '',
@@ -599,6 +630,7 @@ const plateColorOptions = ['黄牌', '蓝牌', '绿牌'].map(item => ({ label: i
export default {
components: {
ImageUploadField,
VehicleLedger,
},
data() {
return {
@@ -607,7 +639,9 @@ export default {
query: {
expireStatus: '',
},
searchForm: {},
searchForm: {
businessRelation: '',
},
loading: true,
submitLoading: false,
data: [],
@@ -624,6 +658,12 @@ export default {
expired: 0,
},
vehicleBox: false,
vehicleLedgerBox: false,
ledgerVehicle: {},
certificationAuditBox: false,
certificationAuditForm: {},
certificationRejectReason: '',
showRejectReason: false,
readonly: false,
vehicleForm: emptyForm(),
vehicleCertificateUploads: {},
@@ -643,7 +683,10 @@ export default {
energyTypeOptions: ['柴油', '汽油', '新能源', '天然气'],
formRules: {
organizationName: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
plateNo: [{ validator: this.validatePlateNo, trigger: 'change' }],
plateNo: [
{ required: true, message: '请输入车牌号', trigger: 'change' },
{ validator: this.validatePlateNo, trigger: 'change' },
],
vehicleType: [{ required: true, message: '请选择车辆类型', trigger: 'change' }],
approvedLoadKg: [{ required: true, message: '请输入核定载质量', trigger: 'blur' }],
businessRelation: [{ required: true, message: '请选择业务关系', trigger: 'change' }],
@@ -681,6 +724,25 @@ export default {
}
return this.vehicleForm.id ? '修改车辆' : '新增车辆';
},
auditDetailItems() {
return [
['所属组织', 'organizationName'], ['车牌号', 'plateNo'], ['车牌颜色', 'plateColor'],
['车辆类型', 'vehicleType'], ['外廓长度(mm)', 'outerLength'], ['外廓宽度(mm)', 'outerWidth'],
['外廓高度(mm)', 'outerHeight'], ['核定载质量(KG)', 'approvedLoadKg'], ['准牵引总质量(KG)', 'tractionMassKg'],
['业务关系', 'businessRelation'], ['能源类型', 'energyType'], ['强制报废日期', 'compulsoryScrapDate'],
['海关备案号', 'customsRecordNo'], ['行驶证档案编号', 'drivingLicenseNo'], ['行驶证有效期起', 'drivingLicenseStartDate'],
['行驶证有效期止', 'drivingLicenseEndDate'], ['道路运输证号', 'roadTransportCertNo'], ['道路运输证有效期起', 'roadTransportCertStartDate'],
['道路运输证有效期止', 'roadTransportCertEndDate'], ['道路运输年审有效期', 'annualReviewEndDate'],
['机动车登记编号', 'registrationNo'], ['机动车登记日期', 'registrationDate'], ['备注', 'remark'],
].map(([label, prop]) => ({ label, prop }));
},
expiryTagOptions() {
return [
{ label: '全部', value: '', statKey: 'total' },
{ label: '30天内到期', value: 'within30', statKey: 'within30' },
{ label: '已到期', value: 'expired', statKey: 'expired' },
];
},
},
created() {
this.initDeptTree();
@@ -694,15 +756,20 @@ export default {
this.organizationOptions = this.flattenDeptOptions(res.data.data || []);
const column = this.findColumn(this.option.column, 'organizationName');
column.dicData = this.organizationOptions;
if (this.vehicleBox && !this.vehicleForm.id && !this.vehicleForm.organizationName) {
this.vehicleForm.organizationName = this.getCurrentOrganizationName();
}
});
},
flattenDeptOptions(tree = [], level = 0) {
const result = [];
tree.forEach(item => {
const label = item.title || item.deptName || item.name;
const rawLabel = item.title || item.deptName || item.name;
result.push({
label: `${' '.repeat(level)}${label}`,
value: label,
label: `${' '.repeat(level)}${rawLabel}`,
value: rawLabel,
rawLabel,
id: item.id || item.value,
});
if (item.children && item.children.length) {
result.push(...this.flattenDeptOptions(item.children, level + 1));
@@ -710,6 +777,16 @@ export default {
});
return result;
},
getCurrentOrganizationName() {
const currentDeptId = this.userInfo.deptId || this.userInfo.dept_id;
const currentDept = this.organizationOptions.find(
item => String(item.id) === String(currentDeptId)
);
return currentDept?.rawLabel || this.userInfo.deptName || this.userInfo.dept_name || '';
},
digitsOnly(value = '') {
return String(value).replace(/\D/g, '');
},
validatePlateNo(rule, value, callback) {
const plateProvince = String(this.vehicleForm.plateProvince || '').trim();
const plateNoBody = String(this.vehicleForm.plateNoBody || '')
@@ -737,6 +814,7 @@ export default {
this.readonly = readonly;
if (!row?.id) {
this.vehicleForm = emptyForm();
this.vehicleForm.organizationName = this.getCurrentOrganizationName();
this.syncPlateNo(false);
this.vehicleBox = true;
return;
@@ -751,6 +829,38 @@ export default {
this.vehicleBox = true;
});
},
openVehicleLedger(row) {
this.ledgerVehicle = { ...row };
this.vehicleLedgerBox = true;
},
openCertificationAudit(row) {
getDetail(row.id).then(res => {
this.certificationAuditForm = { ...(res.data.data || {}) };
this.certificationRejectReason = '';
this.showRejectReason = false;
this.certificationAuditBox = true;
});
},
formatAuditValue(item) {
const value = this.certificationAuditForm[item.prop];
return value === undefined || value === null || value === '' ? '-' : value;
},
handleCertificationApprove() {
auditCertification(this.certificationAuditForm.id, 1).then(() => {
this.$message.success('认证已通过');
this.certificationAuditBox = false;
this.onLoad(this.page);
});
},
handleCertificationReject() {
if (!this.showRejectReason) { this.showRejectReason = true; return; }
if (!this.certificationRejectReason.trim()) { this.$message.warning('请输入认证驳回原因'); return; }
auditCertification(this.certificationAuditForm.id, 2, this.certificationRejectReason).then(() => {
this.$message.success('认证已驳回');
this.certificationAuditBox = false;
this.onLoad(this.page);
});
},
resetVehicle() {
this.vehicleForm = emptyForm();
this.vehicleCertificateUploads = {};
@@ -762,9 +872,9 @@ export default {
this.vehicleForm[prop] = url;
this.$refs.vehicleForm?.validateField(prop);
},
handleVehicleCertificateUploadSuccess(prop, url, file = {}) {
handleVehicleCertificateUploadSuccess(prop, url) {
this.setImage(prop, url);
this.vehicleCertificateUploads[prop] = this.getUploadRecognitionKey(file, url);
this.vehicleCertificateUploads[prop] = url;
this.recognizeVehicleCertificates();
},
recognizeVehicleCertificates() {
@@ -845,19 +955,6 @@ export default {
});
});
},
getUploadRecognitionKey(file = {}, url = '') {
return (
file.objectKey ||
file.key ||
file.fileName ||
file.name ||
file.link ||
file.url ||
file.domain ||
url ||
''
);
},
normalizeDate(value = '') {
const dateValue = String(value || '').trim();
const match = dateValue.match(/^(\d{4})[-/.年](\d{1,2})[-/.月](\d{1,2})日?$/);
@@ -948,7 +1045,8 @@ export default {
});
});
},
handleExpireChange() {
handleExpireChange(expireStatus) {
this.query.expireStatus = expireStatus;
this.page.currentPage = 1;
this.onLoad(this.page);
},
@@ -962,7 +1060,9 @@ export default {
});
},
searchReset() {
this.searchForm = {};
this.searchForm = {
businessRelation: '',
};
this.query = {
expireStatus: this.query.expireStatus,
};
@@ -1063,14 +1163,44 @@ export default {
}
return '';
},
getCertificationStatus(row) {
if (row.certificationStatus === 0) return { label: '认证中', type: 'warning' };
if (row.certificationStatus === 1) return { label: '已认证', type: 'success' };
if (row.certificationStatus === 2) return { label: '已驳回', type: 'danger' };
const today = this.$dayjs().startOf('day');
const certificateDates = [
[row.drivingLicenseEndDate, row.drivingLicenseLongTerm],
[row.roadTransportCertEndDate, row.roadTransportCertLongTerm],
];
if (certificateDates.some(([date, longTerm]) => longTerm !== 1 && !date)) {
return { label: '未认证', type: 'info' };
}
if (
certificateDates.some(
([date, longTerm]) => longTerm !== 1 && this.$dayjs(date).isBefore(today, 'day')
)
) {
return { label: '已过期', type: 'danger' };
}
return { label: '已认证', type: 'success' };
},
},
};
</script>
<style lang="scss" scoped>
.transport-vehicle-page {
.vehicle-stat {
flex-shrink: 0;
&__expiry-tags {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
width: 100%;
:deep(.el-check-tag) {
margin: 0;
white-space: nowrap;
}
}
:deep(.el-table th .cell),
@@ -1193,7 +1323,7 @@ export default {
}
.date-warning {
color: #f56c6c;
color: #d40000;
font-weight: 600;
}

View File

@@ -72,7 +72,7 @@ export default {
index: true,
selection: true,
viewBtn: true,
menuWidth: 160,
menuWidth: 180,
dialogWidth: 880,
column: [
{

View File

@@ -119,7 +119,7 @@ export default {
index: true,
selection: true,
viewBtn: true,
menuWidth: 160,
menuWidth: 180,
dialogWidth: 880,
column: [
{

View File

@@ -124,7 +124,7 @@ export default {
selection: true,
viewBtn: true,
height: 300,
menuWidth: 160,
menuWidth: 180,
dialogWidth: 880,
column: [
{

View File

@@ -33,6 +33,7 @@ export default {
{
label: '申请日期',
prop: 'date',
sortable: true,
type: 'date',
row: true,
},

View File

@@ -103,6 +103,7 @@ export default {
{
label: '申请日期',
prop: 'date',
sortable: true,
type: 'date',
span: 14,
row: true,

View File

@@ -70,6 +70,28 @@
class="accident-record-page__input"
/>
</template>
<template #directEconomicLossForm>
<el-input
v-model="form.directEconomicLoss"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('directEconomicLoss', value)"
>
<template #suffix></template>
</el-input>
</template>
<template #insuranceClaimAmountForm>
<el-input
v-model="form.insuranceClaimAmount"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('insuranceClaimAmount', value)"
>
<template #suffix></template>
</el-input>
</template>
<template #attachments="{ row }">
<span>{{ formatAttachments(row.attachments) }}</span>
</template>
@@ -256,6 +278,13 @@ export default {
if (!value || typeof value === 'string') return value;
return JSON.stringify(value);
},
normalizeDecimalInput(prop, value) {
const sanitized = String(value ?? '').replace(/[^\d.]/g, '');
const [integerPart, ...decimalParts] = sanitized.split('.');
this.form[prop] = decimalParts.length
? `${integerPart || '0'}.${decimalParts.join('').slice(0, 2)}`
: integerPart;
},
normalizeRow(row) {
const values = { ...row };
values.vehicleType = values.vehicleType || '车辆';

View File

@@ -70,6 +70,17 @@
class="annual-inspection-record-page__input"
/>
</template>
<template #feeForm>
<el-input
v-model="form.fee"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('fee', value)"
>
<template #suffix></template>
</el-input>
</template>
<template #attachments="{ row }">
<span>{{ formatAttachments(row.attachments) }}</span>
</template>
@@ -256,6 +267,13 @@ export default {
if (!value || typeof value === 'string') return value;
return JSON.stringify(value);
},
normalizeDecimalInput(prop, value) {
const sanitized = String(value ?? '').replace(/[^\d.]/g, '');
const [integerPart, ...decimalParts] = sanitized.split('.');
this.form[prop] = decimalParts.length
? `${integerPart || '0'}.${decimalParts.join('').slice(0, 2)}`
: integerPart;
},
normalizeRow(row) {
const values = { ...row };
values.vehicleType = values.vehicleType || '车辆';

View File

@@ -429,7 +429,7 @@ export default {
editBtn: false,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '评分表名称',
@@ -449,6 +449,7 @@ export default {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -1022,8 +1023,8 @@ export default {
}
.base-form {
max-width: 1080px;
margin: 10px auto 28px;
max-width: 60%;
padding: 24px;
}
.section-head {

View File

@@ -151,9 +151,7 @@
:disabled="readonly"
class="archive-form"
>
<div class="section-title dialog-section-title">基本信息</div>
<div class="archive-form__group">
<div class="dialog-section-title archive-form__group-title">工商信息</div>
<section-card title="工商信息">
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="客商编号" prop="customerCode">
@@ -297,10 +295,9 @@
</el-form-item>
</el-col>
</el-row>
</div>
</section-card>
<div class="archive-form__group">
<div class="dialog-section-title archive-form__group-title">联系信息</div>
<section-card title="联系信息">
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="客商负责人" prop="principal">
@@ -329,10 +326,9 @@
</el-form-item>
</el-col>
</el-row>
</div>
</section-card>
<div class="archive-form__group">
<div class="dialog-section-title archive-form__group-title">财务/信用信息</div>
<section-card title="财务/信用信息">
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="注册资金(万元)" prop="registeredCapital">
@@ -377,10 +373,9 @@
</el-form-item>
</el-col>
</el-row>
</div>
</section-card>
<div class="archive-form__group">
<div class="dialog-section-title archive-form__group-title">其他信息</div>
<section-card title="其他信息">
<el-row :gutter="18">
<el-col :span="24">
<el-form-item label="备注" prop="remark">
@@ -393,24 +388,24 @@
</el-form-item>
</el-col>
</el-row>
</div>
</section-card>
</el-form>
<el-tabs v-model="activeTab" class="archive-tabs">
<el-tab-pane label="评分表" name="scores">
<div class="section-head">
<span></span>
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="addScore">
添加评分记录
</el-button>
</div>
<el-table :data="scoreTableData" border class="score-list-table">
<section-card>
<template #extra>
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="addScore">
添加评分记录
</el-button>
</template>
<el-table :data="scoreTableData" border class="score-list-table">
<el-table-column label="序号" width="90" align="center">
<template #default="{ row }">
<span>{{ row.__index }}</span>
</template>
</el-table-column>
<el-table-column label="评分日期" min-width="150" align="center">
<el-table-column label="评分日期" min-width="150" align="center" sortable>
<template #default="{ row }">
<span>{{ row.scoreDate }}</span>
</template>
@@ -470,15 +465,16 @@
@size-change="handleScoreSizeChange"
@current-change="handleScoreCurrentChange"
/>
</section-card>
</el-tab-pane>
<el-tab-pane label="联系人" name="contacts">
<div class="section-head">
<span></span>
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="openContact()">
新增联系人
</el-button>
</div>
<el-table :data="contactTableData" border class="contact-table">
<section-card>
<template #extra>
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="openContact()">
新增联系人
</el-button>
</template>
<el-table :data="contactTableData" border class="contact-table">
<el-table-column label="序号" prop="__index" width="90" align="center" />
<el-table-column label="联系人姓名" prop="contactName" min-width="150" />
<el-table-column label="联系人电话" prop="contactPhone" min-width="150" />
@@ -509,15 +505,16 @@
@size-change="size => handleDetailSizeChange('contact', size)"
@current-change="page => handleDetailCurrentChange('contact', page)"
/>
</section-card>
</el-tab-pane>
<el-tab-pane label="收款信息" name="receiptAccounts">
<div class="section-head">
<span></span>
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="openReceipt()">
新增收款信息
</el-button>
</div>
<el-table :data="receiptTableData" border class="receipt-table">
<section-card>
<template #extra>
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="openReceipt()">
新增收款信息
</el-button>
</template>
<el-table :data="receiptTableData" border class="receipt-table">
<el-table-column label="序号" prop="__index" width="90" align="center" />
<el-table-column label="收款单位名称" prop="accountName" min-width="180" />
<el-table-column label="开户人姓名" prop="accountHolderName" min-width="150" />
@@ -539,15 +536,16 @@
@size-change="size => handleDetailSizeChange('receipt', size)"
@current-change="page => handleDetailCurrentChange('receipt', page)"
/>
</section-card>
</el-tab-pane>
<el-tab-pane label="发票信息" name="invoices">
<div class="section-head">
<span></span>
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="openInvoice()">
新增发票信息
</el-button>
</div>
<el-table :data="invoiceTableData" border class="invoice-table">
<section-card>
<template #extra>
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="openInvoice()">
新增发票信息
</el-button>
</template>
<el-table :data="invoiceTableData" border class="invoice-table">
<el-table-column label="序号" prop="__index" width="90" align="center" />
<el-table-column label="收票方名称" prop="invoiceTitle" min-width="180" />
<el-table-column label="纳税人识别号" prop="taxNo" min-width="180" />
@@ -575,12 +573,12 @@
@size-change="size => handleDetailSizeChange('invoice', size)"
@current-change="page => handleDetailCurrentChange('invoice', page)"
/>
</section-card>
</el-tab-pane>
</el-tabs>
<div class="customer-material-section">
<div class="section-head">
<div class="section-title dialog-section-title">客商材料</div>
<section-card title="客商材料">
<template #extra>
<div v-if="!readonly" class="section-actions">
<el-button icon="el-icon-download" @click="downloadAttachments">批量下载</el-button>
<el-button type="primary" icon="el-icon-plus" @click="addAttachment"
@@ -590,7 +588,7 @@
OCR上传识别
</el-button>
</div>
</div>
</template>
<el-alert
type="info"
:closable="false"
@@ -644,7 +642,7 @@
<span>{{ row.uploadUserName || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="上传时间" min-width="170">
<el-table-column label="上传时间" min-width="170" sortable>
<template #default="{ row }">
<span>{{ row.uploadTime || '-' }}</span>
</template>
@@ -657,17 +655,16 @@
</template>
</el-table-column>
</el-table>
</div>
</section-card>
<div class="change-record-section">
<div class="section-title dialog-section-title">变更记录</div>
<section-card title="变更记录">
<el-table :data="archiveForm.changeRecords" border>
<el-table-column label="序号" type="index" width="90" align="center" />
<el-table-column label="变更日期" prop="changeTime" min-width="170" />
<el-table-column label="变更日期" prop="changeTime" min-width="170" sortable />
<el-table-column label="变更内容" prop="changeContent" min-width="260" />
<el-table-column label="变更账号" prop="changeUserName" min-width="160" />
</el-table>
</div>
</section-card>
<template #footer>
<el-button @click="archiveBox = false">{{ readonly ? '关闭' : '取消' }}</el-button>
@@ -838,8 +835,8 @@
label-width="120px"
class="invoice-form"
>
<div class="invoice-form__section-title">发票信息</div>
<el-row :gutter="34">
<section-card title="发票信息">
<el-row :gutter="34">
<el-col :span="12">
<el-form-item label="纳税人识别号" prop="taxNo">
<el-input v-model="invoiceForm.taxNo" maxlength="30" />
@@ -887,8 +884,10 @@
</el-form-item>
</el-col>
</el-row>
<div class="invoice-form__section-title">邮寄信息</div>
<el-row :gutter="34">
</section-card>
<section-card title="邮寄信息">
<el-row :gutter="34">
<el-col :span="12">
<el-form-item label="收件人姓名" prop="receiverName">
<el-input v-model="invoiceForm.receiverName" maxlength="30" />
@@ -917,6 +916,7 @@
</el-form-item>
</el-col>
</el-row>
</section-card>
</el-form>
<template #footer>
<div class="invoice-dialog__footer-actions">
@@ -1075,7 +1075,7 @@
<el-input
v-model="detail.reviewScore"
maxlength="10"
:disabled="readonly"
:disabled="readonly || !hasPermission('customer_type_re_cert')"
@input="value => handleScoreDetailScoreInput(detail, 'reviewScore', value)"
>
<template #suffix>分</template>
@@ -1105,9 +1105,8 @@
</el-table-column>
</el-table>
<div class="score-material-section">
<div class="score-material-section__head">
<div class="dialog-section-title">证明材料</div>
<section-card title="证明材料" class="score-material-card">
<template #extra>
<vehicle-attachment-upload
v-if="!readonly"
v-model="scoreAttachmentFiles"
@@ -1116,7 +1115,7 @@
:show-tip="false"
@success="handleScoreAttachmentUploadSuccess"
/>
</div>
</template>
<el-table :data="scoreAttachmentFiles" border class="score-material-table">
<el-table-column label="序号" type="index" width="90" align="center" />
<el-table-column label="文件名" prop="name" min-width="220" />
@@ -1131,7 +1130,7 @@
<span>{{ row.uploadUserName || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="上传时间" min-width="180" align="center">
<el-table-column label="上传时间" min-width="180" align="center" sortable>
<template #default="{ row }">
<span>{{ row.uploadTime || '-' }}</span>
</template>
@@ -1144,11 +1143,17 @@
</template>
</el-table-column>
</el-table>
</div>
</section-card>
<template #footer>
<el-button type="primary" v-if="!readonly" @click="saveScoreDetail">保存</el-button>
<el-button type="primary" v-if="!readonly" @click="confirmReviewScore">复评确认</el-button>
<el-button
v-if="!readonly && hasPermission('customer_type_re_cert')"
type="primary"
@click="confirmReviewScore"
>
复评确认
</el-button>
<el-button type="primary" @click="scoreBox = false">关闭</el-button>
</template>
</el-dialog>
@@ -1425,7 +1430,7 @@ export default {
editBtn: false,
selection: true,
dialogClickModal: false,
menuWidth: 320,
menuWidth: 280,
column: [
{
label: '客商编号',
@@ -1532,6 +1537,7 @@ export default {
{
label: '审核通过时间',
prop: 'approvedTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -3471,30 +3477,7 @@ export default {
}
}
.archive-form__group {
padding: 16px 20px;
margin-bottom: 16px;
background-color: #f0f2f5;
border-radius: 8px;
.dialog-section-title {
margin: 0 0 12px;
font-size: 14px;
font-weight: 500;
color: #303133;
}
// 内层白色卡片:字段区域白底,浮于灰色分组块之上
> .el-row {
margin: 0;
padding: 18px 16px 4px;
background-color: #fff;
border-radius: 6px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}
}
.business-term-field,
business-term-field,
.archive-form__address {
display: flex;
align-items: center;
@@ -3526,24 +3509,6 @@ export default {
}
}
.section-title {
margin: 16px 0 12px;
font-size: 18px;
font-weight: 700;
color: #303133;
}
.section-head {
display: flex;
align-items: center;
justify-content: space-between;
margin: 18px 0 12px;
.section-title {
margin: 0;
}
}
.section-actions {
display: flex;
flex-wrap: wrap;
@@ -3681,13 +3646,7 @@ export default {
}
}
.customer-material-section {
margin-top: 18px;
}
.change-record-section {
margin-top: 18px;
}
// 客商材料、变更记录已改用全局 <section-card> 组件,无需额外 margin-top
.score-list-table,
.contact-table,
@@ -3714,25 +3673,6 @@ export default {
.invoice-form {
max-width: none;
&__section-title {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
margin: 8px 0 20px;
font-size: 16px;
font-weight: 700;
color: #303133;
&::before,
&::after {
flex: 1;
height: 1px;
content: '';
background-color: #dcdfe6;
}
}
}
.contact-form__address {
@@ -3843,20 +3783,7 @@ export default {
}
}
.score-material-section {
margin: 26px 36px 0;
}
.score-material-section__head {
display: flex;
align-items: center;
gap: 18px;
margin-bottom: 16px;
.dialog-section-title {
margin: 0;
}
.score-material-card {
:deep(.vehicle-attachment-upload) {
width: auto;
}

View File

@@ -0,0 +1,343 @@
<template>
<basic-container class="equipment-ledger-page">
<avue-crud
ref="crud"
v-model="form"
v-model:page="page"
:option="option"
:data="data"
:permission="permissionList"
:table-loading="loading"
: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
v-if="hasPermission('equipment_ledger_import')"
type="primary"
plain
@click="handleImport"
>
批量导入
</el-button>
<el-button
v-if="hasPermission('equipment_ledger_template')"
type="primary"
plain
@click="handleTemplate"
>
下载模板
</el-button>
<el-button
v-if="hasPermission('equipment_ledger_export')"
type="primary"
plain
@click="handleExport"
>
批量导出
</el-button>
</template>
<template #vehicleNo="{ row, index }">
<el-link type="primary" @click="$refs.crud.rowView(row, index)">{{
row.vehicleNo
}}</el-link>
</template>
<template #vehicleNoForm>
<el-autocomplete
v-model="form.vehicleNo"
:disabled="boxType === 'view'"
:fetch-suggestions="fetchVehicleOptions"
:placeholder="vehicleNoPlaceholder"
clearable
value-key="value"
/>
</template>
<template #equipmentCodeForm>
<el-input v-model="form.equipmentCode" disabled />
</template>
<template #attachments="{ row }">
<span>{{ formatAttachments(row.attachments) }}</span>
</template>
<template #attachmentsForm>
<vehicle-attachment-upload v-model="form.attachments" :readonly="boxType === 'view'" />
</template>
<template #attachments-form>
<vehicle-attachment-upload v-model="form.attachments" :readonly="boxType === 'view'" />
</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">点击下载</el-button>
</template>
</avue-form>
</el-dialog>
</basic-container>
</template>
<script>
import {
add,
getDetail,
getList,
getNextEquipmentCode,
remove,
update,
} from '@/api/vehicle/equipment-ledger';
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
import { getList as getShipList } from '@/api/transportCapacity/transport-ship';
import { exportBlob } from '@/api/common';
import { getDeptTree } from '@/api/system/dept';
import { getDictionary } from '@/api/system/dictbiz';
import { getToken } from '@/utils/auth';
import { openImportDialog } from '@/utils/import-excel';
import { downloadXls } from '@/utils/util';
import { mapGetters } from 'vuex';
import { excelOption, option } from '@/option/vehicle/equipment-ledger';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
export default {
data() {
return {
form: {},
query: {},
loading: true,
excelBox: false,
excelForm: {},
boxType: '',
option,
excelOption,
data: [],
selectionList: [],
page: { pageSize: 10, pageSizes: [10, 20, 50, 100], currentPage: 1, total: 0 },
};
},
computed: {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
return (this.userInfo.authority || '').includes('admin');
},
permissionList() {
return {
addBtn: this.hasPermission('equipment_ledger_add'),
viewBtn: this.hasPermission('equipment_ledger_view'),
editBtn: this.hasPermission('equipment_ledger_edit'),
delBtn: this.hasPermission('equipment_ledger_delete'),
};
},
vehicleNoPlaceholder() {
return this.form.vehicleType === '船舶' ? '输入船号模糊查询选择' : '输入车牌号模糊查询选择';
},
ids() {
return this.selectionList.map(item => item.id).join(',');
},
},
created() {
this.initDeptTree();
this.initEquipmentTypeOptions();
},
methods: {
hasPermission(code) {
return this.isAdmin || this.validData(this.permission[code], false);
},
initDeptTree() {
getDeptTree(this.userInfo.tenantId).then(res => {
this.findColumn(this.option.column, 'createDept').dicData = res.data.data || [];
});
},
initEquipmentTypeOptions() {
getDictionary({ code: 'equip_type' }).then(res => {
this.findColumn(this.option.column, 'equipmentType').dicData = (res.data.data || []).map(
item => ({
label: item.dictValue,
value: item.dictValue,
})
);
});
},
fetchVehicleOptions(queryString, callback) {
const isShip = this.form.vehicleType === '船舶';
const request = isShip ? getShipList : getVehicleList;
const params = isShip ? { shipIdentifierNo: queryString } : { plateNo: queryString };
request(1, 20, params)
.then(res => {
callback(
(res.data.data.records || []).map(item => ({
value: isShip ? item.shipIdentifierNo || item.shipName : item.plateNo,
}))
);
})
.catch(() => callback([]));
},
formatAttachments(value) {
if (!value) return '';
if (Array.isArray(value)) return `${value.length}`;
try {
const attachments = JSON.parse(value);
return Array.isArray(attachments) ? `${attachments.length}` : '1 个';
} catch (error) {
return `${value.split(',').filter(Boolean).length}`;
}
},
parseAttachments(value) {
if (!value || Array.isArray(value)) return value;
try {
const attachments = JSON.parse(value);
return Array.isArray(attachments) ? attachments : [];
} catch (error) {
return value
.split(',')
.filter(Boolean)
.map(url => ({ name: url, url }));
}
},
normalizeRow(row) {
const values = { ...row };
values.vehicleType = values.vehicleType || '车辆';
values.vehicleNo = (values.vehicleNo || '').trim();
if (values.vehicleType === '车辆') values.vehicleNo = values.vehicleNo.toUpperCase();
values.attachments = Array.isArray(values.attachments)
? JSON.stringify(values.attachments)
: values.attachments;
return values;
},
rowSave(row, done, loading) {
add(this.normalizeRow(row)).then(
() => {
this.onLoad(this.page);
this.$message.success('操作成功');
done();
},
() => loading()
);
},
rowUpdate(row, index, done, loading) {
update(this.normalizeRow(row)).then(
() => {
this.onLoad(this.page);
this.$message.success('操作成功');
done();
},
() => loading()
);
},
rowDel(row) {
this.$confirm('确认删除该条数据?删除后将不可恢复!', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => remove(row.id))
.then(() => {
this.onLoad(this.page);
this.$message.success('操作成功');
});
},
beforeOpen(done, type) {
this.boxType = type;
if (type === 'add') {
getNextEquipmentCode().then(res => {
this.form = { vehicleType: '车辆', equipmentCode: res.data.data };
done();
});
return;
}
if (['edit', 'view'].includes(type)) {
getDetail(this.form.id).then(res => {
this.form = {
...res.data.data,
attachments: this.parseAttachments(res.data.data.attachments),
};
done();
});
return;
}
done();
},
searchChange(params, done) {
this.query = params;
this.page.currentPage = 1;
this.onLoad(this.page, params);
done();
},
searchReset() {
this.query = {};
this.onLoad(this.page);
},
selectionChange(list) {
this.selectionList = list;
},
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, { ...this.query, ...params })
.then(res => {
const result = res.data.data;
this.page.total = result.total;
this.data = result.records;
})
.finally(() => {
this.loading = false;
});
},
handleImport() {
openImportDialog(this, '设备台账');
},
handleExport() {
this.$confirm('是否导出设备台账数据?', '提示', { type: 'warning' }).then(() => {
NProgress.start();
exportBlob(
'/blade-transport/equipment-ledger/export-equipment-ledger',
{ ...this.query, ids: this.ids, [this.website.tokenHeader]: getToken() },
{ feedback: true }
)
.then(res =>
downloadXls(res.data, `设备台账${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`)
)
.finally(() => NProgress.done());
});
},
handleTemplate() {
exportBlob(
`/blade-transport/equipment-ledger/export-template?${
this.website.tokenHeader
}=${getToken()}`,
{ feedback: true }
).then(res => downloadXls(res.data, '设备台账模板.xlsx'));
},
},
};
</script>
<style lang="scss" scoped>
.equipment-ledger-page {
:deep(.el-table th .cell),
:deep(.el-table td .cell) {
white-space: nowrap;
}
}
</style>

View File

@@ -70,6 +70,17 @@
class="etc-record-page__input"
/>
</template>
<template #transactionAmountForm>
<el-input
v-model="form.transactionAmount"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('transactionAmount', value)"
>
<template #suffix></template>
</el-input>
</template>
<template #attachments="{ row }">
<span>{{ formatAttachments(row.attachments) }}</span>
</template>
@@ -209,6 +220,13 @@ export default {
isEmpty(value) {
return value === undefined || value === null || value === '';
},
normalizeDecimalInput(prop, value) {
const sanitized = String(value ?? '').replace(/[^\d.]/g, '');
const [integerPart, ...decimalParts] = sanitized.split('.');
this.form[prop] = decimalParts.length
? `${integerPart || '0'}.${decimalParts.join('').slice(0, 2)}`
: integerPart;
},
normalizeRow(row) {
const values = { ...row };
values.dataSource = values.dataSource || '手工录入';
@@ -379,11 +397,9 @@ export default {
type: 'warning',
}).then(() => {
NProgress.start();
exportBlob(
'/blade-transport/etc-record/export-etc-record',
this.buildExportParams(),
{ feedback: true }
)
exportBlob('/blade-transport/etc-record/export-etc-record', this.buildExportParams(), {
feedback: true,
})
.then(res => {
downloadXls(res.data, `ETC记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
})

View File

@@ -69,6 +69,28 @@
class="insurance-record-page__vehicle-input"
/>
</template>
<template #insuredAmountForm>
<el-input
v-model="form.insuredAmount"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('insuredAmount', value)"
>
<template #append></template>
</el-input>
</template>
<template #premiumForm>
<el-input
v-model="form.premium"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('premium', value)"
>
<template #append></template>
</el-input>
</template>
<template #policyFileForm>
<div class="insurance-record-page__upload">
<div class="insurance-record-page__upload-info">
@@ -177,6 +199,7 @@ export default {
excelBox: false,
excelForm: {},
isDetailLoading: false,
boxType: '',
option,
excelOption,
page: {
@@ -285,6 +308,13 @@ export default {
}
return values;
},
normalizeDecimalInput(prop, value) {
const sanitized = String(value ?? '').replace(/[^\d.]/g, '');
const [integerPart, ...decimalParts] = sanitized.split('.');
this.form[prop] = decimalParts.length
? `${integerPart || '0'}.${decimalParts.join('').slice(0, 2)}`
: integerPart;
},
buildQuery(params = {}) {
return normalizeSearchRangeParams({ ...params, ...this.query }, createTimeRangeMap);
},
@@ -369,6 +399,7 @@ export default {
});
},
beforeOpen(done, type) {
this.boxType = type;
if (type === 'add') {
this.form.vehicleType = '车辆';
this.form.ocrTemplate = '紫金-机动车交强险';

View File

@@ -70,6 +70,39 @@
class="maintenance-plan-page__input"
/>
</template>
<template #mileageForm>
<el-input
v-model="form.mileage"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('mileage', value)"
>
<template #append>{{ form.mileageUnit || '公里' }}</template>
</el-input>
</template>
<template #costForm>
<el-input
v-model="form.cost"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('cost', value)"
>
<template #append></template>
</el-input>
</template>
<template #nextMaintenanceMileageForm>
<el-input
v-model="form.nextMaintenanceMileage"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('nextMaintenanceMileage', value)"
>
<template #append>{{ form.mileageUnit || '公里' }}</template>
</el-input>
</template>
<template #mileage="{ row }">
<span>{{ formatMileage(row.mileage, row.mileageUnit) }}</span>
</template>
@@ -169,10 +202,11 @@ export default {
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '车船类型',
@@ -212,6 +246,7 @@ export default {
{
label: '保养时间',
prop: 'maintenanceTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -223,12 +258,10 @@ export default {
{
label: '里程/航程数',
prop: 'mileage',
type: 'number',
precision: 2,
type: 'input',
minWidth: 130,
slot: true,
span: 12,
append: '公里',
placeholder: '请输入',
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
},
@@ -255,9 +288,8 @@ export default {
{
label: '费用',
prop: 'cost',
type: 'number',
precision: 2,
append: '元',
type: 'input',
slot: true,
span: 12,
placeholder: '请输入',
rules: [
@@ -292,6 +324,7 @@ export default {
{
label: '下次保养时间',
prop: 'nextMaintenanceTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -302,12 +335,10 @@ export default {
{
label: '下次保养里程/航程',
prop: 'nextMaintenanceMileage',
type: 'number',
precision: 2,
type: 'input',
minWidth: 170,
slot: true,
span: 12,
append: '公里',
placeholder: '请输入',
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
},
@@ -332,6 +363,7 @@ export default {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -382,6 +414,7 @@ export default {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -531,6 +564,13 @@ export default {
if (!value || typeof value === 'string') return value;
return JSON.stringify(value);
},
normalizeDecimalInput(prop, value) {
const sanitized = String(value ?? '').replace(/[^\d.]/g, '');
const [integerPart, ...decimalParts] = sanitized.split('.');
this.form[prop] = decimalParts.length
? `${integerPart || '0'}.${decimalParts.join('').slice(0, 2)}`
: integerPart;
},
initDeptTree() {
getDeptTree(this.userInfo.tenantId).then(res => {
const column = this.findColumn(this.option.column, 'createDept');

View File

@@ -70,6 +70,28 @@
class="maintenance-record-page__input"
/>
</template>
<template #costForm>
<el-input
v-model="form.cost"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('cost', value)"
>
<template #append></template>
</el-input>
</template>
<template #mileageForm>
<el-input
v-model="form.mileage"
:disabled="boxType === 'view'"
inputmode="decimal"
placeholder="请输入"
@input="value => normalizeDecimalInput('mileage', value)"
>
<template #append>{{ form.mileageUnit || '公里' }}</template>
</el-input>
</template>
<template #mileage="{ row }">
<span>{{ formatMileage(row.mileage, row.mileageUnit) }}</span>
</template>
@@ -167,10 +189,11 @@ export default {
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
viewBtn: true,
selection: true,
dialogClickModal: false,
menuWidth: 160,
menuWidth: 180,
column: [
{
label: '车船类型',
@@ -211,6 +234,7 @@ export default {
{
label: '维修时间',
prop: 'maintenanceTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -239,9 +263,8 @@ export default {
{
label: '费用',
prop: 'cost',
type: 'number',
precision: 2,
append: '元',
type: 'input',
slot: true,
span: 12,
placeholder: '请输入',
rules: [
@@ -277,6 +300,7 @@ export default {
{
label: '出厂时间',
prop: 'factoryTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -287,10 +311,8 @@ export default {
{
label: '里程/航程数',
prop: 'mileage',
type: 'number',
precision: 2,
type: 'input',
minWidth: 130,
append: '公里',
slot: true,
span: 12,
placeholder: '请输入',
@@ -328,6 +350,7 @@ export default {
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'date',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -378,6 +401,7 @@ export default {
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -526,6 +550,13 @@ export default {
if (!value || typeof value === 'string') return value;
return JSON.stringify(value);
},
normalizeDecimalInput(prop, value) {
const sanitized = String(value ?? '').replace(/[^\d.]/g, '');
const [integerPart, ...decimalParts] = sanitized.split('.');
this.form[prop] = decimalParts.length
? `${integerPart || '0'}.${decimalParts.join('').slice(0, 2)}`
: integerPart;
},
initDeptTree() {
getDeptTree(this.userInfo.tenantId).then(res => {
const column = this.findColumn(this.option.column, 'createDept');

View File

@@ -82,6 +82,50 @@
class="mileage-record-page__input"
/>
</template>
<template #previousMonthMileageForm>
<el-input
v-model="form.previousMonthMileage"
:disabled="boxType === 'view'"
inputmode="numeric"
placeholder="请输入"
@input="value => normalizeIntegerInput('previousMonthMileage', value)"
>
<template #suffix>km</template>
</el-input>
</template>
<template #currentMonthMileageForm>
<el-input
v-model="form.currentMonthMileage"
:disabled="boxType === 'view'"
inputmode="numeric"
placeholder="请输入"
@input="value => normalizeIntegerInput('currentMonthMileage', value)"
>
<template #suffix>km</template>
</el-input>
</template>
<template #monthlyMileageForm>
<el-input
v-model="form.monthlyMileage"
:disabled="boxType === 'view'"
inputmode="numeric"
placeholder="请输入"
@input="value => normalizeIntegerInput('monthlyMileage', value)"
>
<template #suffix>km</template>
</el-input>
</template>
<template #totalMileageForm>
<el-input
v-model="form.totalMileage"
:disabled="boxType === 'view'"
inputmode="numeric"
placeholder="请输入"
@input="value => normalizeIntegerInput('totalMileage', value)"
>
<template #suffix>km</template>
</el-input>
</template>
<template #totalMileageStart-search="{ row }">
<div class="mileage-record-page__range">
<el-input
@@ -224,7 +268,6 @@ export default {
Object.keys(labelMap).forEach(prop => {
const column = this.findColumn(this.option.column, prop);
column.label = labelMap[prop];
column.append = unit;
});
vehicleTypeColumn.disabled = Boolean(this.form.id);
const unitColumn = this.findColumn(this.option.column, 'mileageUnit');
@@ -304,6 +347,9 @@ export default {
toMoney(value) {
return Math.round(Number(value) * 100) / 100;
},
normalizeIntegerInput(prop, value) {
this.form[prop] = String(value ?? '').replace(/\D/g, '');
},
normalizeRow(row) {
const values = { ...row };
values.vehicleType = values.vehicleType || '车辆';

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