8 Commits

Author SHA1 Message Date
gxwebsoft 508d73fc0d Merge remote-tracking branch 'origin/master' 2026-09-09 12:45:50 +08:00
gxwebsoft e73e1da877 feat(contract-manage): 将付款比例设置提示改为tooltip展示
- 付款比例设置区块标题后添加问号图标悬浮提示,替换原红色文字说明
- 编辑弹窗、详情弹窗、合同变更页统一使用el-tooltip + el-icon-question-filled组件
- 移除contract-manage.vue中原有.red tip提示及相应样式

feat(temporary-credit-limit): 优化临时额度申请界面及校验规则

- 增加新增/编辑/查看临时额度申请标题,覆盖默认菜单注入标题
- 所有表单字段统一设置placeholder为“请输入/请选择”无字段名
- 搜索栏占位符统一为“请输入/请选择”,与其他模块一致
- 临时额度申请有效期至字段增加“必须晚于当前日期”日期校验规则
- 增添futureDateRule通用校验规则,支持多种日期格式即时触发验证

feat(project-apply): 新增资金使用风险快速筛选及风险提示

- 项目申请列表增加资金使用风险列,显示高风险/中风险标签
- 新增资金使用风险快速筛选区,可点击标签筛选项目风险等级
- 提供资金使用风险阈值配置,支持中风险≥80%、高风险≥90%
- 调整页面样式及交互,优化风险筛选体验
- 新增后端接口getFundRiskStats,用于获取风险统计数据并实时刷新
2026-09-09 12:45:46 +08:00
gxwebsoft 4d5b99da82 feat(contract): 优化合同变更及车辆认证审核弹窗
- 合同变更页「其它附件」上传按钮样式调整,与变更材料区按钮统一
- 新增分区标题必填星号样式,改用 ::after 避免冲突
- 变更页 load() 强制清空表单关键字段,避免带入旧数据
- 实现 mergeChangeAttachments() 合并上次变更附件,去重并格式化大小
- 合同变更记录表格新增「变更内容」列,支持单行摘要显示与悬浮全量提示
- 操作列由单「流程」改为「详情 + 流程」按钮,详情文字统一
- 变更记录值格式化增强,支持方案名、支付比例、结算规则等摘要展示
- buildDetailChangeRecordRows 过滤条件更新,前后均为空时跳过展示
- 车辆认证审核弹窗样式及结构重构,使用 section-card 白卡分组,内容补全证件图片区
- 证件图片实现只读大图展示,复用全局样式,无需新增规则
- label 单位统一为中文全角括号,提升界面一致性
- 新增长期有效显示逻辑,避免日期未填「-」与长期有效混淆
- 删除无用计算属性 auditDetailItems,改用简洁 auditValue 方法返回展示值
- 全站弹窗标题统一增加 4px 主色竖条,符合设计规范,且对无标题弹窗进行排除处理
- 客户端临时额度申请期限字段拆分为两个独立字段,清晰明了
- 统一合同变更页变更原因必填标识及上传按钮样式
- 修正 EP 表单项标签高度和对齐,完善星号方案兼容性设计
2026-09-09 11:57:52 +08:00
gxwebsoft f3021d4bae style(ship): 调整航区字段布局及上传器宽度
- 将航区选择控件从宽度12调整为宽度6的列中展示
- 保留航区选择及提示信息功能不变,位置更紧凑
- 调整上传组件宽度从240px增加到248px以适配新布局
2026-09-09 11:20:52 +08:00
gxwebsoft 7eb6cce2db fix(project-apply): 修正项目简称输入框的必填校验
- 取消了项目简称表单项的 required 属性
- 在项目简称的验证规则中新增必填校验
- 使得项目简称必填校验逻辑与表单项显示一致
- 保持校验触发事件为失焦(blur)事件
2026-09-09 11:14:44 +08:00
gxwebsoft be2888d428 fix(styles): 修复必填星号首行对齐问题
- 调整 `.is-required .el-form-item__label` 采用 `align-items: normal` 实现星号首行对齐
- 覆盖 Element Plus 固定高度 `height: 32px` 为 `height: auto` 以支持内容自适应高度
- 保持 `align-self: center` 使单行 label 与输入框垂直居中,折行时星号贴首行
- 修改星号 `::before` 的高度为 `auto` 并继承行高,保证首行基线一致
- 修复多行 label 下星号因 flex 居中导致位置偏移的问题
- 兼容 label 多种位置(右、左、顶)及多行折行场景,影响 project-apply 等表单页
2026-09-09 11:11:39 +08:00
gxwebsoft 06f6950434 refactor(contract-manage): 优化结算货币标签显示
- 结算货币选项中仅显示字典值,去除字典键拼接
- 注释掉样式中 el-form-item 标签的行高设置,避免样式冲突
2026-09-09 10:39:30 +08:00
gxwebsoft 09f1af9a0a fix(styles): 调整必填星号与标签首行对齐样式
- 修改 .el-form-item__label 的对齐方式使必填星号与首行文字齐平
- 设置多标签布局下星号对齐,兼容单行和多行标签显示
- 调整 ::before 伪元素行高和外边距,确保星号基线一致

fix(project-apply): 更新表单字段“资金需求”为“履约保证金”

- 修改项目申请页面中“资金需求”标签为“履约保证金”
- 更新对应验证规则标签名称,保持校验一致性

chore(config): 更新开发服务器端口及代理设置

