fix(styles): 优化多页面表单及弹窗样式统一性和功能
- 全站业务状态标签文字化,el-tag.status-text 转为纯文本,统一字号、行高和基线对齐 - 临时额度详情弹窗加宽 label 列宽至 180px,避免标签折成三行 - 年检记录费用字段将单位“元”移至标签内,清理无效 append 配置和 template 代码 - 维修记录里程字段单位动态显示于标签,移除输入框右侧单位后缀 - 全站 Avue CRUD 弹窗标题自动添加页面全称,支持手动覆盖和自定义 - 合同管理独立表单页顶部补充“新增合同管理”标题,统一与其它表单页面样式 - 结算-正式结算页表格整合到“应付/应收”两个标签页内,复用组件并调整操作列宽度至320px - 业务相关弹窗和组件统一支持 dialogCustomClass 传递及样式适配 - 取消部分页面样式中冗余或注释代码,调整组件内按钮布局和权限判断函数逻辑 - 新增全局 mixin 以设置 CRUD 弹窗标题,主入口 main.js 注册相关初始化函数
This commit is contained in:
@@ -38,3 +38,47 @@
|
|||||||
- 根因:用户截图反馈 customer-archive 的"准入类型/审批状态/状态"列文字比其它列小/错位。`.el-tag` 默认 `font-size: var(--el-tag-font-size)`(12px)且 `height` 固定,即便 `.status-text` 去掉了背景边框,字号和盒模型仍与单元格普通文本不一致。
|
- 根因:用户截图反馈 customer-archive 的"准入类型/审批状态/状态"列文字比其它列小/错位。`.el-tag` 默认 `font-size: var(--el-tag-font-size)`(12px)且 `height` 固定,即便 `.status-text` 去掉了背景边框,字号和盒模型仍与单元格普通文本不一致。
|
||||||
- 修复:在 `src/styles/element-ui.scss` 的 `.el-tag.status-text` 全局规则追加 `font-size: inherit !important; height: auto !important; vertical-align: baseline; line-height: inherit;`,强制继承父级字号与行高,高度随内容自适应,基线对齐与普通 inline 文本一致。
|
- 修复:在 `src/styles/element-ui.scss` 的 `.el-tag.status-text` 全局规则追加 `font-size: inherit !important; height: auto !important; vertical-align: baseline; line-height: inherit;`,强制继承父级字号与行高,高度随内容自适应,基线对齐与普通 inline 文本一致。
|
||||||
- 结论:所有使用 `.status-text` 的状态列(customer-archive 准入类型/审批状态/状态、transport-plan 调度状态、waybill 运单状态、payment 审批状态、启用停用等)自动与相邻普通文字列的字体字号/行高/基线完全统一,无需逐页修改。
|
- 结论:所有使用 `.status-text` 的状态列(customer-archive 准入类型/审批状态/状态、transport-plan 调度状态、waybill 运单状态、payment 审批状态、启用停用等)自动与相邻普通文字列的字体字号/行高/基线完全统一,无需逐页修改。
|
||||||
|
|
||||||
|
## 临时额度详情弹窗长 label 折成两行
|
||||||
|
- 需求:用户截图反馈 `temporary-credit-limit` 详情弹窗中,"已使用项目资金使用额度(万元)" 和 "剩余项目资金使用额度(万元)" label 被折成三行,希望只折两行。
|
||||||
|
- 根因:`business-crud-page` 自定义详情弹窗(`detailBox`)使用 `el-descriptions :column="2"`,`.el-descriptions__label` 在 `business-crud-page__detail-content` 里被全局固定 `width: 140px`,15 个汉字的超长 label 在 140px 下被挤成三行。
|
||||||
|
- 修复:
|
||||||
|
- 让 `business-crud-page` 的详情弹窗根节点也携带页面对话框的 `dialogCustomClass`(在 `:class` 数组中加入 `$attrs.option?.dialogCustomClass`),使 `temporary-credit-limit-dialog` 类能作用到详情弹窗。
|
||||||
|
- 在 `temporary-credit-limit.vue` 全局 `<style>` 中新增 `.temporary-credit-limit-dialog .business-crud-page__detail-content .el-descriptions__label { width: 180px !important; }`,只对该页详情 label 加宽,让 15 字 label 自然折成两行。
|
||||||
|
- 改动文件:`src/views/business/components/business-crud-page.vue`、`src/views/business/temporary-credit-limit.vue`。
|
||||||
|
- 结论:仅临时额度管理详情弹窗的 label 列宽加宽到 180px,其它页面详情弹窗保持 140px 不变。
|
||||||
|
|
||||||
|
## 年检记录费用字段单位放 label
|
||||||
|
- 需求:用户截图反馈 `annual-inspection-record` 新增弹窗中"费用"输入框右侧带"元" suffix,要求把"元"放到 label 后并用括号括起,即"费用(元)",保持风格一致。
|
||||||
|
- 改动文件:
|
||||||
|
- `src/option/vehicle/annual-inspection-record.js`:`label` 由 "费用" 改为 "费用(元)";移除 `append: '元'`(因表单走自定义 slot,该属性本不生效,但清理避免歧义)。
|
||||||
|
- `src/views/vehicle/annual-inspection-record.vue`:自定义 `#feeForm` 的 `<el-input>` 移除 `<template #append>元</template>`。
|
||||||
|
- 结论:新增/编辑弹窗中费用字段 label 显示为"费用(元)",输入框不再带右侧"元" suffix。
|
||||||
|
|
||||||
|
## 维修记录里程字段单位放 label(动态单位)
|
||||||
|
- 需求:用户要求 `maintenance-record` 把里程输入框右侧"公里" suffix 移到 label 后括起来,风格与"费用(元)"一致。
|
||||||
|
- 差异:该字段有单位切换(车辆→公里,船舶→海里),故 label 需动态。
|
||||||
|
- 改动文件:`src/views/vehicle/maintenance-record.vue`(option 内联在 data 中,无独立 option 文件):
|
||||||
|
- 默认 `label` 由 "里程/航程数" 改为 "里程/航程数(公里)"。
|
||||||
|
- `updateVehicleType()` 中原 `mileageColumn.append = mileageUnit;` 改为 `mileageColumn.label = \`里程/航程数(${mileageUnit})\``,跟随车船类型动态切换标签(车辆→公里 / 船舶→海里)。
|
||||||
|
- 自定义 `#mileageForm` 的 `<el-input>` 移除 `<template #append>{{ form.mileageUnit || '公里' }}</template>`。
|
||||||
|
- 结论:表单/列头 label 随车船类型显示"里程/航程数(公里)"或"里程/航程数(海里)",输入框不再带单位 suffix;列表数据值仍由 `#mileage` slot 的 `formatMileage()` 带单位显示(如"100 公里"),未改动。
|
||||||
|
|
||||||
|
## 全站 Avue CRUD 弹窗标题加页面全称
|
||||||
|
- 需求:新增/编辑/查看弹窗标题统一加全名称,如"新增空港机场主数据"(原只显示"新增"/"编辑"/"查看")。
|
||||||
|
- 根因:Avue 弹窗标题默认走 i18n 的 addTitle/editTitle/viewTitle,全站页面均未配置,故只有动作词。
|
||||||
|
- 实现:新增全局 mixin `src/utils/crud-dialog-title.js` 的 `setupCrudDialogTitle(app)`,在 `main.js` 中 `app.mount` 前调用注册;组件 `created` 时自动给含 `column` 数组的 `this.option` 注入 `addTitle/editTitle/viewTitle = 新增/编辑/查看 + 页面菜单中文标题`(来源 `this.$router.$avueRouter.generateTitle(this.$route,{label:'name'})`);页面可手动配置这三个字段覆盖,或定义 `getCrudPageTitle()` 自定义来源;已配置则不覆盖。
|
||||||
|
- 影响范围:全站约 80 个 avue-crud 页面自动生效;手写 el-dialog / PageAvueForm 独立表单页不受影响。
|
||||||
|
- 校验:dev 服务器 localhost:2889 正常(200)。HMR 热更新生效,无需重启。
|
||||||
|
|
||||||
|
## 合同管理独立表单页标题补漏
|
||||||
|
- 需求:用户反馈 `business/contract-manage/form` 独立表单页顶部没有像 `project-apply/form` 一样的"新增合同管理"标题。
|
||||||
|
- 根因:contract-manage 是手写表单页(非 business-crud-page),页面内已有 `formPageTitle` computed(取 `query.name` 或默认"新增/编辑合同管理"),但模板里未渲染。
|
||||||
|
- 修复:在 `src/views/business/contract-manage.vue` 的表单页 `<template v-else>` 中、`<el-form>` 前加入 `<div class="archive-page-form__title">{{ formPageTitle }}</div>`,复用全局 `archive-page-form__title` 样式。
|
||||||
|
- 结论:contract-manage/form 现在顶部显示"新增合同管理"/"编辑合同管理",与 project-apply 风格一致。
|
||||||
|
|
||||||
|
## formal-settlement 表格包进应付/应收 tabs
|
||||||
|
- 需求:结算-正式结算页(/settlement/formal-settlement)表格要包进"应付/应收"两个 tab 内(原 el-tabs 的 pane 是空壳,表格在 el-tabs 外作为兄弟节点)。
|
||||||
|
- 实现:复用已有组件 `src/views/settlement/components/formal-settlement-table-panel.vue`(工具栏+表格+分页一体,通过 emit 事件与父通信),分别挂入"应付""应收"两个 `el-tab-pane` 内,共享父级 `rows/columns/loading/page/hasPermissionFn`,事件回连:@create→openCreate、@payment→openPaymentDialog、@sync→handleSync、@export→handleExport、@refresh→loadTable、@page-change→onPageChange、@action→onAction(分发 view/edit/delete/submit/approve/return/void/print)、@update:selection→selection 同步。切 tab 仍走 `handleSettlementTypeChange` 重载数据。
|
||||||
|
- 配套改动:父页 `:has-permission` 传 `hasPermissionFn`(computed 返回箭头函数,避免方法作 prop 时 this 丢失导致权限恒为 true);`handlePrint(row)` 增加可选 row 入参;移除父页内联表格及对应 scoped 样式(.formal-page__toolbar/links/pagination);组件操作列 width 120→320(符合 AGENTS.md >3 按钮规范)。
|
||||||
|
- 校验:@vue/compiler-sfc parse/compileTemplate 0 错误、esbuild 脚本语法 OK;`pnpm build` 因沙箱保护 dist/ 被拦截(非代码问题)。仅改 formal-settlement.vue 一个页面文件 + 组件 width。
|
||||||
|
|||||||
+36
-47
@@ -1,63 +1,52 @@
|
|||||||
# 项目长期记忆(tms-erp-web-ws / Saber3 客商模块)
|
# 项目长期记忆(tms-erp-web-ws / Saber3 客商模块)
|
||||||
|
|
||||||
## 设计约定(经用户确认的决策)
|
## 设计约定(已确认)
|
||||||
|
|
||||||
### 弹窗分组:统一用全局 `<section-card>` 组件
|
### 弹窗分组:统一用全局 `<section-card>`
|
||||||
- 整弹窗 body 由 `src/styles/element-ui.scss` 全局 `.el-dialog__body { background:#f5f6fa; padding:16px }` 统一灰底;每组 = 一张白底卡片(4px 主色竖条 + 8px 间距卡头 + 白底 body,圆角 6px + 极淡阴影)。
|
- 弹窗 body 背景由 `src/styles/element-ui.scss` 全局 `.el-dialog__body { background:#f5f6fa; padding:16px }` 统一灰底。
|
||||||
- 组件已全局注册 `<section-card title="...">`,支持 `#title` / `#extra` slot。严禁再手写 `.xxx-form__block` + `.dialog-section-title` 拼凑卡片。
|
- 每组 = 白底卡片:4px 主色竖条 + 8px 间距 + 圆角 6px + 淡阴影。组件全局注册 `<section-card title="...">`,支持 `#title` / `#extra` slot。
|
||||||
- 仅 3 项的小分组用 `el-col :span="8"` 均分一行。
|
- 仅 3 项的小分组用 `el-col :span="8"` 均分一行。
|
||||||
|
|
||||||
### Avue 内置弹窗内用 section-card:需 dialogCustomClass 透明化 avue-form
|
### Avue 内置弹窗用 section-card:dialogCustomClass 透明化 avue-form
|
||||||
- 全站 `.el-dialog .avue-form { background:#fff; padding:8px 16px 4px }` 会把表单整体包白卡,内置弹窗放 section-card 会白卡叠白卡。
|
- option 顶层加 `dialogCustomClass:'xxx-dialog'`,再在 `element-ui.scss` 写三条:
|
||||||
- 解法:option 顶层加 `dialogCustomClass:'xxx-dialog'`,再在 element-ui.scss 写三条:
|
1. `.xxx-dialog .avue-form { background:transparent; box-shadow:none; padding:0 }`
|
||||||
① `.xxx-dialog .avue-form { background:transparent; box-shadow:none; padding:0 }`
|
2. `.xxx-dialog .avue-form__group > .el-col > .el-form-item { margin-bottom:0 }`
|
||||||
② `.xxx-dialog .avue-form__group > .el-col > .el-form-item { margin-bottom:0 }`(防 form-item margin 与卡距叠加)
|
3. `.xxx-dialog .avue-dialog__footer, .avue-crud__dialog .xxx-dialog .avue-dialog__footer { margin-top:0 !important }`
|
||||||
③ `.xxx-dialog .avue-dialog__footer, .avue-crud__dialog .xxx-dialog .avue-dialog__footer { margin-top:0 !important }`(抵消全局 947 行 `-30px !important` 上移)。
|
|
||||||
|
|
||||||
### 弹窗底部操作栏浮动(全站统一,全局 CSS 实现)
|
### 弹窗底部操作栏浮动(全局 CSS)
|
||||||
- `.el-dialog__body { max-height: calc(100vh - 200px); overflow-y:auto; overflow-x:hidden }`;`.el-dialog__footer { margin:0; padding:12px 20px; background:#fff; border-top:1px solid #ebeef5; box-shadow:0 -2px 8px rgba(0,0,0,.04) }`。
|
- `.el-dialog__body { max-height:calc(100vh - 200px); overflow-y:auto; overflow-x:hidden }`
|
||||||
- **易踩坑**:Avue 内置弹窗 footer 真实 class 是 `.avue-dialog__footer`(非 `.el-dialog__footer`),且渲染在 `.el-dialog__body` 内、随 body 滚动滚出视口。要让它贴底须 `position:sticky; bottom:0; flex:none; background:#fff`(配合 body flex 列布局 + overflow-y:auto)。手写 el-dialog 的 `.el-dialog__footer` 在 body 外天然可见。
|
- `.el-dialog__footer { margin:0; padding:12px 20px; background:#fff; border-top:1px solid #ebeef5; box-shadow:0 -2px 8px rgba(0,0,0,.04) }`
|
||||||
- **弹窗高度链**:① `.avue-dialog`(库内置)`max-height:calc(100% - 200px)` 控制整体上限;② `.avue-crud__dialog .el-dialog__body { max-height:calc(100vh - 200px); overflow-y:auto }`(451-457 行);③ 专项弹窗(shipping-template-dialog / transport-plan-dialog)`.el-dialog` 自身 `margin-top:4vh; height/max-height:calc(100vh-4vh-24px); display:flex; flex-direction:column` + header flex:none / body flex:1 1 auto; min-height:0; overflow-y:auto; max-height:none!important / footer sticky。**改这类弹窗尺寸只改 section-card 周边规则,不要动 body 内 max-height。**
|
- Avue 内置弹窗 footer 真实 class 是 `.avue-dialog__footer`,须 `position:sticky; bottom:0; flex:none; background:#fff` 贴底。
|
||||||
|
|
||||||
### 上传证件区域尺寸规范(transportCapacity 强制)
|
### 上传证件区域尺寸(transportCapacity)
|
||||||
- 二代身份证 85.6×53.98mm → 宽:高 ≈ **1.586:1**;取 ~75% → 固定 **240×151px**,el-col 内 `margin:0` 左对齐。
|
- 身份证/行驶证/道路运输证等固定 **240×151px**(1.586:1)。
|
||||||
- 全 `<image-upload-field large>` 适用(身份证/行驶证/道路运输证/登记本/船舶各证)。每页 scoped 覆盖 `.xxx-uploader--large`(`width:240px`)与 `--large .el-upload`(`width:100%; height:151px`);注意 `.xxx-uploader` 须写在 `.xxx-uploader--large` 之前(同特定性,源码顺序决胜)。
|
- scoped 覆盖 `.xxx-uploader--large { width:240px }` 与 `--large .el-upload { width:100%; height:151px }`。
|
||||||
|
|
||||||
### 上传图片点击放大预览(全局组件 `image-upload-field`)
|
### 上传图片点击放大预览
|
||||||
- `src/components/image-upload-field/main.vue`:裸 `<img>` → `<el-image :preview-src-list="[value]" fit="contain" preview-teleported :z-index="3000" @click.stop>`。图片 `@click.stop` 阻止冒泡到 el-upload(否则重传);"更换"经隐藏 `input.el-upload__input` 的 `.click()`(el-upload 无 openFileDialog)。readonly 仅预览。driver/vehicle/ship 三页自动获得。z-index 必须 3000 + teleported 否则被 dialog 遮罩盖住。
|
- `src/components/image-upload-field/main.vue` 用 `<el-image preview-src-list fit="contain" preview-teleported :z-index="3000" @click.stop>`,阻止冒泡到 el-upload。
|
||||||
|
|
||||||
### 独立表单页标题 `archive-page-form__title`(全局统一,参考 customer-archive)
|
### 独立表单页标题 `archive-page-form__title`
|
||||||
- 样式已**全局化**到 `src/styles/element-ui.scss`(5b 段):18px/600/#303133 + margin-bottom:20px + `::before` 4px 主色竖条 + 8px 间距。原 `customer-archive.vue`(scoped)与 `business-crud-page.vue`(`:global`)两处本地重复定义已删除,改由全局规则统一驱动(全站任意页面用此 class 即得统一标题)。
|
- 样式全局化到 `src/styles/element-ui.scss`:18px/600/#303133 + margin-bottom:20px + 4px 主色竖条 + 8px 间距。
|
||||||
- **business-crud-page 系列**(shipping-template / transport-plan / waybill-manage / contract-manage 独立表单页):`PageAvueForm`(render `h('div',{class:dialogCustomClass},[titleDiv, avue-form])`)声明 `props.formPageTitle`,模板 `:form-page-title="isStandaloneFormPage ? formPageTitle : undefined"`;computed `formPageTitle()` 在 `isStandaloneFormPage && (isShippingTemplatePage || isTransportPlanPage || isWaybillDetailLayout || isContractConfig)` 时返回 `this.$route.query.name || '新增'/'编辑'+config.title`(`isWaybillDetailLayout` = `config.permission==='waybill_manage'`;`isContractConfig` = `config.permission==='contract_manage'`)。
|
- business-crud-page 系列通过 `formPageTitle` prop 注入;loading-manage / master-order 手写前置标题。
|
||||||
- **loading-manage/form**:路由直接映射 `loading-manage.vue`(非 business-crud-page)。新增 computed `formPageTitle()`(`query.name || '新增/编辑配载管理'`),并在 `<component :is="isStandaloneFormPage?'div':'el-dialog'">` 内、body 前加 `<div v-if="isStandaloneFormPage" class="archive-page-form__title">`。
|
|
||||||
- **master-order?mode=editor**:`master-order.vue` 把 `<master-order-editor>` 包进 `<template v-else-if="mode==='editor'">`,前置 `<div class="archive-page-form__title">{{ masterEditorTitle }}</div>`(computed `masterEditorTitle` = `routeId ? '编辑总单' : '新增总单'`)。
|
|
||||||
- 扩展原则:新增独立表单页要带此标题,要么并入 business-crud-page 的 `formPageTitle` 条件(business-crud-page 系列),要么在对应页面模板手写 `<div class="archive-page-form__title">`(loading-manage / master-order 模式)。
|
|
||||||
|
|
||||||
### 表格列内嵌输入控件:全宽(全局 CSS,不动手写 scoped)
|
### 表格列内嵌输入控件全宽
|
||||||
- `element-ui.scss` 在 660-678 行加了一条全局规则:`.el-table .el-table__body td.el-table__cell .cell > .el-input-number/.el-input/.el-select/.el-date-editor/.el-cascader { width:100%; --el-*-width:100% }`(带对应 `--el-input-number-width` 等变量兜底)。
|
- `element-ui.scss` 全局兜底:`.el-table .el-table__body td.el-table__cell .cell > .el-input-number/.el-input/.el-select/.el-date-editor/.el-cascader { width:100% }`。
|
||||||
- 适用场景:可编辑 el-table 列、弹窗内编辑表格。原先容易踩坑的是 el-plus 的 el-input-number 默认根 width=150px,cell 给到 min-width=150 后输入框只占自然宽度、看着"显示不全"。**新增任何"el-table cell 内嵌 el-input-number/el-input/el-select"需求不用再写 scoped 宽度**——已全局兜底。
|
|
||||||
- 与"表单内 250px 全宽"互不冲突:本规则只命中 `.el-table .el-table__body td`,表单内 `.el-form-item__content >` 的 250px 规则仍生效。
|
|
||||||
- 已覆盖:pre-settlement-editor「结算明细调整」弹窗(adjustDialog 列内 el-input-number/el-input 全宽)、transport-reconciliation-editor/formal-settlement-editor/settlement-adjustment-editor 同款结构。
|
|
||||||
|
|
||||||
### 业务状态标签文字化:全局 `.status-text` 工具类
|
### 业务状态标签文字化
|
||||||
- 需求:全站"调度状态/运单状态/审批状态"等用 `el-tag` 展示的字段,统一改为**普通文字,不带颜色、不显示 tag 背景/边框/圆点**。
|
- 全站"状态语义"的 `el-tag` 加 `class="status-text"`,转为普通文字,不带颜色/背景/边框/圆点。
|
||||||
- 实现:`element-ui.scss` 末尾 `.el-tag.status-text { background:transparent!important; border-color:transparent!important; color:var(--el-text-color-regular)!important; padding:0; margin:0; font-weight:normal!important; font-size:inherit!important; height:auto!important; vertical-align:baseline; line-height:inherit }`,并隐藏 `&::before` 圆点。`el-tag` 的 `:type` 继续保留在模板中(无需改动),但颜色/字号/行高已被全局 CSS 强制覆盖为与单元格普通文本一致。
|
- 实现:`element-ui.scss` 末尾 `.el-tag.status-text { ... }`。
|
||||||
- 用法:业务状态 `el-tag` 加 `class="status-text"`。
|
|
||||||
- **全站统一完成(2026-08-25)**:所有"状态语义"的 el-tag 均已加 `class="status-text"` 转纯文本,含:business-crud-page(#status/#businessStatus/#approvalStatus + 调度弹窗头)、master-order 系列、loading-manage 详情、结算(settlement-adjustment/pre-settlement/formal-settlement/formal-settlement-table-panel/transport-reconciliation-editor 匹配结果·状态)、payment(invoice-application/bill-payment-table/receipt-flow/receipt-claim-record/invoice-receipt/payment-application 审批·认领状态)、contract-manage(合同阶段 + 审批)、temporary-credit-limit、process-config、credit-score-quantification、flow(manager/follow 激活·挂起)、work(start 激活·挂起 / send 完成)、transportCapacity(vehicle 启用·认证 / driver / ship 启用)、base(railway-station/airport-master/currency/customer-type/fee-item/port-terminal 启用停用)、system(dept/authority apikey·ratelimit 启用禁用 / authlock lockStatus / dict·dictbiz 是否封存 / codesetting 否是)、resource(sms/oss statusName)、customer-archive(审批 + 启用停用)、transit/risk-disposal(column.tag/field.tag)。
|
|
||||||
- 例外(刻意保留 tag 样式,非状态语义):`loading-manage.vue` 列表状态列用 `<span class="loading-manage-page__status is-${status}">`(先例);"类型标签"——transport-flow-tag、transportTypeName、business-crud-page:3252 运输方式、loading-manage:399 运输方式 warning、authlock lockType(锁类型)、vehicle-ledger plateColor/businessRelation、cron-editor、以及 categoryName/version/code/tenantId/roleName/deptName/apiKey/remoteIp/env/tableName/method/recordLevel/pattern/modelTable/fileSize/node.tags 等"值/标签/提示"类。注:customer-archive 准入类型原在例外中,2026-08-25 用户要求也转纯文本(已加 status-text)。
|
|
||||||
|
|
||||||
## 关键文件
|
## 关键文件
|
||||||
- 分组白卡:`src/components/section-card/main.vue`。
|
- `src/components/section-card/main.vue`
|
||||||
- 全局弹窗/section-card 样式:`src/styles/element-ui.scss`。
|
- `src/styles/element-ui.scss`
|
||||||
- 客商档案弹窗:`src/views/vehicle/customer-archive.vue`(用 `archive-page-form__title` 作页面标题,`:deep(.archive-dialog .el-dialog__body){max-height:72vh}`)。
|
- `src/views/vehicle/customer-archive.vue`
|
||||||
- 司机/车辆/船舶档案:`src/views/transportCapacity/{driver,vehicle,ship}.vue`(手写 el-form + section-card + 240×151 证件图)。
|
- `src/views/transportCapacity/{driver,vehicle,ship}.vue`
|
||||||
- 项目补录弹窗:`src/views/business/project-apply.vue`(手写 el-dialog + #footer 浮动,视觉参照)。
|
- `src/views/business/project-apply.vue`
|
||||||
|
|
||||||
## 环境注意
|
## 环境注意
|
||||||
- Vite 代理跨域:axios `withCredentials=true`,直连绝对地址后端须 CORS `Allow-Origin` 为具体域名(不能用 `*`)。改 `.env`/`vite.config.mjs` 必须重启 dev。
|
- Vite 代理跨域:`axios.withCredentials=true`,改 `.env`/`vite.config.mjs` 须重启 dev。
|
||||||
|
|
||||||
## business-crud-page 复用模式(被 waybill/transport-plan/shipping-template/contract 共用)
|
## business-crud-page 复用模式
|
||||||
- option 由 prop `crudOption` 经 `cloneOption` 克隆(保留所有顶层属性)。给各页 option 顶层加 `dialogCustomClass:'xxx-dialog'` + element-ui.scss `.xxx-dialog.el-dialog{height/max-height:calc(100vh - 40px)!important}` 可只影响该页。
|
- option 由 prop `crudOption` 经 `cloneOption` 克隆。
|
||||||
- avue-crud 弹窗带 `avue-crud__dialog` class,受全局 `.avue-crud__dialog .avue-dialog__footer{margin-top:-30px!important}` 影响,自定义 footer 须加 `margin-top:0!important` 抵消。
|
- 独立表单页走 `PageAvueForm`,不包 el-dialog。
|
||||||
- 独立表单页(contract-manage / shipping-template / waybill / transport-plan)走 `PageAvueForm`,不包 el-dialog,底部按钮随内容滚动不浮动。`pageFormOption`(6205 行)负责装配。contract 用 `buildContractFormGroupOption`(6861 行)按 order 分 7 组白卡。
|
- 自定义详情弹窗:`config.detailButton=true` + `detailSections:[{title, fields}]`。
|
||||||
- **查看详情弹窗(detailBox)自定义机制**:列表"查看/详情"按钮默认调 Avue 内置 `rowView`(列数复用 column.span,常呈 3 列)。要改成自定义两列白卡详情,在页面 `config` 加 `detailButton:true` + `detailSections:[{title, fields:[[prop,label,span],...]}]`,组件渲染 `section-card` + `el-descriptions(:column=2)`(field 默认占 1 列,span:2 整行)。数据经 `api.getDetail(row.id)` 拉取,值用 `formatDetailValue` 经 column.dicData 转字典显示 label。开关:`showDetailButton` 默认仅非草稿态显示"详情"(草稿态仍走 rowView 3 列);加 `config.detailButtonIgnoreDraftStatus:true` 可让所有状态(含草稿)都走自定义两列详情。statusProp='approvalStatus' 的页面草稿值须为 'draft'。
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { getScreen, findColumn } from './utils/util';
|
|||||||
import { setupPaginationEnhancer } from './utils/pagination';
|
import { setupPaginationEnhancer } from './utils/pagination';
|
||||||
import { setupDialogFormLabelAlign } from './utils/dialog-form-label';
|
import { setupDialogFormLabelAlign } from './utils/dialog-form-label';
|
||||||
import { setupTableColumnDrag } from './utils/table-column-drag';
|
import { setupTableColumnDrag } from './utils/table-column-drag';
|
||||||
|
import { setupCrudDialogTitle } from './utils/crud-dialog-title';
|
||||||
import './permission';
|
import './permission';
|
||||||
import error from './error';
|
import error from './error';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
@@ -109,6 +110,7 @@ app.use(VueClipboard, {
|
|||||||
appendToBody: true, // 这可以帮助解决一些更复杂的使用场景下的问题
|
appendToBody: true, // 这可以帮助解决一些更复杂的使用场景下的问题
|
||||||
});
|
});
|
||||||
setupTableColumnDrag(app);
|
setupTableColumnDrag(app);
|
||||||
|
setupCrudDialogTitle(app);
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
setupPaginationEnhancer();
|
setupPaginationEnhancer();
|
||||||
setupDialogFormLabelAlign();
|
setupDialogFormLabelAlign();
|
||||||
|
|||||||
@@ -142,11 +142,10 @@ export const option = {
|
|||||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '费用',
|
label: '费用(元)',
|
||||||
prop: 'fee',
|
prop: 'fee',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
formslot: true,
|
formslot: true,
|
||||||
append: '元',
|
|
||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
span: 12,
|
span: 12,
|
||||||
placeholder: '请输入',
|
placeholder: '请输入',
|
||||||
|
|||||||
@@ -1049,3 +1049,6 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.el-select__tags-text {
|
||||||
|
font-size: var(--el-form-label-font-size);
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 全站 Avue CRUD 新增/编辑/查看弹窗标题自动注入
|
||||||
|
*
|
||||||
|
* 默认情况下 Avue 弹窗标题走 i18n,只显示「新增 / 编辑 / 查看」。
|
||||||
|
* 本 mixin 在组件创建时自动根据当前菜单中文标题拼出完整标题,例如:
|
||||||
|
* 「新增空港机场主数据」/「编辑空港机场主数据」/「查看空港机场主数据」
|
||||||
|
*
|
||||||
|
* 标题来源优先级:
|
||||||
|
* 1. 页面自定义方法 getCrudPageTitle()(若存在)
|
||||||
|
* 2. 当前路由中文菜单标题(this.$router.$avueRouter.generateTitle)
|
||||||
|
*
|
||||||
|
* 若页面已在 option 中手动配置了 addTitle / editTitle / viewTitle,则不覆盖。
|
||||||
|
*/
|
||||||
|
export function setupCrudDialogTitle(app) {
|
||||||
|
app.mixin({
|
||||||
|
created() {
|
||||||
|
if (!this.option || typeof this.option !== 'object') return;
|
||||||
|
if (!Array.isArray(this.option.column)) return;
|
||||||
|
if (this.option._dialogTitleInjected) return;
|
||||||
|
|
||||||
|
const getTitle =
|
||||||
|
typeof this.getCrudPageTitle === 'function'
|
||||||
|
? this.getCrudPageTitle
|
||||||
|
: () => {
|
||||||
|
const router = this.$router;
|
||||||
|
if (router && router.$avueRouter) {
|
||||||
|
return router.$avueRouter.generateTitle(this.$route, { label: 'name' });
|
||||||
|
}
|
||||||
|
return (this.$route && this.$route.meta && this.$route.meta.title) || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const title = getTitle();
|
||||||
|
if (!title) return;
|
||||||
|
|
||||||
|
this.option.addTitle = this.option.addTitle || `新增${title}`;
|
||||||
|
this.option.editTitle = this.option.editTitle || `编辑${title}`;
|
||||||
|
this.option.viewTitle = this.option.viewTitle || `查看${title}`;
|
||||||
|
this.option._dialogTitleInjected = true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -2165,6 +2165,7 @@
|
|||||||
show-close
|
show-close
|
||||||
:class="[
|
:class="[
|
||||||
'business-crud-page__detail-dialog',
|
'business-crud-page__detail-dialog',
|
||||||
|
$attrs.option?.dialogCustomClass,
|
||||||
{ 'is-transport-plan-detail': isTransportPlanDetailLayout },
|
{ 'is-transport-plan-detail': isTransportPlanDetailLayout },
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -106,6 +106,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
<div class="archive-page-form__title">{{ formPageTitle }}</div>
|
||||||
<el-form
|
<el-form
|
||||||
ref="contractForm"
|
ref="contractForm"
|
||||||
:model="form"
|
:model="form"
|
||||||
@@ -2164,7 +2165,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.contract-manage-form {
|
.contract-manage-form {
|
||||||
padding: 12px 16px 24px;
|
//padding: 12px 16px 24px;
|
||||||
background: #f5f6fa;
|
background: #f5f6fa;
|
||||||
|
|
||||||
&__section {
|
&__section {
|
||||||
|
|||||||
@@ -1072,7 +1072,7 @@ export default {
|
|||||||
editBtn: false,
|
editBtn: false,
|
||||||
delBtn: false,
|
delBtn: false,
|
||||||
// 操作列收窄:本项目操作按钮较少,320px 过宽,调整为 220px(链接会自动换行兜底)
|
// 操作列收窄:本项目操作按钮较少,320px 过宽,调整为 220px(链接会自动换行兜底)
|
||||||
menuWidth: 180,
|
menuWidth: 200,
|
||||||
column: (option.column || []).map(column => ({
|
column: (option.column || []).map(column => ({
|
||||||
...column,
|
...column,
|
||||||
display: false,
|
display: false,
|
||||||
|
|||||||
@@ -666,4 +666,8 @@ export default {
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.temporary-credit-limit-dialog .business-crud-page__detail-content .el-descriptions__label {
|
||||||
|
width: 180px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<basic-container class="claim-record-page">
|
<basic-container class="claim-record-page">
|
||||||
<section class="claim-record-page__search">
|
<section-card class="claim-record-page__search">
|
||||||
<el-form :model="query" label-position="right" label-width="160px" @submit.prevent>
|
<el-form :model="query" label-position="right" label-width="160px" @submit.prevent>
|
||||||
<div class="claim-record-page__search-grid">
|
<div class="claim-record-page__search-grid">
|
||||||
<el-form-item label="认领通知单">
|
<el-form-item label="认领通知单">
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
</section>
|
</section-card>
|
||||||
|
|
||||||
<section class="claim-record-page__table-panel">
|
<section class="claim-record-page__table-panel">
|
||||||
<el-table v-loading="loading" :data="rows" border>
|
<el-table v-loading="loading" :data="rows" border>
|
||||||
@@ -93,7 +93,11 @@
|
|||||||
<el-link v-if="column.link" type="primary" @click="openDetail(row)">
|
<el-link v-if="column.link" type="primary" @click="openDetail(row)">
|
||||||
{{ displayValue(row[column.prop]) }}
|
{{ displayValue(row[column.prop]) }}
|
||||||
</el-link>
|
</el-link>
|
||||||
<el-tag v-else-if="column.status" :type="statusType(row.claimStatus)" class="status-text">
|
<el-tag
|
||||||
|
v-else-if="column.status"
|
||||||
|
:type="statusType(row.claimStatus)"
|
||||||
|
class="status-text"
|
||||||
|
>
|
||||||
{{ displayValue(row[column.prop]) }}
|
{{ displayValue(row[column.prop]) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
<span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
|
||||||
@@ -133,6 +137,7 @@ import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import * as api from '@/api/payment/receiptClaimRecord';
|
import * as api from '@/api/payment/receiptClaimRecord';
|
||||||
import { receiptClaimRecordTableColumns } from '@/option/payment/receiptClaimRecord';
|
import { receiptClaimRecordTableColumns } from '@/option/payment/receiptClaimRecord';
|
||||||
|
import SectionCard from '@/components/section-card/main.vue';
|
||||||
|
|
||||||
const emptyQuery = () => ({
|
const emptyQuery = () => ({
|
||||||
receiptNoticeNo: '',
|
receiptNoticeNo: '',
|
||||||
@@ -150,6 +155,7 @@ const emptyQuery = () => ({
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ReceiptClaimRecord',
|
name: 'ReceiptClaimRecord',
|
||||||
|
components: { SectionCard },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ArrowDown,
|
ArrowDown,
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="120" fixed="right" align="center">
|
<el-table-column label="操作" width="320" fixed="right" align="center">
|
||||||
<template #default="{ row }"
|
<template #default="{ row }"
|
||||||
><div class="fs-table-panel__links">
|
><div class="fs-table-panel__links">
|
||||||
<el-link
|
<el-link
|
||||||
|
|||||||
@@ -46,149 +46,41 @@
|
|||||||
v-model="activeSettlementType"
|
v-model="activeSettlementType"
|
||||||
@tab-change="handleSettlementTypeChange"
|
@tab-change="handleSettlementTypeChange"
|
||||||
>
|
>
|
||||||
<el-tab-pane label="应付" name="payable" />
|
<el-tab-pane label="应付" name="payable">
|
||||||
<el-tab-pane label="应收" name="receivable" />
|
<formal-settlement-table-panel
|
||||||
</el-tabs>
|
:rows="rows"
|
||||||
<div class="formal-page__toolbar">
|
:columns="columns"
|
||||||
<div>
|
:loading="loading"
|
||||||
<el-button
|
:page="page"
|
||||||
v-if="hasPermission('formal_settlement_add')"
|
:has-permission="hasPermissionFn"
|
||||||
type="primary"
|
@create="openCreate"
|
||||||
@click="openCreate"
|
@payment="openPaymentDialog"
|
||||||
>新增</el-button
|
@sync="handleSync"
|
||||||
>
|
@export="handleExport"
|
||||||
<el-button
|
@refresh="loadTable"
|
||||||
v-if="hasPermission('formal_settlement_payment')"
|
@page-change="onPageChange"
|
||||||
type="primary"
|
@action="onAction"
|
||||||
plain
|
@update:selection="selection = $event"
|
||||||
disabled
|
|
||||||
@click="openPaymentDialog"
|
|
||||||
>付款申请</el-button
|
|
||||||
>
|
|
||||||
<el-button
|
|
||||||
v-if="hasPermission('formal_settlement_sync')"
|
|
||||||
type="primary"
|
|
||||||
plain
|
|
||||||
disabled
|
|
||||||
@click="handleSync"
|
|
||||||
>同步金蝶</el-button
|
|
||||||
>
|
|
||||||
<el-button
|
|
||||||
v-if="hasPermission('formal_settlement_print')"
|
|
||||||
type="primary"
|
|
||||||
plain
|
|
||||||
@click="handlePrint"
|
|
||||||
>打印结算单</el-button
|
|
||||||
>
|
|
||||||
<el-button
|
|
||||||
v-if="hasPermission('formal_settlement_export')"
|
|
||||||
type="primary"
|
|
||||||
plain
|
|
||||||
@click="handleExport"
|
|
||||||
>导出</el-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="formal-page__toolbar-right">
|
|
||||||
<el-tooltip content="刷新" placement="top">
|
|
||||||
<el-button :icon="Refresh" text @click="loadTable" />
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-table v-loading="loading" :data="rows" border @selection-change="selection = $event">
|
|
||||||
<el-table-column type="selection" width="52" fixed="left" align="center" />
|
|
||||||
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
|
|
||||||
<el-table-column
|
|
||||||
v-for="column in columns"
|
|
||||||
:key="column.prop"
|
|
||||||
v-bind="column"
|
|
||||||
align="center"
|
|
||||||
show-overflow-tooltip
|
|
||||||
>
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-link v-if="column.link && row[column.prop]" type="primary" @click="openView(row)">{{
|
|
||||||
row[column.prop]
|
|
||||||
}}</el-link>
|
|
||||||
<el-tag v-else-if="column.status" :type="statusType(row.approvalStatus)" class="status-text">{{
|
|
||||||
displayValue(row[column.prop])
|
|
||||||
}}</el-tag>
|
|
||||||
<span v-else-if="column.money">{{ formatMoney(row[column.prop], row.currency) }}</span>
|
|
||||||
<span v-else-if="column.prop === 'invoiceStatusName'">{{
|
|
||||||
invoiceName(row.invoiceStatus)
|
|
||||||
}}</span>
|
|
||||||
<span v-else-if="column.prop === 'paymentStatusName'">{{
|
|
||||||
paymentName(row.paymentStatus)
|
|
||||||
}}</span>
|
|
||||||
<span v-else>{{ displayValue(row[column.prop]) }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="180" fixed="right" align="center">
|
|
||||||
<template #default="{ row }"
|
|
||||||
><div class="formal-page__links">
|
|
||||||
<el-link
|
|
||||||
v-if="hasPermission('formal_settlement_view')"
|
|
||||||
type="primary"
|
|
||||||
@click="openView(row)"
|
|
||||||
>查看</el-link
|
|
||||||
>
|
|
||||||
<el-link
|
|
||||||
v-if="hasPermission('formal_settlement_edit') && isEditable(row)"
|
|
||||||
type="primary"
|
|
||||||
@click="openEdit(row)"
|
|
||||||
>编辑</el-link
|
|
||||||
>
|
|
||||||
<el-link
|
|
||||||
v-if="hasPermission('formal_settlement_delete') && row.approvalStatus === 'draft'"
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(row)"
|
|
||||||
>删除</el-link
|
|
||||||
>
|
|
||||||
<el-link
|
|
||||||
v-if="hasPermission('formal_settlement_submit') && isEditable(row)"
|
|
||||||
type="primary"
|
|
||||||
@click="handleSubmit(row)"
|
|
||||||
>提交</el-link
|
|
||||||
>
|
|
||||||
<el-link
|
|
||||||
v-if="
|
|
||||||
hasPermission('formal_settlement_approve') && row.approvalStatus === 'reviewing'
|
|
||||||
"
|
|
||||||
type="primary"
|
|
||||||
@click="handleApprove(row)"
|
|
||||||
>通过</el-link
|
|
||||||
>
|
|
||||||
<el-link
|
|
||||||
v-if="
|
|
||||||
hasPermission('formal_settlement_approve') && row.approvalStatus === 'reviewing'
|
|
||||||
"
|
|
||||||
type="danger"
|
|
||||||
@click="handleReturn(row)"
|
|
||||||
>驳回</el-link
|
|
||||||
>
|
|
||||||
<el-link
|
|
||||||
v-if="
|
|
||||||
hasPermission('formal_settlement_void') &&
|
|
||||||
row.approvalStatus === 'approved' &&
|
|
||||||
row.kingdeeSyncStatus !== 'synced'
|
|
||||||
"
|
|
||||||
type="danger"
|
|
||||||
@click="handleVoid(row)"
|
|
||||||
>作废</el-link
|
|
||||||
>
|
|
||||||
</div></template
|
|
||||||
>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<div class="formal-page__pagination">
|
|
||||||
<el-pagination
|
|
||||||
v-model:current-page="page.current"
|
|
||||||
v-model:page-size="page.size"
|
|
||||||
:total="page.total"
|
|
||||||
:page-sizes="[10, 20, 50, 100]"
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
@current-change="loadTable"
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="应收" name="receivable">
|
||||||
|
<formal-settlement-table-panel
|
||||||
|
:rows="rows"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="loading"
|
||||||
|
:page="page"
|
||||||
|
:has-permission="hasPermissionFn"
|
||||||
|
@create="openCreate"
|
||||||
|
@payment="openPaymentDialog"
|
||||||
|
@sync="handleSync"
|
||||||
|
@export="handleExport"
|
||||||
|
@refresh="loadTable"
|
||||||
|
@page-change="onPageChange"
|
||||||
|
@action="onAction"
|
||||||
|
@update:selection="selection = $event"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
</section>
|
</section>
|
||||||
<formal-settlement-editor
|
<formal-settlement-editor
|
||||||
v-model="editor.visible"
|
v-model="editor.visible"
|
||||||
@@ -239,7 +131,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ArrowDown, ArrowUp, Refresh } from '@element-plus/icons-vue';
|
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import * as api from '@/api/settlement/formalSettlement';
|
import * as api from '@/api/settlement/formalSettlement';
|
||||||
@@ -250,6 +142,7 @@ import {
|
|||||||
} from '@/option/settlement/formalSettlementSearch';
|
} from '@/option/settlement/formalSettlementSearch';
|
||||||
import { formalSettlementTableColumns } from '@/option/settlement/formalSettlementTable';
|
import { formalSettlementTableColumns } from '@/option/settlement/formalSettlementTable';
|
||||||
import FormalSettlementEditor from './components/formal-settlement-editor.vue';
|
import FormalSettlementEditor from './components/formal-settlement-editor.vue';
|
||||||
|
import FormalSettlementTablePanel from './components/formal-settlement-table-panel.vue';
|
||||||
|
|
||||||
const emptyQuery = () => ({
|
const emptyQuery = () => ({
|
||||||
formalSettlementNo: '',
|
formalSettlementNo: '',
|
||||||
@@ -268,12 +161,11 @@ const emptyQuery = () => ({
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormalSettlement',
|
name: 'FormalSettlement',
|
||||||
components: { FormalSettlementEditor },
|
components: { FormalSettlementEditor, FormalSettlementTablePanel },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ArrowDown,
|
ArrowDown,
|
||||||
ArrowUp,
|
ArrowUp,
|
||||||
Refresh,
|
|
||||||
query: emptyQuery(),
|
query: emptyQuery(),
|
||||||
searchExpanded: false,
|
searchExpanded: false,
|
||||||
activeSettlementType: 'payable',
|
activeSettlementType: 'payable',
|
||||||
@@ -294,6 +186,9 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['permission']),
|
...mapGetters(['permission']),
|
||||||
|
hasPermissionFn() {
|
||||||
|
return code => this.permission?.[code] !== false;
|
||||||
|
},
|
||||||
visibleSearchFields() {
|
visibleSearchFields() {
|
||||||
return this.searchExpanded ? this.searchFields : this.searchFields.slice(0, 4);
|
return this.searchExpanded ? this.searchFields : this.searchFields.slice(0, 4);
|
||||||
},
|
},
|
||||||
@@ -339,6 +234,24 @@ export default {
|
|||||||
this.page.current = 1;
|
this.page.current = 1;
|
||||||
this.loadTable();
|
this.loadTable();
|
||||||
},
|
},
|
||||||
|
onPageChange({ current, size }) {
|
||||||
|
this.page.current = current;
|
||||||
|
this.page.size = size;
|
||||||
|
this.loadTable();
|
||||||
|
},
|
||||||
|
onAction({ type, row }) {
|
||||||
|
const handlers = {
|
||||||
|
view: () => this.openView(row),
|
||||||
|
edit: () => this.openEdit(row),
|
||||||
|
delete: () => this.handleDelete(row),
|
||||||
|
submit: () => this.handleSubmit(row),
|
||||||
|
approve: () => this.handleApprove(row),
|
||||||
|
return: () => this.handleReturn(row),
|
||||||
|
void: () => this.handleVoid(row),
|
||||||
|
print: () => this.handlePrint(row),
|
||||||
|
};
|
||||||
|
(handlers[type] || (() => {}))();
|
||||||
|
},
|
||||||
openCreate() {
|
openCreate() {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
path: '/settlement/formal-settlement/form',
|
path: '/settlement/formal-settlement/form',
|
||||||
@@ -436,9 +349,11 @@ export default {
|
|||||||
this.paymentDialog.loading = false;
|
this.paymentDialog.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handlePrint() {
|
handlePrint(row) {
|
||||||
const row = this.selectedOne('打印');
|
if (!row) {
|
||||||
|
row = this.selectedOne('打印');
|
||||||
if (!row) return;
|
if (!row) return;
|
||||||
|
}
|
||||||
const win = window.open('', '_blank');
|
const win = window.open('', '_blank');
|
||||||
if (!win) return this.$message.warning('浏览器阻止了打印窗口,请允许弹窗后重试');
|
if (!win) return this.$message.warning('浏览器阻止了打印窗口,请允许弹窗后重试');
|
||||||
win.document.write(
|
win.document.write(
|
||||||
@@ -578,28 +493,6 @@ export default {
|
|||||||
.formal-page__table-panel {
|
.formal-page__table-panel {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
.formal-page__toolbar {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 12px 0;
|
|
||||||
}
|
|
||||||
.formal-page__toolbar-right {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
.formal-page__links {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
.formal-page__pagination {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
.formal-page :deep(.el-table) {
|
.formal-page :deep(.el-table) {
|
||||||
--el-table-border-color: #eff1f7;
|
--el-table-border-color: #eff1f7;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@
|
|||||||
>
|
>
|
||||||
<section-card title="基础船舶信息">
|
<section-card title="基础船舶信息">
|
||||||
<el-row :gutter="18">
|
<el-row :gutter="18">
|
||||||
<el-col :span="6">
|
<el-col :span="8">
|
||||||
<el-form-item label="船舶所属组织" prop="organizationName">
|
<el-form-item label="船舶所属组织" prop="organizationName">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="shipForm.organizationName"
|
v-model="shipForm.organizationName"
|
||||||
@@ -160,12 +160,12 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="8">
|
||||||
<el-form-item label="船名" prop="shipName">
|
<el-form-item label="船名" prop="shipName">
|
||||||
<el-input v-model="shipForm.shipName" maxlength="20" show-word-limit />
|
<el-input v-model="shipForm.shipName" maxlength="20" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="8">
|
||||||
<el-form-item label="安放龙骨日期/建造完工日期" prop="keelLayingDate">
|
<el-form-item label="安放龙骨日期/建造完工日期" prop="keelLayingDate">
|
||||||
<div class="date-range-inline">
|
<div class="date-range-inline">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
@@ -186,71 +186,59 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="18">
|
<el-row :gutter="18">
|
||||||
<el-col :span="6">
|
<el-col :span="8">
|
||||||
<el-form-item label="总长" prop="totalLength">
|
<el-form-item label="总长(m)" prop="totalLength">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="shipForm.totalLength"
|
v-model="shipForm.totalLength"
|
||||||
@input="normalizeNonNegativeDecimal('totalLength')"
|
@input="normalizeNonNegativeDecimal('totalLength')"
|
||||||
>
|
/>
|
||||||
<template #suffix>m</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="8">
|
||||||
<el-form-item label="船宽" prop="shipWidth">
|
<el-form-item label="船宽(m)" prop="shipWidth">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="shipForm.shipWidth"
|
v-model="shipForm.shipWidth"
|
||||||
@input="normalizeNonNegativeDecimal('shipWidth')"
|
@input="normalizeNonNegativeDecimal('shipWidth')"
|
||||||
>
|
/>
|
||||||
<template #suffix>m</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="8">
|
||||||
<el-form-item label="型深" prop="moldedDepth">
|
<el-form-item label="型深(m)" prop="moldedDepth">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="shipForm.moldedDepth"
|
v-model="shipForm.moldedDepth"
|
||||||
@input="normalizeNonNegativeDecimal('moldedDepth')"
|
@input="normalizeNonNegativeDecimal('moldedDepth')"
|
||||||
>
|
/>
|
||||||
<template #suffix>m</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="18">
|
<el-row :gutter="18">
|
||||||
<el-col :span="6">
|
<el-col :span="8">
|
||||||
<el-form-item label="最大船高" prop="maxShipHeight">
|
<el-form-item label="最大船高(m)" prop="maxShipHeight">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="shipForm.maxShipHeight"
|
v-model="shipForm.maxShipHeight"
|
||||||
@input="normalizeNonNegativeDecimal('maxShipHeight')"
|
@input="normalizeNonNegativeDecimal('maxShipHeight')"
|
||||||
>
|
/>
|
||||||
<template #suffix>m</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="8">
|
||||||
<el-form-item label="空载吃水" prop="lightDraft">
|
<el-form-item label="空载吃水(t)" prop="lightDraft">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="shipForm.lightDraft"
|
v-model="shipForm.lightDraft"
|
||||||
@input="normalizeNonNegativeDecimal('lightDraft')"
|
@input="normalizeNonNegativeDecimal('lightDraft')"
|
||||||
>
|
/>
|
||||||
<template #suffix>t</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="8">
|
||||||
<el-form-item label="满载吃水" prop="fullLoadDraft">
|
<el-form-item label="满载吃水(t)" prop="fullLoadDraft">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="shipForm.fullLoadDraft"
|
v-model="shipForm.fullLoadDraft"
|
||||||
@input="normalizeNonNegativeDecimal('fullLoadDraft')"
|
@input="normalizeNonNegativeDecimal('fullLoadDraft')"
|
||||||
>
|
/>
|
||||||
<template #suffix>t</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="18">
|
<el-row :gutter="18">
|
||||||
<el-col :span="6">
|
<el-col :span="8">
|
||||||
<el-form-item label="航区" prop="navigationArea">
|
<el-form-item label="航区" prop="navigationArea">
|
||||||
<div class="navigation-area-field">
|
<div class="navigation-area-field">
|
||||||
<el-select v-model="shipForm.navigationArea" placeholder="请选择">
|
<el-select v-model="shipForm.navigationArea" placeholder="请选择">
|
||||||
|
|||||||
@@ -77,9 +77,7 @@
|
|||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@input="value => normalizeDecimalInput('fee', value)"
|
@input="value => normalizeDecimalInput('fee', value)"
|
||||||
>
|
/>
|
||||||
<template #append>元</template>
|
|
||||||
</el-input>
|
|
||||||
</template>
|
</template>
|
||||||
<template #attachments="{ row }">
|
<template #attachments="{ row }">
|
||||||
<span>{{ formatAttachments(row.attachments) }}</span>
|
<span>{{ formatAttachments(row.attachments) }}</span>
|
||||||
|
|||||||
@@ -97,9 +97,7 @@
|
|||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@input="value => normalizeDecimalInput('mileage', value)"
|
@input="value => normalizeDecimalInput('mileage', value)"
|
||||||
>
|
/>
|
||||||
<template #append>{{ form.mileageUnit || '公里' }}</template>
|
|
||||||
</el-input>
|
|
||||||
</template>
|
</template>
|
||||||
<template #mileage="{ row }">
|
<template #mileage="{ row }">
|
||||||
<span>{{ formatMileage(row.mileage, row.mileageUnit) }}</span>
|
<span>{{ formatMileage(row.mileage, row.mileageUnit) }}</span>
|
||||||
@@ -331,7 +329,7 @@ export default {
|
|||||||
placeholder: '日期时间选择器',
|
placeholder: '日期时间选择器',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '里程/航程数',
|
label: '里程/航程数(公里)',
|
||||||
prop: 'mileage',
|
prop: 'mileage',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
minWidth: 130,
|
minWidth: 130,
|
||||||
@@ -520,7 +518,7 @@ export default {
|
|||||||
vehicleNoColumn.placeholder =
|
vehicleNoColumn.placeholder =
|
||||||
vehicleType === '船舶' ? '输入船号模糊查询选择' : '输入车牌号模糊查询选择';
|
vehicleType === '船舶' ? '输入船号模糊查询选择' : '输入车牌号模糊查询选择';
|
||||||
vehicleTypeColumn.disabled = Boolean(this.form.id);
|
vehicleTypeColumn.disabled = Boolean(this.form.id);
|
||||||
mileageColumn.append = mileageUnit;
|
mileageColumn.label = `里程/航程数(${mileageUnit})`;
|
||||||
mileageUnitColumn.value = mileageUnit;
|
mileageUnitColumn.value = mileageUnit;
|
||||||
this.form.mileageUnit = mileageUnit;
|
this.form.mileageUnit = mileageUnit;
|
||||||
if (!this.isDetailLoading && oldValue && vehicleType !== oldValue && this.form.vehicleNo) {
|
if (!this.isDetailLoading && oldValue && vehicleType !== oldValue && this.form.vehicleNo) {
|
||||||
|
|||||||
Reference in New Issue
Block a user