- 注释旧服务器配置,启用新端口 2889
- 更新 /api 代理目标地址为 172.16.203.228:8000
- 保持代理跨域设置不变
2026-09-09 10:31:22 +08:00
14 changed files with 761 additions and 137 deletions
+57
View File
@@ -0,0 +1,57 @@
## 付款比例设置提示改为 tooltipcontract-manage / contract-manage-change
- 需求:将「付款比例设置」区块原有的红色文字提示改为标题右侧的 `?` 图标悬停提示。
- 改动:编辑弹窗、详情弹窗、合同变更页三处标题后添加 `el-tooltip` + `el-icon-question-filled`,content 为「非必填;配置付款比例时,合计必须等于100%。」;同时移除 contract-manage.vue 中原 `.contract-manage-form__tip` 红色提示文字及对应样式。
## 必填星号与 label 首行齐平(全局)
- 根因:`element-ui.scss``.el-form-item--label-right .el-form-item__label { align-items:center }` 使 inline-flex label 的星号 `::before`(独立 flex 子项)在多行折行时被垂直居中。
- 修复:label 改 `align-self:center`(整体仍随行垂直居中)+ `align-items:flex-start`(星号/文字首行齐平),覆盖 label-right/left/top 三种位置;旧 inline-block 注释已修正。
- 影响页面:project-apply 独立表单页等所有多行 label 场景(弹窗内 `white-space:nowrap` 不受影响)。
## 必填星号首行对齐:改用 align-items: normalstretch 方案)
- 最终写法(`src/styles/element-ui.scss`):`.is-required .el-form-item__label { align-self:center; align-items:normal; }` + `::before { display:inline-block; height:auto; line-height:inherit; margin-right:4px }`
- 原理:stretch 让星号盒子在「换行时」被拉伸到 label 全高、内容从顶部排布 → 贴首行;单行时盒子=一行高,视觉不变(比 flex-start 更贴合「只换行才生效」)。
- 前提:::before 高度必须 auto 且继承行高;label 不能是 固定 height + 等高 line-height,否则退化成居中,需 JS 判行数加 class。
## 合同变更页(contract-manage-change.vue
- 「其它附件」上传按钮改为 `type="primary" plain icon="el-icon-upload"`,与「变更材料」区 vehicle-attachment-upload 内置按钮一致。
- 分区标题必填星号:`.dialog-section-title.is-required::after { content:'*'; color:#f56c6c }``::before` 已被蓝色竖条占用,星号走 `::after`)。
- 再次变更逻辑:`load()` 里 form 强制重置 `changeContent/changeReason/changeAttachmentsJson = ''``changeMaterials = []`(不带上一次内容);新增 `mergeChangeAttachments()` 把上一次的 `changeAttachmentsJson` 归集进 `attachments`(其它附件),按 url/名去重,数字 size 转格式化文本,提交时随 attachmentsJson 持久化。
## 合同变更记录对齐客商变更记录(contract-manage.vue
- 编辑页与详情弹窗的两处「变更记录」表格新增「变更内容」列:`formatContractChangeContent(row)` 复用 `buildDetailChangeRecordRows` 生成「字段:变更前:x → 变更后:y」摘要,单行省略 + `el-tooltip` 全量悬浮(样式 `.contract-change-record-content-cell/-tooltip` 照搬客商 `change-record-content-*`)。
- 编辑页操作列由单个「流程」改为「详情 + 流程」(`openDetailChangeRecord` + `openFlow`,宽度 120→150);详情弹窗表格「查看详情」改「详情」与客商一致。
- 值格式化增强:`billingPlanJson`→方案名(默认)列表、`paymentRatioJson`→「第N笔 xx%」、`settlementRuleJson/preSettlementConfigJson/formalSettlementConfigJson`→可读摘要(自动生成开关/结算类型/周期类型/截单日/周期天数,含预结算+正式结算双段)。
- `buildDetailChangeRecordRows` 过滤条件补「前后都为空则跳过」(原来只比 JSON 字符串,'' 与 null 会漏)。
## 星号方案补丁(关键)
- EP 的 `.el-form-item__label` 写死 `height:32px; line-height:32px`(项目只覆盖了 line-height:14px),导致 stretch 方案文字顶到 32px 盒子顶部、单行不居中。
- 修复:`.is-required .el-form-item__label { height:auto; align-self:center; align-items:normal }`——height:auto 收缩到内容高 + align-self:center 行内居中 + normal(stretch) 仅换行时星号贴首行。
## 车辆认证审核弹窗重构(transportCapacity/vehicle.vue
- 背景:用户反馈「样式不统一、内容不全」。根因是审核弹窗用 `<el-descriptions :column="3" border>`(大白表 + 居中 label),与同页编辑弹窗的 `<section-card>` 白卡分组风格不一致。
- 改造:弹窗主体改为 4 个 `section-card` 分组 —— 基础信息 / 尺寸重量 / 证件信息 / 证件图片;内部 `el-form label-position="right" label-width="auto"` + `el-row/el-col`3 列 `:span="8"`,所属组织与备注占 `:span="24"`)。
- 内容补全:新增「证件图片」分组,6 张证件照用 `<image-upload-field readonly large class-prefix="vehicle">` 只读展示(可点击放大);证件图片尺寸 240×151 复用已有全局 `:deep(.vehicle-uploader--large)` 规则,无需新写。
- label 单位统一为中文全角括号:`外廓长度(毫米)` / `核定载质量(KG`(原为半角 `外廓长度(mm)`)。
- 方法重构:`formatAuditValue(item)``auditValue(prop)`,签名更简洁;新增 longTermMap,对 drivingLicenseEndDate / roadTransportCertEndDate / annualReviewEndDate / compulsoryScrapDate 在勾选长期有效时返回「长期有效」,避免与未填写的「-」混淆(与列表页 formatEndDate 行为一致)。
- 删除已无引用的 `auditDetailItems` computed。
## 全站弹窗标题补 4px 主色竖条(全局 element-ui.scss
- 发现:`.el-dialog__title` 全局只配了 `inline-flex + gap:8px`**没有竖条**(竖条此前只在 `.dialog-section-title::before`),导致全站 el-dialog 标题都不带竖条,不符合 AGENTS.md 4.8。
- 修复:新增 `.el-dialog__title:not(:empty)::before { width:4px; height:16px; border-radius:2px; background:var(--el-color-primary) }`
- 注意:必须加 `:not(:empty)`,否则无标题弹窗会出现孤立蓝条。
- customer-archive.vue 评分详情弹窗:将「临时额度申请使用期限」区间行(span=12 + 至 连接)拆分为两个 span=6 独立字段「临时申请额度有效期起 / 止」,字段名 tempCreditStartDate/End 不变,删除废弃样式 .score-detail-form__range。
- temporary-credit-limit 文案调整:option 增加 addTitle/editTitle/viewTitle=「新增/编辑/查看临时额度申请」(覆盖 crud-dialog-title mixin 按菜单名注入的「临时额度管理」);remark label 改「临时额度信息」(hide:true 无表格副作用,rules 同步);projectName 表单 label 用 `#projectName-label` 插槽改「项目额度信息」,列 label 保持「项目名称」不动表格列头/搜索。Avue 支持-{prop}-label / -header / -search 插槽分离表单/表头/搜索文案。
- temporary-credit-limit「申请有效期至」增加「必须晚于当前日期」校验:common.js 新增通用 `futureDateRule(label)`trigger: ['change','blur'],空值/非法值放行,按 0 点比较,当天也算过期),temporary-credit-limit.js 的 validUntil rules 追加该规则。已验证 Element Plus 通用 picker 在选中日期时调用 formItem.validate('change'),故选择即刻提示;form.validate() 不带 trigger 时会跑全部规则,提交仍拦截。
- temporary-credit-limit 搜索占位文案统一:createCrudOption(withSearchPlaceholders([...])),搜索栏 placeholder 变为「请输入/请选择」不带字段名,与 waybill-manage / transport-plan / shipping-template 同一模式。
- temporary-credit-limit 弹窗表单 placeholder 统一为「请输入/请选择」不带字段名:option 各列加 placeholderprojectCode/undertakeDeptName/projectFundLimit/usedFundLimit/remainingFundLimit/remark=请输入,validUntil=请选择),视图 formslot 自定义控件(projectName select、applyLimit inputplaceholder 同步改。
## 【项目管理】列表增加「项目资金使用预警」(project-apply
- 需求:列表新增「资金使用风险」列,高风险红色、中风险黄色;列表头部增加「资金使用风险」快速筛选标签(高风险(数)/中风险(数)),点击筛选。规则 tooltip:中风险≥80%、高风险≥90%,使用率=已使用资金/项目资金使用额度×100%。
- 前端(已落地,src/views/business/project-apply.vue + src/option/business/project-apply.js + src/api/business/project-apply.js):
- option/project-apply.js 新增列 `fundUseRisk`(slot:true, hide 列表显示) + 导出 `fundUseRiskOptions`(high/medium/none) 与 `fundUseRiskThreshold`{high:90, medium:80}。
- view 新增 `#header` 插槽:`el-check-tag` 快速筛选(复用 contract-manage 到期标签交互),带数量(fundRiskStats[statKey])与 `?` tooltip 规则;新增 `#fundUseRisk` 插槽渲染彩色 `el-tag`high→type=danger 红,medium→type=warning 黄,无→"-")。
- data 加 fundRiskScope/fundRiskStats{high,medium,none}/fundRiskTagOptionsonLoad 追加 `fundUseRisk` 筛选参数 + 成功后 refreshFundRiskStats;新增 changeFundRiskScope(再次点击取消)、refreshFundRiskStatssearchReset 清空风险筛选。
- api 新增 `getFundRiskStats` → GET /blade-transport/project-apply/fund-risk-stats。
- 后端契约(未改,待确认数据来源):ProjectApplyVO 补齐 `fundUseRisk`(high/medium/none)、`fundUseRiskName``fundUseRate``usedFundLimit`/`remainingFundLimit`;列表 selectProjectApplyPage 填充每行风险 + buildQuery 支持 `fundUseRisk` 过滤;新增 `/fund-risk-stats` 返回 {high,medium,none}。
- ⚠️ 关键阻塞:系统里项目与任何费用表(运输计划/运单/账单/客商)均无关联字段,`usedFundLimit`(已使用资金) 无真实取数来源;临时额度模块的 project-apply/list 返回里 usedFundLimit 实际为空。需业务方确认「已使用资金」口径后才能算风险等级,否则列表风险列全空、统计为 0。
+8
View File
@@ -9,6 +9,14 @@ export const getDetail = api.getDetail;
export const submit = api.submit;
export const remove = api.remove;
// 项目资金使用风险统计(高风险 / 中风险数量),供列表头部快速筛选标签使用
export const getFundRiskStats = params =>
request({
url: `${baseUrl}/fund-risk-stats`,
method: 'get',
params,
});
export const saveDraft = row =>
request({
url: `${baseUrl}/save-draft`,
+26
View File
@@ -169,6 +169,32 @@ export const nonNegativeRule = label => ({
trigger: 'blur',
});
// 日期必须晚于当前日期(当天不可选):选择后立即校验,trigger 固定为 change
// 取值支持 YYYY-MM-DD / YYYY-MM-DD HH:mm:ss / 时间戳
export const futureDateRule = label => ({
validator: (rule, value, callback) => {
if (value === undefined || value === null || value === '') {
callback();
return;
}
const raw = String(value).trim().replace(/-/g, '/');
const selected = new Date(raw);
if (Number.isNaN(selected.getTime())) {
callback();
return;
}
const today = new Date();
today.setHours(0, 0, 0, 0);
selected.setHours(0, 0, 0, 0);
if (selected.getTime() <= today.getTime()) {
callback(new Error(`${label}必须晚于当前日期`));
return;
}
callback();
},
trigger: ['change', 'blur'],
});
export const phoneRule = label => ({
pattern: /^1[3-9]\d{9}$/,
message: `${label}格式不正确`,
+21
View File
@@ -16,6 +16,18 @@ const projectEffectiveTypeOptions = [
{ label: '正式', value: 'formal' },
];
export const fundUseRiskOptions = [
{ label: '高风险', value: 'high' },
{ label: '中风险', value: 'medium' },
{ label: '正常', value: 'none' },
];
// 项目资金额度使用率阈值:中风险 ≥ 80%,高风险 ≥ 90%
export const fundUseRiskThreshold = {
high: 90,
medium: 80,
};
const amountRules = label => [
{ required: true, message: `请输入${label}`, trigger: 'blur' },
nonNegativeRule(label),
@@ -402,5 +414,14 @@ export const option = createCrudOption([
span: 24,
hide: true,
},
{
label: '资金使用风险',
prop: 'fundUseRisk',
slot: true,
minWidth: 130,
addDisplay: false,
editDisplay: false,
hide: false,
},
...auditColumns.map(column => ({ ...column, hide: true })),
]);
+20 -5
View File
@@ -2,8 +2,10 @@ import {
approvalStatusOptions,
auditColumns,
createCrudOption,
futureDateRule,
nonNegativeRule,
textRule,
withSearchPlaceholders,
} from './common';
const listDicFormatter = res => {
@@ -118,7 +120,10 @@ export const config = {
export const option = {
dialogCustomClass: 'temporary-credit-limit-dialog',
...createCrudOption([
addTitle: '新增临时额度申请',
editTitle: '编辑临时额度申请',
viewTitle: '查看临时额度申请',
...createCrudOption(withSearchPlaceholders([
{
label: '申请单号',
prop: 'applicationNo',
@@ -158,6 +163,7 @@ export const option = {
prop: 'projectCode',
hide: true,
disabled: true,
placeholder: '请输入',
span: 6,
minWidth: 150,
rules: textRule('项目编号', 50),
@@ -167,6 +173,7 @@ export const option = {
prop: 'undertakeDeptName',
hide: true,
disabled: true,
placeholder: '请输入',
span: 6,
minWidth: 150,
rules: textRule('承办部门', 50, true),
@@ -176,6 +183,7 @@ export const option = {
prop: 'projectFundLimit',
hide: true,
disabled: true,
placeholder: '请输入',
span: 6,
minWidth: 200,
rules: [nonNegativeRule('项目资金使用额度')],
@@ -185,6 +193,7 @@ export const option = {
prop: 'usedFundLimit',
hide: true,
disabled: true,
placeholder: '请输入',
span: 6,
minWidth: 220,
value: 0,
@@ -195,6 +204,7 @@ export const option = {
prop: 'remainingFundLimit',
hide: true,
disabled: true,
placeholder: '请输入',
span: 6,
minWidth: 230,
value: 0,
@@ -217,9 +227,13 @@ export const option = {
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
placeholder: '请选择',
span: 6,
minWidth: 140,
rules: [{ required: true, message: '请选择申请有效期至', trigger: 'change' }],
rules: [
{ required: true, message: '请选择申请有效期至', trigger: 'change' },
futureDateRule('申请有效期至'),
],
},
{
label: '申请部门',
@@ -285,15 +299,16 @@ export const option = {
editDisplay: false,
},
{
label: '备注',
label: '临时额度信息',
prop: 'remark',
type: 'textarea',
span: 24,
minRows: 2,
hide: true,
placeholder: '请输入',
maxlength: 200,
showWordLimit: true,
rules: textRule('备注', 200),
rules: textRule('临时额度信息', 200),
},
{
label: '附件',
@@ -303,7 +318,7 @@ export const option = {
hide: true,
},
...auditColumns.map(column => ({ ...column, hide: true })),
]),
])),
dialogWidth: '96%',
menuFixed: 'right',
menuWidth: 320,
+28 -8
View File
@@ -213,6 +213,17 @@
gap: 8px;
}
// 全站弹窗标题统一带 4px 主色竖条(与 .dialog-section-title 保持一致)
// :not(:empty) 避免无标题弹窗出现孤立竖条
.el-dialog__title:not(:empty)::before {
content: '';
flex: none;
width: 4px;
height: 16px;
border-radius: 2px;
background-color: var(--el-color-primary);
}
.dialog-section-title::before {
content: '';
flex: none;
@@ -708,9 +719,9 @@
.avue-crud .el-table__body-wrapper tr.el-table__row:nth-child(even) td.el-table__cell,
.avue-crud .el-table__body-wrapper tr.el-table__row:nth-child(even) td.el-table-fixed-column--left,
.avue-crud
.el-table__body-wrapper
tr.el-table__row:nth-child(even)
td.el-table-fixed-column--right {
.el-table__body-wrapper
tr.el-table__row:nth-child(even)
td.el-table-fixed-column--right {
background-color: #fafafa;
}
@@ -1050,18 +1061,27 @@
.el-form-item--label-right .el-form-item__label {
align-items: center;
}
// ============ 必填星号 * 与 label 首行齐平(仅换行时生效) ============
// label 为 inline-flex,星号 ::before 与文字是两个独立 flex 子项。
// align-items: normal= stretch):单行时星号盒子 = 一行高,位置不变;
// 折行后星号盒子被拉伸到 label 全高,但其内部按 line-height 从顶部排布 → 星号停在第一行。
// height: auto 覆盖 Element Plus 写死的 32px,让 label 收缩到内容高;
// align-self: center 再让收缩后的 label 在表单行内垂直居中(单行与输入框对齐)。
.is-required .el-form-item__label {
height: auto;
align-self: center;
align-items: normal;
}
.el-form-item--default {
margin-bottom: 14px;
}
// ============ 必填星号 * 与首行文字垂直居中 ============
// Element Plus 默认 .el-form-item__label 为 inline-flex + align-items: flex-start
// 星号 :before 高度(1em) 与文字行盒(line-height) 不一致时,多行/折行 label 会偏上。
// 让 :before 跟随 label 行高、以 inline-block 参与行内对齐即可修复,
// 兼容单行(默认 32px)与多行(auto + 18px)两种 label 高度场景。
// 配套:星号必须保持「高度 auto + 继承行高」,stretch 才会按首行排布;
// 写死 height 或 line-height 与 height 相等时效果会退化成居中。
// 注:客商档案(customer-archive)的 label 为 auto 高度 + padding 下沉方案,scoped 特异性更高会覆盖本规则。
.el-form-item.is-required .el-form-item__label::before {
display: inline-block;
height: auto;
line-height: inherit;
margin-right: 4px;
}
+19 -4
View File
@@ -63,7 +63,12 @@
</section>
<section class="change-section">
<div class="dialog-section-title">付款比例设置</div>
<div class="dialog-section-title">
付款比例设置
<el-tooltip content="非必填;配置付款比例时,合计必须等于100%。" placement="top">
<el-icon style="margin-left: 4px; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
</el-tooltip>
</div>
<el-table :data="paymentRatioRows" border class="change-table">
<el-table-column type="index" label="序号" width="70" />
<el-table-column prop="paymentTerm" label="付款笔数" min-width="180" />
@@ -79,13 +84,13 @@
<section class="change-section attachment-section">
<div class="section-head"><div class="dialog-section-title">其它附件</div><el-button type="primary" plain>批量下载</el-button></div>
<el-table :data="attachments" border class="change-table"><el-table-column type="index" label="序号" width="70" /><el-table-column label="文件名" min-width="240"><template #default="{ row }"><el-link type="primary" @click="previewAttachment(row, attachments)">{{ row.originalName || row.name }}</el-link></template></el-table-column><el-table-column prop="description" label="附件描述" min-width="240" /><el-table-column prop="size" label="文件大小" width="120" /><el-table-column prop="userName" label="上传人" width="140" /><el-table-column prop="uploadTime" label="上传时间" width="180" /><el-table-column label="操作" width="100"><template #default="{ $index }"><el-link type="danger" @click="attachments.splice($index, 1)">删除</el-link></template></el-table-column></el-table>
<el-upload action="#" :auto-upload="false" multiple :show-file-list="false" @change="handleAttachment"><el-button plain>上传附件</el-button></el-upload>
<el-upload action="#" :auto-upload="false" multiple :show-file-list="false" @change="handleAttachment"><el-button type="primary" plain icon="el-icon-upload">上传附件</el-button></el-upload>
</section>
<section class="change-section change-reason-section">
<div class="dialog-section-title">变更内容</div>
<el-form-item prop="changeContent"><el-input v-model="form.changeContent" type="textarea" :rows="3" maxlength="2000" show-word-limit placeholder="请输入变更内容" /></el-form-item>
<div class="dialog-section-title">变更原因</div>
<div class="dialog-section-title is-required">变更原因</div>
<el-form-item prop="changeReason"><el-input v-model="form.changeReason" type="textarea" :rows="3" maxlength="500" show-word-limit placeholder="请输入变更原因" /></el-form-item>
<div class="dialog-section-title">变更材料</div>
<el-table :data="changeMaterials" border class="change-table">
@@ -138,8 +143,17 @@ export default {
mounted() { this.load(); },
watch: { settlementConfigTab(tab, oldTab) { if (tab === oldTab) return; if (oldTab === 'pre') this.preSettlementConfig = { ...this.settlementRule }; else this.formalSettlementConfig = { ...this.settlementRule }; this.settlementRule = { ...(tab === 'pre' ? this.preSettlementConfig : this.formalSettlementConfig) }; } },
methods: {
async load() { const id = this.$route.query.id; if (!id) return; const res = await api.getDetail(id); const data = res.data?.data || res.data || {}; this.form = { ...data, changeContent: '', changeReason: '', changeAttachmentsJson: '', copyCount: normalizeOptionalPositiveInteger(data.copyCount), paymentDays: normalizeOptionalPositiveInteger(data.paymentDays), changeType: '合同信息变更' }; this.changeMaterials = []; this.period = data.startDate && data.endDate ? [data.startDate, data.endDate] : []; this.plans = this.parse(data.billingPlanJson); this.attachments = this.parse(data.attachmentsJson); this.contractFileRows = this.parse(data.contractFileJson); const rules = this.parseObject(data.settlementRuleJson); const pre = this.parseObject(data.preSettlementConfigJson); const formal = this.parseObject(data.formalSettlementConfigJson); const legacy = Object.keys(rules).some(key => !['preSettlementConfig', 'formalSettlementConfig'].includes(key)) ? rules : {}; this.preSettlementConfig = rules.preSettlementConfig || (Object.keys(pre).length ? pre : legacy); this.formalSettlementConfig = rules.formalSettlementConfig || (Object.keys(formal).length ? formal : legacy); this.settlementRule = { ...this.preSettlementConfig }; this.feeGenerationMode = data.feeGenerationMode || (Number(data.billingEnabled) === 0 ? 'manual' : 'system'); this.paymentRatioRows = this.parse(data.paymentRatioJson); },
async load() { const id = this.$route.query.id; if (!id) return; const res = await api.getDetail(id); const data = res.data?.data || res.data || {}; this.form = { ...data, changeContent: '', changeReason: '', changeAttachmentsJson: '', copyCount: normalizeOptionalPositiveInteger(data.copyCount), paymentDays: normalizeOptionalPositiveInteger(data.paymentDays), changeType: '合同信息变更' }; this.changeMaterials = []; this.period = data.startDate && data.endDate ? [data.startDate, data.endDate] : []; this.plans = this.parse(data.billingPlanJson); this.attachments = this.mergeChangeAttachments(this.parse(data.attachmentsJson), this.parse(data.changeAttachmentsJson)); this.contractFileRows = this.parse(data.contractFileJson); const rules = this.parseObject(data.settlementRuleJson); const pre = this.parseObject(data.preSettlementConfigJson); const formal = this.parseObject(data.formalSettlementConfigJson); const legacy = Object.keys(rules).some(key => !['preSettlementConfig', 'formalSettlementConfig'].includes(key)) ? rules : {}; this.preSettlementConfig = rules.preSettlementConfig || (Object.keys(pre).length ? pre : legacy); this.formalSettlementConfig = rules.formalSettlementConfig || (Object.keys(formal).length ? formal : legacy); this.settlementRule = { ...this.preSettlementConfig }; this.feeGenerationMode = data.feeGenerationMode || (Number(data.billingEnabled) === 0 ? 'manual' : 'system'); this.paymentRatioRows = this.parse(data.paymentRatioJson); },
parse(value) { try { const result = JSON.parse(value || '[]'); return Array.isArray(result) ? result : []; } catch { return []; } },
// 审核通过后上一次的变更材料归集到「其它附件」,按文件地址/文件名去重,避免重复展示
mergeChangeAttachments(attachments = [], changeAttachments = []) {
const key = item => this.attachmentUrl(item) || this.attachmentName(item);
const exists = new Set(attachments.map(key));
const merged = changeAttachments
.filter(item => !exists.has(key(item)))
.map(item => ({ ...item, size: /^\d+$/.test(String(item.size ?? '')) ? this.formatFileSize(item.size) : item.size }));
return [...attachments, ...merged];
},
parseObject(value) { try { return { autoGenerate: 1, settlementType: '月结', billCycleType: '固定截单日', billCutoffDay: 25, cycleDays: 15, ...(JSON.parse(value || '{}') || {}) }; } catch { return { autoGenerate: 1, settlementType: '月结', billCycleType: '固定截单日', billCutoffDay: 25, cycleDays: 15 }; } },
positiveIntegerInput(prop, value) { this.form[prop] = String(value ?? '').replace(/\D/g, '').replace(/^0+/, ''); },
addPlan() { this.planEditorIndex = -1; this.planEditor = { planName: `计费方案${this.plans.length + 1}`, defaultPlan: !this.plans.length, remark: '', rules: [{}] }; this.planDialogVisible = true; },
@@ -187,6 +201,7 @@ export default {
.contract-basic-section :deep(.el-date-editor) { width: 360px; max-width: 100%; }
.dialog-section-title { margin-bottom: 18px; font-size: 16px; font-weight: 600; }
.dialog-section-title::before { display: inline-block; width: 4px; height: 16px; margin-right: 8px; vertical-align: -2px; background: #409eff; content: ''; }
.dialog-section-title.is-required::after { margin-left: 4px; color: #f56c6c; content: '*'; }
.section-head { display: flex; align-items: center; justify-content: space-between; }
.change-table { margin: 16px 0; }
.ratio-tip { margin-bottom: 8px; color: #f56c6c; }
+142 -13
View File
@@ -425,10 +425,14 @@
<section class="contract-manage-form__section contract-manage-form__section--panel">
<div class="contract-manage-form__section-head">
<div class="dialog-section-title">付款比例设置</div>
<div class="dialog-section-title">
付款比例设置
<el-tooltip content="非必填配置付款比例时合计必须等于100%" placement="top">
<el-icon style="margin-left: 4px; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
</el-tooltip>
</div>
<el-button type="primary" plain @click="addPaymentRatio">添加</el-button>
</div>
<p class="contract-manage-form__tip">非必填;配置付款比例时,合计必须等于100%。</p>
<el-table :data="paymentRatioRows" border>
<el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="paymentTerm" label="付款笔数" min-width="180" align="center" />
@@ -480,6 +484,20 @@
/>
<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 label="变更内容" min-width="420">
<template #default="{ row }">
<el-tooltip placement="top" :show-after="200">
<template #content>
<div class="contract-change-record-content-tooltip">
{{ formatContractChangeContent(row) }}
</div>
</template>
<span class="contract-change-record-content-cell">{{
formatContractChangeContent(row)
}}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
prop="changeReason"
label="变更原因"
@@ -488,8 +506,9 @@
show-overflow-tooltip
/>
<el-table-column prop="statusName" label="状态" min-width="140" align="center" />
<el-table-column label="操作" width="120" align="center" fixed="right">
<el-table-column label="操作" width="150" align="center" fixed="right">
<template #default="{ row }">
<el-link type="primary" @click="openDetailChangeRecord(row)">详情</el-link>
<el-link type="primary" @click="openFlow(row)">流程</el-link>
</template>
</el-table-column>
@@ -725,7 +744,12 @@
</section>
<section class="contract-manage-form__section contract-manage-form__section--panel">
<div class="dialog-section-title">付款比例设置</div>
<div class="dialog-section-title">
付款比例设置
<el-tooltip content="非必填配置付款比例时合计必须等于100%" placement="top">
<el-icon style="margin-left: 4px; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
</el-tooltip>
</div>
<el-table :data="detailPaymentRatioRows" border>
<el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="paymentTerm" label="付款笔数" min-width="180" align="center" />
@@ -759,6 +783,20 @@
/>
<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 label="变更内容" min-width="420">
<template #default="{ row }">
<el-tooltip placement="top" :show-after="200">
<template #content>
<div class="contract-change-record-content-tooltip">
{{ formatContractChangeContent(row) }}
</div>
</template>
<span class="contract-change-record-content-cell">{{
formatContractChangeContent(row)
}}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
prop="changeReason"
label="变更原因"
@@ -769,7 +807,7 @@
<el-table-column prop="statusName" label="状态" min-width="140" align="center" />
<el-table-column label="操作" width="120" align="center" fixed="right">
<template #default="{ row }"
><el-link type="primary" @click="openDetailChangeRecord(row)">查看详情</el-link></template
><el-link type="primary" @click="openDetailChangeRecord(row)">详情</el-link></template
>
</el-table-column>
</el-table>
@@ -1790,7 +1828,7 @@ export default {
return Array.isArray(data) ? data : data.records || [];
};
this.settlementCurrencyOptions = records(currencyRes).map(item => ({
label: `${item.dictKey} - ${item.dictValue}`,
label: item.dictValue,
value: item.dictKey,
}));
this.settlementModeDictOptions = records(methodRes).map(item => ({
@@ -2250,12 +2288,73 @@ export default {
.filter(Boolean);
return names.length ? names.join('、') : '空';
},
formatChangeRecordBillingPlans(value) {
const list = Array.isArray(value) ? value : [];
if (!list.length) return '空';
return list
.map(item => {
if (!item || typeof item !== 'object') return String(item ?? '');
const name = item.planName || item.name || '未命名方案';
return item.defaultPlan ? `${name}(默认)` : name;
})
.filter(Boolean)
.join('、');
},
formatChangeRecordPaymentRatios(value) {
const list = Array.isArray(value) ? value : [];
if (!list.length) return '空';
return list
.map(item => {
if (!item || typeof item !== 'object') return String(item ?? '');
const term = item.paymentTerm || '';
const ratio = item.ratioLimit === undefined || item.ratioLimit === null || item.ratioLimit === '' ? '' : `${item.ratioLimit}%`;
return [term, ratio].filter(Boolean).join(' ');
})
.filter(Boolean)
.join('');
},
formatChangeRecordSettlementRule(value) {
const normalized = this.normalizeChangeRecordValue(value);
if (!normalized || typeof normalized !== 'object' || Array.isArray(normalized)) {
return this.isEmptyChangeRecordValue(normalized) ? '空' : String(normalized);
}
const summary = config => {
if (!config || typeof config !== 'object') return '';
const parts = [];
const autoGenerate = config.autoGenerate;
if (autoGenerate !== undefined && autoGenerate !== null && autoGenerate !== '') {
parts.push(`自动生成结算单:${Number(autoGenerate) === 1 ? '开启' : '关闭'}`);
}
if (config.settlementType) parts.push(`结算类型:${config.settlementType}`);
if (config.billCycleType) parts.push(`账单周期类型:${config.billCycleType}`);
if (config.billCutoffDay) parts.push(`账单截单日:${config.billCutoffDay}日`);
if (config.cycleDays) parts.push(`周期天数:${config.cycleDays}天`);
if (config.billStartDate) parts.push(`账单起始日期:${config.billStartDate}`);
return parts.join('');
};
if (normalized.preSettlementConfig || normalized.formalSettlementConfig) {
return [
`预结算:${summary(normalized.preSettlementConfig) || '空'}`,
`正式结算:${summary(normalized.formalSettlementConfig) || '空'}`,
].join('');
}
return summary(normalized) || '空';
},
formatContractChangeValue(field, value) {
const normalized = this.normalizeChangeRecordValue(value);
if (this.isEmptyChangeRecordValue(normalized)) return '空';
if (['contractFileJson', 'attachmentsJson', 'changeAttachmentsJson'].includes(field)) {
return this.formatChangeRecordAttachments(normalized);
}
if (field === 'billingPlanJson') return this.formatChangeRecordBillingPlans(normalized);
if (field === 'paymentRatioJson') return this.formatChangeRecordPaymentRatios(normalized);
if (
['settlementRuleJson', 'preSettlementConfigJson', 'formalSettlementConfigJson'].includes(
field
)
) {
return this.formatChangeRecordSettlementRule(normalized);
}
if (field === 'legalSealFlag') return Number(normalized) === 1 ? '是' : '否';
if (field === 'feeGenerationMode') return normalized === 'manual' ? '手动生成' : '系统生成';
if (field === 'invoiceCycle' || field === 'paymentDays') return `${normalized}天`;
@@ -2264,16 +2363,33 @@ export default {
}
return String(normalized);
},
// 列表「变更内容」列:把变更前后内容拼成一段摘要(参照客商变更记录)
formatContractChangeContent(row = {}) {
const rows = this.buildDetailChangeRecordRows(row).filter(
item => item.field !== '变更原因'
);
return rows
.map(item => `${item.field}:变更前:${item.before} → 变更后:${item.after}`)
.join('');
},
buildDetailChangeRecordRows(row = {}) {
const beforeData = this.parseChangeRecordData(row.beforeData);
const afterData = this.parseChangeRecordData(row.afterData);
const fields = [...new Set([...Object.keys(beforeData), ...Object.keys(afterData)])];
const rows = fields
.filter(
field =>
.filter(field => {
// 前后都为空的字段不展示('' 与 null 序列化不同,单靠 JSON 比对会漏掉)
if (
this.isEmptyChangeRecordValue(beforeData[field]) &&
this.isEmptyChangeRecordValue(afterData[field])
) {
return false;
}
return (
JSON.stringify(this.normalizeChangeRecordValue(beforeData[field])) !==
JSON.stringify(this.normalizeChangeRecordValue(afterData[field]))
)
);
})
.map(field => ({
field: this.getContractChangeFieldLabel(field),
before: this.formatContractChangeValue(field, beforeData[field]),
@@ -2496,6 +2612,21 @@ export default {
}
}
.contract-change-record-content-cell {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.contract-change-record-content-tooltip {
max-width: 900px;
max-height: 320px;
overflow: auto;
white-space: pre-wrap;
word-break: break-word;
}
.contract-change-record-detail-meta {
display: flex;
flex-wrap: wrap;
@@ -2505,6 +2636,8 @@ export default {
}
:deep(.contract-change-record-detail-dialog .el-dialog__body) {
max-height: 65vh;
overflow: auto;
padding-top: 12px;
}
@@ -2768,8 +2901,4 @@ export default {
white-space: normal;
line-height: 1.2;
}
.contract-manage-form__tip {
color: #ff0000 !important;
font-size: 12px;
}
</style>
+93 -5
View File
@@ -58,6 +58,40 @@
{{ displayStatus(row, 'approvalStatus') }}
</template>
<template #header>
<div class="project-apply-page__risk-search">
<span class="project-apply-page__risk-label">
资金使用风险
<el-tooltip placement="top" effect="dark">
<template #content>
<div>中风险项目资金额度使用率 80%</div>
<div>高风险项目资金额度使用率 90%</div>
<div>使用率 = 已使用资金 / 项目资金使用额度 × 100%</div>
</template>
<i class="el-icon-question project-apply-page__risk-help" />
</el-tooltip>
</span>
<el-check-tag
v-for="item in fundRiskTagOptions"
:key="item.value"
:checked="fundRiskScope === item.value"
@change="changeFundRiskScope(item.value)"
>
{{ item.label }}{{ fundRiskStats[item.statKey] || 0 }}
</el-check-tag>
</div>
</template>
<template #fundUseRisk="{ row }">
<el-tag v-if="row.fundUseRisk === 'high'" type="danger">
{{ row.fundUseRiskName || '高风险' }}
</el-tag>
<el-tag v-else-if="row.fundUseRisk === 'medium'" type="warning">
{{ row.fundUseRiskName || '中风险' }}
</el-tag>
<span v-else class="project-apply-page__risk-none">-</span>
</template>
<template #menu="{ row, index }">
<el-link
type="primary"
@@ -157,7 +191,7 @@
:disabled="isBasicInfoReadonly"
/>
</el-form-item>
<el-form-item label="项目简称" prop="projectShortName" required>
<el-form-item label="项目简称" prop="projectShortName">
<el-input
v-model="form.projectShortName"
placeholder="请填写项目简称"
@@ -376,7 +410,7 @@
<template #suffix>万元</template>
</el-input>
</el-form-item>
<el-form-item label="资金需求" prop="fundDemand">
<el-form-item label="履约保证金" prop="fundDemand">
<el-input
v-model="form.fundDemand"
@input="value => handleNumberInput('fundDemand', value)"
@@ -975,6 +1009,12 @@ export default {
data: [],
query: {},
allDept: 0,
fundRiskScope: '',
fundRiskStats: { high: 0, medium: 0, none: 0 },
fundRiskTagOptions: [
{ label: '高风险', value: 'high', statKey: 'high' },
{ label: '中风险', value: 'medium', statKey: 'medium' },
],
page: {
pageSize: 10,
pageSizes: [10, 20, 50, 100],
@@ -995,6 +1035,7 @@ export default {
{ max: 50, message: '项目名称不能超过50个字符', trigger: 'blur' },
],
projectShortName: [
{ required: true, message: '请输入项目简称', trigger: 'blur' },
{ max: 20, message: '项目简称不能超过20个字符', trigger: 'blur' },
],
businessDeptId: [{ required: true, message: '请选择业务部门', trigger: 'change' }],
@@ -1025,7 +1066,7 @@ export default {
],
projectScale: [{ label: '项目规模', validator: validateAmount, trigger: 'blur' }],
estimatedProfit: [{ label: '预估利润', validator: validateAmount, trigger: 'blur' }],
fundDemand: [{ label: '资金需求', validator: validateAmount, trigger: 'blur' }],
fundDemand: [{ label: '履约保证金', validator: validateAmount, trigger: 'blur' }],
handlerUserId: [
{
required: true,
@@ -1341,12 +1382,14 @@ export default {
...params,
...this.query,
allDept: this.allDept,
...(this.fundRiskScope ? { fundUseRisk: this.fundRiskScope } : {}),
})
.then(res => {
const result = res.data.data || {};
this.page.total = result.total || 0;
this.data = result.records || [];
this.selectionClear();
this.refreshFundRiskStats({ ...params, ...this.query, allDept: this.allDept });
})
.finally(() => {
this.loading = false;
@@ -1354,6 +1397,7 @@ export default {
},
searchReset() {
this.query = {};
this.fundRiskScope = '';
this.page.currentPage = 1;
this.onLoad(this.page);
},
@@ -1363,6 +1407,18 @@ export default {
this.onLoad(this.page, params);
done();
},
// 切换「资金使用风险」快速筛选:再次点击已选中项则取消筛选
changeFundRiskScope(value) {
this.fundRiskScope = this.fundRiskScope === value ? '' : value;
this.page.currentPage = 1;
this.onLoad(this.page, this.query);
},
refreshFundRiskStats(params) {
this.api.getFundRiskStats(params).then(res => {
const stats = res.data?.data || {};
this.fundRiskStats = { ...this.fundRiskStats, ...stats };
});
},
selectionChange(list) {
this.selectionList = list;
},
@@ -2473,8 +2529,40 @@ export default {
.project-apply-page-form {
min-height: 100%;
margin-bottom: 60px;
//padding: 20px 24px 92px;
//background: #f5f6fa;
}
.project-apply-page {
&__risk-search {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
margin-top: 8px;
margin-bottom: 16px;
:deep(.el-check-tag) {
font-weight: 500;
}
}
&__risk-label {
display: inline-flex;
align-items: center;
color: #606266;
font-weight: 600;
margin-right: 4px;
}
&__risk-help {
margin-left: 4px;
color: #a8abb2;
cursor: help;
font-size: 14px;
}
&__risk-none {
color: #c0c4cc;
}
}
.project-apply-dialog__footer {
@@ -50,11 +50,13 @@
</el-tag>
</template>
<template #projectName-label>项目额度信息</template>
<template #projectName-form>
<el-select
v-model="selectedProjectId"
class="temporary-credit-limit-page__field"
placeholder="请选择项目"
placeholder="请选择"
filterable
clearable
:loading="projectLoading"
@@ -75,7 +77,7 @@
<el-input
v-model="form.applyLimit"
class="temporary-credit-limit-page__field"
placeholder="请输入申请临时额度"
placeholder="请输入"
:disabled="dialogReadonly"
@input="handleAmountInput"
/>
@@ -822,7 +824,7 @@ export default {
.el-form-item__label {
height: auto !important;
line-height: 18px;
padding-top: 7px; // 视觉补偿:让单行 label 仍接近 input 中线
//padding-top: 7px; // 视觉补偿:让单行 label 仍接近 input 中线
}
// 强制必填星号垂直居中于行盒,与第一行汉字字符中心同基线(关键)
@@ -841,4 +843,8 @@ export default {
.el-form-item--default .el-form-item__label {
height: auto !important;
}
.el-dialog .avue-form{
padding: 16px 16px 4px !important;
}
</style>
+35 -35
View File
@@ -179,6 +179,40 @@
</div>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="航区" prop="navigationArea">
<div class="navigation-area-field">
<el-select v-model="shipForm.navigationArea" placeholder="请选择">
<el-option label="A级" value="A级" />
<el-option label="B级" value="B级" />
</el-select>
<el-tooltip effect="light" placement="top" popper-class="ship-navigation-tooltip">
<template #content>
<table class="navigation-cargo-table">
<tbody>
<tr>
<th rowspan="2">参考<br />载货量</th>
<th>航区</th>
<th>A级</th>
<th>B级</th>
<th>FA</th>
<th>FB</th>
</tr>
<tr>
<th>载货量(t)</th>
<td>864.00</td>
<td>864.00</td>
<td>965.00</td>
<td>1015.00</td>
</tr>
</tbody>
</table>
</template>
<el-icon class="navigation-info-icon"><InfoFilled /></el-icon>
</el-tooltip>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
@@ -231,40 +265,6 @@
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="航区" prop="navigationArea">
<div class="navigation-area-field">
<el-select v-model="shipForm.navigationArea" placeholder="请选择">
<el-option label="A级" value="A级" />
<el-option label="B级" value="B级" />
</el-select>
<el-tooltip effect="light" placement="top" popper-class="ship-navigation-tooltip">
<template #content>
<table class="navigation-cargo-table">
<tbody>
<tr>
<th rowspan="2">参考<br />载货量</th>
<th>航区</th>
<th>A级</th>
<th>B级</th>
<th>FA</th>
<th>FB</th>
</tr>
<tr>
<th>载货量(t)</th>
<td>864.00</td>
<td>864.00</td>
<td>965.00</td>
<td>1015.00</td>
</tr>
</tbody>
</table>
</template>
<el-icon class="navigation-info-icon"><InfoFilled /></el-icon>
</el-tooltip>
</div>
</el-form-item>
</el-col>
</el-row>
</section-card>
@@ -1351,7 +1351,7 @@ export default {
:deep(.ship-uploader--large) {
display: block;
width: 240px;
width: 248px;
margin: 0;
}
+271 -22
View File
@@ -120,15 +120,252 @@
<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-dialog
v-model="certificationAuditBox"
title="车辆认证审核"
width="92%"
top="4vh"
class="certification-audit-dialog"
>
<el-form label-position="right" label-width="auto" class="certification-audit archive-form">
<section-card title="基础信息">
<el-row :gutter="18">
<el-col :span="24">
<el-form-item label="所属组织">
<span class="certification-audit__value">{{ auditValue('organizationName') }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="车牌号">
<span class="certification-audit__value">{{ auditValue('plateNo') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="车牌颜色">
<span class="certification-audit__value">{{ auditValue('plateColor') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="车辆类型">
<span class="certification-audit__value">{{ auditValue('vehicleType') }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="业务关系">
<span class="certification-audit__value">{{ auditValue('businessRelation') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="能源类型">
<span class="certification-audit__value">{{ auditValue('energyType') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="强制报废日期">
<span class="certification-audit__value">
{{ auditValue('compulsoryScrapDate') }}
</span>
</el-form-item>
</el-col>
</el-row>
</section-card>
<section-card title="尺寸重量">
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="外廓长度(毫米)">
<span class="certification-audit__value">{{ auditValue('outerLength') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="外廓宽度(毫米)">
<span class="certification-audit__value">{{ auditValue('outerWidth') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="外廓高度(毫米)">
<span class="certification-audit__value">{{ auditValue('outerHeight') }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="核定载质量(KG">
<span class="certification-audit__value">{{ auditValue('approvedLoadKg') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="准牵引总质量(KG">
<span class="certification-audit__value">{{ auditValue('tractionMassKg') }}</span>
</el-form-item>
</el-col>
</el-row>
</section-card>
<section-card title="证件信息">
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="海关备案号">
<span class="certification-audit__value">{{ auditValue('customsRecordNo') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="行驶证档案编号">
<span class="certification-audit__value">{{ auditValue('drivingLicenseNo') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="行驶证有效期起">
<span class="certification-audit__value">
{{ auditValue('drivingLicenseStartDate') }}
</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="行驶证有效期止">
<span class="certification-audit__value">
{{ auditValue('drivingLicenseEndDate') }}
</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="道路运输证号">
<span class="certification-audit__value">
{{ auditValue('roadTransportCertNo') }}
</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="道路运输证有效期起">
<span class="certification-audit__value">
{{ auditValue('roadTransportCertStartDate') }}
</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="道路运输证有效期止">
<span class="certification-audit__value">
{{ auditValue('roadTransportCertEndDate') }}
</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="道路运输年审有效期">
<span class="certification-audit__value">
{{ auditValue('annualReviewEndDate') }}
</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="机动车登记编号">
<span class="certification-audit__value">{{ auditValue('registrationNo') }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="机动车登记日期">
<span class="certification-audit__value">{{ auditValue('registrationDate') }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="24">
<el-form-item label="备注">
<span class="certification-audit__value">{{ auditValue('remark') }}</span>
</el-form-item>
</el-col>
</el-row>
</section-card>
<section-card title="证件图片">
<el-row :gutter="18">
<el-col :span="8">
<image-upload-field
label="行驶证主页正面"
prop="drivingLicenseImage"
:value="certificationAuditForm.drivingLicenseImage"
readonly
large
class-prefix="vehicle"
/>
</el-col>
<el-col :span="8">
<image-upload-field
label="行驶证主页反面"
prop="drivingLicenseMainBack"
:value="certificationAuditForm.drivingLicenseMainBack"
readonly
large
class-prefix="vehicle"
/>
</el-col>
<el-col :span="8">
<image-upload-field
label="行驶证副页正面"
prop="drivingLicenseViceFront"
:value="certificationAuditForm.drivingLicenseViceFront"
readonly
large
class-prefix="vehicle"
/>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<image-upload-field
label="行驶证副页反面"
prop="drivingLicenseViceBack"
:value="certificationAuditForm.drivingLicenseViceBack"
readonly
large
class-prefix="vehicle"
/>
</el-col>
<el-col :span="8">
<image-upload-field
label="道路运输证图片"
prop="roadTransportCertImage"
:value="certificationAuditForm.roadTransportCertImage"
readonly
large
class-prefix="vehicle"
/>
</el-col>
<el-col :span="8">
<image-upload-field
label="机动车登记本"
prop="registrationImage"
:value="certificationAuditForm.registrationImage"
readonly
large
class-prefix="vehicle"
/>
</el-col>
</el-row>
</section-card>
</el-form>
<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-input
v-model="certificationRejectReason"
type="textarea"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-form>
<template #footer>
@@ -731,18 +968,6 @@ 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' },
@@ -849,8 +1074,19 @@ export default {
this.certificationAuditBox = true;
});
},
formatAuditValue(item) {
const value = this.certificationAuditForm[item.prop];
auditValue(prop) {
// -
const longTermMap = {
drivingLicenseEndDate: 'drivingLicenseLongTerm',
roadTransportCertEndDate: 'roadTransportCertLongTerm',
annualReviewEndDate: 'annualReviewLongTerm',
compulsoryScrapDate: 'compulsoryScrapLongTerm',
};
const longTermProp = longTermMap[prop];
if (longTermProp && this.certificationAuditForm[longTermProp] === 1) {
return '长期有效';
}
const value = this.certificationAuditForm[prop];
return value === undefined || value === null || value === '' ? '-' : value;
},
handleCertificationApprove() {
@@ -1422,6 +1658,19 @@ export default {
}
}
// + section-card
.certification-audit {
&__value {
display: block;
color: #303133;
word-break: break-all;
}
&__reason {
margin-top: 12px;
}
}
.vehicle-form {
:deep(.el-date-editor.el-input),
:deep(.el-date-editor.el-input__wrapper),
+19 -29
View File
@@ -1181,24 +1181,25 @@
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="临时额度申请使用期限">
<div class="score-detail-form__range">
<el-date-picker
v-model="currentScore.tempCreditStartDate"
type="date"
value-format="YYYY-MM-DD"
/>
<span></span>
<el-date-picker
v-model="currentScore.tempCreditEndDate"
type="date"
value-format="YYYY-MM-DD"
/>
</div>
<el-col :span="6">
<el-form-item label="临时申请额度有效期起">
<el-date-picker
v-model="currentScore.tempCreditStartDate"
type="date"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="6">
<el-form-item label="临时申请额度有效期止">
<el-date-picker
v-model="currentScore.tempCreditEndDate"
type="date"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="评分量化表" required>
<el-select
v-model="currentScore.quantificationId"
@@ -4898,10 +4899,10 @@ export default {
width: 108px !important;
min-width: 108px !important;
color: #70757a;
font-size: 15px;
font-size: 14px;
white-space: normal !important;
word-break: break-all;
line-height: 1.4;
line-height: 1.2;
}
:deep(.el-form-item__content > .el-input),
@@ -4911,17 +4912,6 @@ export default {
}
}
.score-detail-form__range {
display: flex;
align-items: center;
gap: 14px;
:deep(.el-date-editor) {
flex: 1;
min-width: 0;
}
}
.score-category-table {
width: calc(100% - 72px);
margin: 8px 36px 16px;
+13 -13
View File
@@ -47,26 +47,26 @@ export default ({ mode, command }) => {
__VUE_I18N_LEGACY_API__: true,
__INTLIFY_PROD_DEVTOOLS__: false,
},
server: {
port: 2888,
proxy: {
'/api': {
target: 'http://localhost',
//target: 'https://saber3.bladex.cn/api',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, ''),
},
},
},
// server: {
// port: 2889,
// port: 2888,
// proxy: {
// '/api': {
// target: 'http://172.16.203.228:8000',
// target: 'http://localhost',
// //target: 'https://saber3.bladex.cn/api',
// changeOrigin: true,
// rewrite: path => path.replace(/^\/api/, ''),
// },
// },
// },
server: {
port: 2889,
proxy: {
'/api': {
target: 'http://172.16.203.228:8000',
changeOrigin: true,
},
},
},
resolve: {
alias: {
'~': resolve(__dirname, './'),