From 7c6ac2e5db334a8bf40e2d5327ea70a789971040 Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Fri, 2 Oct 2026 17:15:03 +0800 Subject: [PATCH 01/47] =?UTF-8?q?:sparkles:=20=E6=96=B0=E5=A2=9E=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E5=AE=A1=E6=A0=B8-=E5=8F=B8=E6=9C=BA/=E8=BD=A6?= =?UTF-8?q?=E8=BE=86=E9=A1=B5=EF=BC=8C=E5=AE=A1=E6=A0=B8=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E6=8C=89=E5=AD=97=E6=AE=B5=E7=BA=A7=20diff=20=E5=AF=B9?= =?UTF-8?q?=E6=AF=94=E5=BE=85=E5=AE=A1=E5=80=BC=E4=B8=8E=E7=94=9F=E6=95=88?= =?UTF-8?q?=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 审核对象由「司机行 / 车辆行」改为「一条认证记录」,故新增两个待审列表页 (挂在运力管理下,path /transportCapacity/cert-audit-driver|vehicle,与页面 文件名一一对应)与审核详情弹窗: - api/transportCapacity/certification-audit.js:pending-page / detail / audit 三个接口 - utils/cert-audit.js:快照字段元数据(中文名、控件类型、分组)与两侧快照的 字段级 diff 计算。两侧字段名一致(同一套快照结构),故逐键比较;日期归一化后 判等、长期有效联动展示、未登记字段兜底进「其他字段」 - components/cert-audit-diff:当前生效值与待审提交值并排展示,差异行高亮并标注 新增 / 已修改 / 已清空,支持切换只看变更项;从未认证通过(生效快照为空)时 显示「无对比基准」,不报错 - components/cert-audit-detail:审核详情弹窗(提交信息 + diff + 审核意见), 通过 / 驳回动作。驳回原因前端先拦一道(必填且不超过 200 字),通过时不提交原因; 已终态记录不可再审 - components/cert-audit-list + 两个菜单页:分页与按展示键(司机姓名 / 车牌号)筛选, 审核链接按 cert_certification_audit_{driver,vehicle}_audit 权限点控制 检索过的现有实现:driver.vue / vehicle.vue 的列表与弹窗写法(Avue option + search、 权限点 hasPermission 写法)、image-upload-field、section-card、utils/table-menu.js。 未找到可复用的快照或 diff 实现,故新增 utils/cert-audit.js 纯函数承担比对; 两个列表页除 bizType 与权限点外完全相同,故抽出通用列表组件承接分页/搜索, 避免两页逻辑各自演化。 --- .../transportCapacity/certification-audit.js | 48 +++ .../transportCapacity/cert-audit-driver.js | 49 +++ .../transportCapacity/cert-audit-vehicle.js | 49 +++ src/utils/cert-audit.js | 393 ++++++++++++++++++ .../transportCapacity/cert-audit-driver.vue | 30 ++ .../transportCapacity/cert-audit-vehicle.vue | 31 ++ .../components/cert-audit-detail/main.vue | 279 +++++++++++++ .../components/cert-audit-diff/main.vue | 246 +++++++++++ .../components/cert-audit-list/main.vue | 144 +++++++ 9 files changed, 1269 insertions(+) create mode 100644 src/api/transportCapacity/certification-audit.js create mode 100644 src/option/transportCapacity/cert-audit-driver.js create mode 100644 src/option/transportCapacity/cert-audit-vehicle.js create mode 100644 src/utils/cert-audit.js create mode 100644 src/views/transportCapacity/cert-audit-driver.vue create mode 100644 src/views/transportCapacity/cert-audit-vehicle.vue create mode 100644 src/views/transportCapacity/components/cert-audit-detail/main.vue create mode 100644 src/views/transportCapacity/components/cert-audit-diff/main.vue create mode 100644 src/views/transportCapacity/components/cert-audit-list/main.vue diff --git a/src/api/transportCapacity/certification-audit.js b/src/api/transportCapacity/certification-audit.js new file mode 100644 index 0000000..931505b --- /dev/null +++ b/src/api/transportCapacity/certification-audit.js @@ -0,0 +1,48 @@ +import request from '@/axios'; + +/** + * 待审列表:只含「审核中」的认证记录,按提交时间倒序 + * 审核对象是**一条认证记录**,不是一辆车或一名司机,故必须带 bizType 区分 + */ +export const getPendingPage = (bizType, current, size, params) => { + return request({ + url: '/blade-transport/certification-audit/pending-page', + method: 'get', + params: { + ...params, + bizType, + current, + size, + }, + }); +}; + +/** + * 审核详情:同时返回本次待审快照与该对象当前生效快照,供前端做字段级对比 + * 从未认证通过时 effectiveSnapshotJson 为空串 + */ +export const getAuditDetail = id => { + return request({ + url: '/blade-transport/certification-audit/detail', + method: 'get', + params: { + id, + }, + }); +}; + +/** + * 审核一条认证记录 + * 通过时不传驳回原因;驳回时必须传,且原因不超过 200 字 + */ +export const auditCertification = (id, pass, rejectReason) => { + return request({ + url: '/blade-transport/certification-audit/audit', + method: 'post', + params: { + id, + pass, + ...(rejectReason ? { rejectReason } : {}), + }, + }); +}; diff --git a/src/option/transportCapacity/cert-audit-driver.js b/src/option/transportCapacity/cert-audit-driver.js new file mode 100644 index 0000000..ca64df4 --- /dev/null +++ b/src/option/transportCapacity/cert-audit-driver.js @@ -0,0 +1,49 @@ +export const option = { + height: 'auto', + calcHeight: 32, + tip: false, + searchShow: true, + searchMenuSpan: 24, + searchIcon: true, + searchIndex: 4, + searchMenuPosition: 'right', + border: true, + index: true, + indexLabel: '序号', + indexWidth: 70, + selection: false, + addBtn: false, + editBtn: false, + delBtn: false, + viewBtn: false, + columnBtn: false, + menuWidth: 110, + column: [ + { + label: '司机姓名', + prop: 'bizKey', + search: true, + searchOrder: 1, + minWidth: 140, + }, + { + label: '审核状态', + prop: 'statusText', + slot: true, + width: 110, + align: 'center', + }, + { + label: '提交人', + prop: 'submitUserName', + minWidth: 120, + }, + { + label: '提交时间', + prop: 'submitTime', + slot: true, + minWidth: 170, + align: 'center', + }, + ], +}; diff --git a/src/option/transportCapacity/cert-audit-vehicle.js b/src/option/transportCapacity/cert-audit-vehicle.js new file mode 100644 index 0000000..8111393 --- /dev/null +++ b/src/option/transportCapacity/cert-audit-vehicle.js @@ -0,0 +1,49 @@ +export const option = { + height: 'auto', + calcHeight: 32, + tip: false, + searchShow: true, + searchMenuSpan: 24, + searchIcon: true, + searchIndex: 4, + searchMenuPosition: 'right', + border: true, + index: true, + indexLabel: '序号', + indexWidth: 70, + selection: false, + addBtn: false, + editBtn: false, + delBtn: false, + viewBtn: false, + columnBtn: false, + menuWidth: 110, + column: [ + { + label: '车牌号', + prop: 'bizKey', + search: true, + searchOrder: 1, + minWidth: 140, + }, + { + label: '审核状态', + prop: 'statusText', + slot: true, + width: 110, + align: 'center', + }, + { + label: '提交人', + prop: 'submitUserName', + minWidth: 120, + }, + { + label: '提交时间', + prop: 'submitTime', + slot: true, + minWidth: 170, + align: 'center', + }, + ], +}; diff --git a/src/utils/cert-audit.js b/src/utils/cert-audit.js new file mode 100644 index 0000000..f248e1f --- /dev/null +++ b/src/utils/cert-audit.js @@ -0,0 +1,393 @@ +/** + * 认证审核通用工具 + * + * 认证对象(司机 / 车辆)每次提交产生一条不可变快照记录,「审核」针对的是**一条记录**。 + * 记录分两侧:本次**待审快照**与该对象**当前生效快照**(最后一次审核通过的那条)。 + * 待审内容在审核通过前不影响生效值,所以审核页必须逐字段对比两侧,而不是只看一条当前值。 + * + * 本文件提供三件事: + * 1. 审核状态文案与标签(与后端 statusText 同一套词表); + * 2. 快照字段的展示元数据(中文名、控件类型、分组); + * 3. 两侧快照的字段级 diff 计算,含「从未认证通过、无对比基准」的处理。 + * + * 两侧快照字段名完全一致(同一套结构),故可逐键比较。 + */ + +const EMPTY_TEXT = '—'; + +const TYPE_IMAGE = 'image'; +const TYPE_DATE = 'date'; +const TYPE_LONG_TERM = 'longTerm'; + +/** 审核状态取值 */ +export const CERT_RECORD_STATUS = { + PENDING: 0, + APPROVED: 1, + REJECTED: 2, + VOIDED: 3, +}; + +/** 状态文案统一为:审核中 / 已通过 / 已驳回 / 已作废 */ +const CERT_STATUS_TAG = { + 0: { label: '审核中', type: 'warning' }, + 1: { label: '已通过', type: 'success' }, + 2: { label: '已驳回', type: 'danger' }, + 3: { label: '已作废', type: 'info' }, +}; + +/** 变更类型 → 差异标签 */ +export const CERT_CHANGE_TAG = { + added: { label: '新增', type: 'success' }, + modified: { label: '已修改', type: 'warning' }, + cleared: { label: '已清空', type: 'danger' }, +}; + +/** + * 「最新提交状态」展示缓存 → 标签 + * + * 缓存为空表示该对象从未提交过认证(即没有生效快照)。 + * 注意它只是最近一次提交的状态,不等于「是否已认证」——后者由生效快照指针决定。 + */ +export const certStatusTag = status => { + if (status === null || status === undefined || status === '') { + return { label: '未认证', type: 'info' }; + } + return CERT_STATUS_TAG[status] || { label: EMPTY_TEXT, type: 'info' }; +}; + +const pad = value => String(value).padStart(2, '0'); + +/** 后端若改用时间戳毫秒序列化日期,也能落成日期文案 */ +const EPOCH_PATTERN = /^\d{13}$/; + +const isEpochText = text => EPOCH_PATTERN.test(text); + +const toDate = text => { + const date = new Date(Number(text)); + return Number.isNaN(date.getTime()) ? null : date; +}; + +/** 提交时间等日期时间展示,兼容 `yyyy-MM-dd HH:mm:ss`、ISO 串与时间戳毫秒 */ +export const formatCertDateTime = value => { + if (value === null || value === undefined || value === '') return EMPTY_TEXT; + const text = String(value).trim(); + const matched = text.match( + /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})[T ](\d{1,2}):(\d{1,2})(?::(\d{1,2}))?/ + ); + if (matched) { + return ( + `${matched[1]}-${pad(matched[2])}-${pad(matched[3])} ` + + `${pad(matched[4])}:${pad(matched[5])}:${pad(matched[6] || 0)}` + ); + } + const date = isEpochText(text) ? toDate(text) : null; + if (!date) return text; + return ( + `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` + + `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}` + ); +}; + +/** + * 快照字段元数据 + * + * 字段名与后端提交 DTO 的 JSON 键一致(司机 PersonCertSubmitRequest / 车辆 VehicleCertSubmitRequest), + * 两侧快照共用同一套结构,故同时用于标签与比较规则。 + */ +const CERT_SNAPSHOT_GROUPS = { + driver: [ + { + title: '身份信息', + fields: [ + { key: 'realName', label: '真实姓名' }, + { key: 'idCardNo', label: '身份证号' }, + { key: 'idCardFrontUrl', label: '身份证正面照', type: TYPE_IMAGE }, + { key: 'idCardBackUrl', label: '身份证反面照', type: TYPE_IMAGE }, + { + key: 'idCardValidStart', + label: '身份证有效期起', + type: TYPE_DATE, + longTermKey: 'idCardLongTerm', + }, + { + key: 'idCardValidEnd', + label: '身份证有效期止', + type: TYPE_DATE, + longTermKey: 'idCardLongTerm', + }, + { key: 'idCardLongTerm', label: '身份证长期有效', type: TYPE_LONG_TERM }, + { key: 'headPhoto', label: '大头照', type: TYPE_IMAGE }, + ], + }, + { + title: '驾驶证信息', + fields: [ + { key: 'driverFileNo', label: '驾驶证档案编号' }, + { key: 'driverClass', label: '准驾车型' }, + { key: 'driverLicenseFrontUrl', label: '驾驶证主页照片', type: TYPE_IMAGE }, + { key: 'driverLicenseBackUrl', label: '驾驶证副页照片', type: TYPE_IMAGE }, + { + key: 'driverValidStart', + label: '驾驶证有效期起', + type: TYPE_DATE, + longTermKey: 'driverLongTerm', + }, + { + key: 'driverValidEnd', + label: '驾驶证有效期止', + type: TYPE_DATE, + longTermKey: 'driverLongTerm', + }, + { key: 'driverLongTerm', label: '驾驶证长期有效', type: TYPE_LONG_TERM }, + ], + }, + { + title: '从业资格证信息', + fields: [ + { key: 'qualificationNo', label: '从业资格证号' }, + { key: 'qualificationUrl', label: '从业资格证照片', type: TYPE_IMAGE }, + { key: 'qualificationValidEnd', label: '从业资格证有效期止', type: TYPE_DATE }, + ], + }, + ], + vehicle: [ + { + title: '车辆基本信息', + fields: [ + { key: 'plateNo', label: '车牌号' }, + { key: 'vehicleType', label: '车辆类型' }, + { key: 'plateColor', label: '车牌颜色' }, + { key: 'vin', label: '车辆识别代号(车架号)' }, + { key: 'engineNo', label: '发动机号' }, + ], + }, + { + title: '行驶证信息', + fields: [ + { key: 'licenseNo', label: '行驶证档案编号' }, + { key: 'licenseFrontUrl', label: '行驶证主页照片', type: TYPE_IMAGE }, + { key: 'licenseBackUrl', label: '行驶证副页照片', type: TYPE_IMAGE }, + { + key: 'licenseValidStart', + label: '行驶证有效期起', + type: TYPE_DATE, + longTermKey: 'licenseLongTerm', + }, + { + key: 'licenseValidEnd', + label: '行驶证有效期止', + type: TYPE_DATE, + longTermKey: 'licenseLongTerm', + }, + { key: 'licenseLongTerm', label: '行驶证长期有效', type: TYPE_LONG_TERM }, + ], + }, + { + title: '道路运输证信息', + fields: [ + { key: 'roadTransportNo', label: '道路运输证号' }, + { key: 'roadTransportUrl', label: '道路运输证照片', type: TYPE_IMAGE }, + { + key: 'roadTransportValidStart', + label: '道路运输证有效期起', + type: TYPE_DATE, + longTermKey: 'roadTransportLongTerm', + }, + { + key: 'roadTransportValidEnd', + label: '道路运输证有效期止', + type: TYPE_DATE, + longTermKey: 'roadTransportLongTerm', + }, + { key: 'roadTransportLongTerm', label: '道路运输证长期有效', type: TYPE_LONG_TERM }, + ], + }, + ], +}; + +/** 提交元信息与主键不参与对比(两侧必相同,展示出来只是噪音) */ +const IGNORED_KEYS = [ + 'id', + 'bizType', + 'bizId', + 'bizKey', + 'certificationStatus', + 'snapshotJson', + 'pendingSnapshotJson', + 'effectiveSnapshotJson', +]; + +/** + * 解析快照 JSON + * + * 后端把 null 序列化为空串,故空串 / 'null' 都按「没有快照」处理,不算解析失败。 + */ +const readSnapshot = json => { + if (json && typeof json === 'object') { + return { value: Object.keys(json).length ? json : null, invalid: false }; + } + const text = String(json === null || json === undefined ? '' : json).trim(); + if (!text || text === 'null') { + return { value: null, invalid: false }; + } + try { + const parsed = JSON.parse(text); + if (parsed && typeof parsed === 'object') { + return { value: parsed, invalid: false }; + } + return { value: null, invalid: true }; + } catch (error) { + return { value: null, invalid: true }; + } +}; + +/** 解析快照 JSON,无快照或解析失败时返回 null */ +export const parseSnapshot = json => readSnapshot(json).value; + +const isEmptyValue = value => value === null || value === undefined || value === ''; + +const isLongTermOn = value => value === 1 || value === '1' || value === true; + +/** 日期归一化到 yyyy-MM-dd,便于 `2026-01-01` 与 `2026-01-01T00:00:00` 判等 */ +const normalizeDate = value => { + if (isEmptyValue(value)) return ''; + const text = String(value).trim(); + const matched = text.match(/^(\d{4})[-/](\d{1,2})[-/](\d{1,2})/); + if (matched) return `${matched[1]}-${pad(matched[2])}-${pad(matched[3])}`; + const date = isEpochText(text) ? toDate(text) : null; + if (date) { + return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`; + } + return text; +}; + +/** 比较用的归一化取值:展示相同即视为未变更 */ +const canonicalOf = (snapshot, field) => { + const value = snapshot ? snapshot[field.key] : undefined; + if (field.type === TYPE_LONG_TERM) { + if (isEmptyValue(value)) return ''; + return isLongTermOn(value) ? '1' : '0'; + } + if (field.type === TYPE_DATE) { + if (field.longTermKey && isLongTermOn(snapshot && snapshot[field.longTermKey])) { + return '长期有效'; + } + return normalizeDate(value); + } + return isEmptyValue(value) ? '' : String(value).trim(); +}; + +/** 展示文案:日期型受「长期有效」影响,图片型返回 URL 由组件渲染缩略图 */ +const textOf = (snapshot, field) => { + const value = snapshot ? snapshot[field.key] : undefined; + if (field.type === TYPE_IMAGE) { + return isEmptyValue(value) ? '' : String(value); + } + if (field.type === TYPE_LONG_TERM) { + if (isEmptyValue(value)) return EMPTY_TEXT; + return isLongTermOn(value) ? '是' : '否'; + } + if (field.type === TYPE_DATE) { + if (field.longTermKey && isLongTermOn(snapshot && snapshot[field.longTermKey])) { + return '长期有效'; + } + return normalizeDate(value) || EMPTY_TEXT; + } + return isEmptyValue(value) ? EMPTY_TEXT : String(value); +}; + +const buildRow = (field, pendingSnapshot, effectiveSnapshot, hasBaseline) => { + const type = field.type || 'text'; + const target = { ...field, type }; + const pendingCanonical = canonicalOf(pendingSnapshot, target); + const effectiveCanonical = canonicalOf(effectiveSnapshot, target); + let changeType = ''; + if (hasBaseline && pendingCanonical !== effectiveCanonical) { + if (!effectiveCanonical) { + changeType = 'added'; + } else if (!pendingCanonical) { + changeType = 'cleared'; + } else { + changeType = 'modified'; + } + } + return { + key: field.key, + label: field.label, + type, + effective: { + raw: effectiveSnapshot ? effectiveSnapshot[field.key] : undefined, + text: textOf(effectiveSnapshot, target), + }, + pending: { + raw: pendingSnapshot ? pendingSnapshot[field.key] : undefined, + text: textOf(pendingSnapshot, target), + }, + changed: Boolean(changeType), + changeType, + }; +}; + +const unionKeys = (pendingSnapshot, effectiveSnapshot) => { + const keys = []; + [pendingSnapshot, effectiveSnapshot].forEach(snapshot => { + Object.keys(snapshot || {}).forEach(key => { + if (!keys.includes(key)) keys.push(key); + }); + }); + return keys; +}; + +/** + * 构造「待审快照 vs 当前生效值」的字段级 diff + * + * @param {string} bizType 认证对象类型:driver / vehicle + * @param {string} pendingJson 本次待审快照 JSON + * @param {string} effectiveJson 该对象当前生效快照 JSON,从未认证通过时为空 + * @returns {Object} { hasBaseline, groups, totalCount, changedCount, ... } + * hasBaseline 为 false 表示无对比基准(从未认证通过),此时不产出任何变更标记 + */ +export const buildSnapshotDiff = (bizType, pendingJson, effectiveJson) => { + const pending = readSnapshot(pendingJson); + const effective = readSnapshot(effectiveJson); + const hasBaseline = effective.value !== null; + const groupsConfig = CERT_SNAPSHOT_GROUPS[bizType] || []; + const knownKeys = []; + + const groups = groupsConfig.map(group => { + const rows = group.fields.map(field => { + knownKeys.push(field.key); + return buildRow(field, pending.value, effective.value, hasBaseline); + }); + return { + title: group.title, + rows, + changedCount: rows.filter(row => row.changed).length, + }; + }); + + // 元数据未登记的字段照样展示,避免后端新增字段时审核员看不到变化 + const extraKeys = unionKeys(pending.value, effective.value).filter( + key => !knownKeys.includes(key) && !IGNORED_KEYS.includes(key) + ); + if (extraKeys.length) { + const rows = extraKeys.map(key => + buildRow({ key, label: key }, pending.value, effective.value, hasBaseline) + ); + groups.push({ + title: '其他字段', + rows, + changedCount: rows.filter(row => row.changed).length, + }); + } + + const rows = groups.reduce((all, group) => all.concat(group.rows), []); + return { + hasBaseline, + pendingInvalid: pending.invalid, + effectiveInvalid: effective.invalid, + groups, + totalCount: rows.length, + changedCount: rows.filter(row => row.changed).length, + }; +}; diff --git a/src/views/transportCapacity/cert-audit-driver.vue b/src/views/transportCapacity/cert-audit-driver.vue new file mode 100644 index 0000000..c746b2b --- /dev/null +++ b/src/views/transportCapacity/cert-audit-driver.vue @@ -0,0 +1,30 @@ + + + diff --git a/src/views/transportCapacity/cert-audit-vehicle.vue b/src/views/transportCapacity/cert-audit-vehicle.vue new file mode 100644 index 0000000..3c3786a --- /dev/null +++ b/src/views/transportCapacity/cert-audit-vehicle.vue @@ -0,0 +1,31 @@ + + + diff --git a/src/views/transportCapacity/components/cert-audit-detail/main.vue b/src/views/transportCapacity/components/cert-audit-detail/main.vue new file mode 100644 index 0000000..825f665 --- /dev/null +++ b/src/views/transportCapacity/components/cert-audit-detail/main.vue @@ -0,0 +1,279 @@ + + + + + diff --git a/src/views/transportCapacity/components/cert-audit-diff/main.vue b/src/views/transportCapacity/components/cert-audit-diff/main.vue new file mode 100644 index 0000000..1137418 --- /dev/null +++ b/src/views/transportCapacity/components/cert-audit-diff/main.vue @@ -0,0 +1,246 @@ + + + + + diff --git a/src/views/transportCapacity/components/cert-audit-list/main.vue b/src/views/transportCapacity/components/cert-audit-list/main.vue new file mode 100644 index 0000000..3f95103 --- /dev/null +++ b/src/views/transportCapacity/components/cert-audit-list/main.vue @@ -0,0 +1,144 @@ + + + From 71a6f7c4f8e7d3253380e4c25842cfb48eb2ec5c Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Fri, 2 Oct 2026 17:15:09 +0800 Subject: [PATCH 02/47] =?UTF-8?q?:recycle:=20=E8=BD=A6=E8=BE=86=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=A7=BB=E9=99=A4=E6=97=A7=E8=AE=A4=E8=AF=81=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8=E5=85=A5=E5=8F=A3=EF=BC=8C=E8=AE=A4=E8=AF=81=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=94=B9=E7=94=A8=E7=BB=9F=E4=B8=80=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E8=AF=8D=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 认证审核职责已迁到「认证审核-车辆」页:审核对象是一条认证记录,生效值与 待审值分离后,原来「看车辆行当前值直接通过/驳回」的单条模型语义不再成立。 - vehicle.vue:移除操作列的「审核」入口与认证审核弹窗及其方法、样式, 同时删除 api/transportCapacity/transport-vehicle.js 的 auditCertification 封装 (后端旧接口仅保留兼容,前端不再调用) - 认证状态列与筛选项统一为 审核中 / 已通过 / 已驳回 / 已作废(新增 已作废, 原先的「认证中/已认证」不再使用),缓存为空时显示「未认证」 - 去掉按证件到期日期推断「已认证 / 已过期」的兜底:是否已认证由生效快照指针 决定(曾审核通过即永久保持),与证件日期无关;到期信息仍由列表顶部到期标签 与日期列标色展示 - driver.vue 选择车辆弹窗的车辆认证状态文案一并统一 --- .../transportCapacity/transport-vehicle.js | 8 - .../transportCapacity/transport-vehicle.js | 7 +- src/views/transportCapacity/driver.vue | 9 +- src/views/transportCapacity/vehicle.vue | 394 +----------------- 4 files changed, 14 insertions(+), 404 deletions(-) diff --git a/src/api/transportCapacity/transport-vehicle.js b/src/api/transportCapacity/transport-vehicle.js index f2bfb67..0eed04c 100644 --- a/src/api/transportCapacity/transport-vehicle.js +++ b/src/api/transportCapacity/transport-vehicle.js @@ -51,14 +51,6 @@ export const changeStatus = (id, status) => { }); }; -export const auditCertification = (id, certificationStatus, rejectReason) => { - return request({ - url: '/blade-transport/transport-vehicle/audit-certification', - method: 'post', - params: { id, certificationStatus, rejectReason }, - }); -}; - export const getExpiryStat = params => { return request({ url: '/blade-transport/transport-vehicle/expiry-stat', diff --git a/src/option/transportCapacity/transport-vehicle.js b/src/option/transportCapacity/transport-vehicle.js index df05326..ffe1bae 100644 --- a/src/option/transportCapacity/transport-vehicle.js +++ b/src/option/transportCapacity/transport-vehicle.js @@ -95,6 +95,8 @@ export const option = { align: 'center', }, { + // 这里是「最新一次提交的状态」缓存,不等于该车当前是否已认证: + // 已认证由生效快照指针决定,曾审核通过即永久保持,重新提交不会让它退回审核中 label: '认证状态', prop: 'certificationStatus', slot: true, @@ -104,9 +106,10 @@ export const option = { type: 'select', dicData: [ { label: '全部', value: '' }, - { label: '认证中', value: 0 }, - { label: '已认证', value: 1 }, + { label: '审核中', value: 0 }, + { label: '已通过', value: 1 }, { label: '已驳回', value: 2 }, + { label: '已作废', value: 3 }, ], minWidth: 110, align: 'center', diff --git a/src/views/transportCapacity/driver.vue b/src/views/transportCapacity/driver.vue index 1066ad6..dad42da 100644 --- a/src/views/transportCapacity/driver.vue +++ b/src/views/transportCapacity/driver.vue @@ -649,6 +649,7 @@ import { getDictionary } from '@/api/system/dictbiz'; import { getLazyTree } from '@/api/base/region'; import { exportBlob } from '@/api/common'; import { getToken, isAdministrator } from '@/utils/auth'; +import { certStatusTag } from '@/utils/cert-audit'; import { getUploadHeaders } from '@/utils/upload'; import { downloadXls } from '@/utils/util'; import ImageUploadField from '@/components/image-upload-field/main.vue'; @@ -1233,13 +1234,9 @@ export default { this.driverForm.drivingVehicle = row.plateNo || ''; this.vehicleSelectorVisible = false; }, + // 车辆认证状态展示的是「最新一次提交的状态」缓存,与审核页使用同一套状态词表 getVehicleCertificationStatus(row) { - const statusMap = { - 0: { label: '认证中', type: 'warning' }, - 1: { label: '已认证', type: 'success' }, - 2: { label: '已驳回', type: 'danger' }, - }; - return statusMap[row.certificationStatus] || { label: '-', type: 'info' }; + return certStatusTag(row.certificationStatus); }, setImage(prop, url) { this.driverForm[prop] = url; diff --git a/src/views/transportCapacity/vehicle.vue b/src/views/transportCapacity/vehicle.vue index 1bfa225..087594a 100644 --- a/src/views/transportCapacity/vehicle.vue +++ b/src/views/transportCapacity/vehicle.vue @@ -105,9 +105,6 @@ 修改 综合台账 - - 审核 - - - - - - - - {{ auditValue('plateNo') }} - - - - - {{ auditValue('vehicleType') }} - - - - -
-
- 长 - - {{ auditValueWithUnit('outerLength', 'mm') }} - -
-
- 宽 - - {{ auditValueWithUnit('outerWidth', 'mm') }} - -
-
- 高 - - {{ auditValueWithUnit('outerHeight', 'mm') }} - -
-
-
-
-
- - - - - {{ auditValueWithUnit('approvedLoadKg', 'KG') }} - - - - - - - {{ auditValueWithUnit('tractionMassKg', 'KG') }} - - - - - - {{ auditValue('businessRelation') }} - - - - - {{ auditValue('energyType') }} - - - - - - - - {{ auditValue('compulsoryScrapDate') }} - - - - - - {{ auditValue('customsRecordNo') }} - - - - - - {{ auditValue('organizationName') }} - - - - - - {{ auditValue('useDepartment') }} - - - -
- - - - - - - {{ auditValue('drivingLicenseEndDate') }} - - - - - - - {{ auditValue('roadTransportCertNo') }} - - - - - - - {{ auditValue('roadTransportCertEndDate') }} - - - - - - - {{ auditValue('annualReviewEndDate') }} - - - - - - - - {{ auditValue('registrationNo') }} - - - - - {{ auditValue('registrationDate') }} - - - - - {{ auditValue('drivingLicenseNo') }} - - - - - - {{ auditValue('drivingLicenseStartDate') }} - - - - - - - - - {{ auditValue('roadTransportCertStartDate') }} - - - - - - - - {{ auditValue('remark') }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - -
- { - this.certificationAuditForm = { ...(res.data.data || {}) }; - this.certificationRejectReason = ''; - this.showRejectReason = false; - this.certificationAuditBox = true; - }); - }, - 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; - }, - auditValueWithUnit(prop, unit) { - const value = this.auditValue(prop); - return value === '-' ? value : `${value}${unit}`; - }, - handleCertificationApprove() { - auditCertification(this.certificationAuditForm.id, 1).then(() => { - this.$message.success('认证已通过'); - this.certificationAuditBox = false; - this.onLoad(this.page); - }); - }, - handleCertificationReject() { - if (!this.showRejectReason) { this.showRejectReason = true; return; } - if (!this.certificationRejectReason.trim()) { this.$message.warning('请输入认证驳回原因'); return; } - auditCertification(this.certificationAuditForm.id, 2, this.certificationRejectReason).then(() => { - this.$message.success('认证已驳回'); - this.certificationAuditBox = false; - this.onLoad(this.page); - }); - }, resetVehicle() { this.vehicleForm = emptyForm(); this.vehicleCertificateUploads = {}; @@ -1648,26 +1333,11 @@ export default { } return ''; }, + // 认证状态展示的是「最新一次提交的状态」缓存,不代表该车当前是否已认证 + // (是否已认证由生效快照指针决定,即曾审核通过即永久保持)。 + // 证书是否临近到期另有列表顶部的到期标签与日期列标色展示,不在这里推断。 getCertificationStatus(row) { - if (row.certificationStatus === 0) return { label: '认证中', type: 'warning' }; - if (row.certificationStatus === 1) return { label: '已认证', type: 'success' }; - if (row.certificationStatus === 2) return { label: '已驳回', type: 'danger' }; - const today = this.$dayjs().startOf('day'); - const certificateDates = [ - [row.drivingLicenseEndDate, row.drivingLicenseLongTerm], - [row.roadTransportCertEndDate, row.roadTransportCertLongTerm], - ]; - if (certificateDates.some(([date, longTerm]) => longTerm !== 1 && !date)) { - return { label: '未认证', type: 'info' }; - } - if ( - certificateDates.some( - ([date, longTerm]) => longTerm !== 1 && this.$dayjs(date).isBefore(today, 'day') - ) - ) { - return { label: '已过期', type: 'danger' }; - } - return { label: '已认证', type: 'success' }; + return certStatusTag(row.certificationStatus); }, }, }; @@ -1695,9 +1365,7 @@ export default { } } -// 车辆认证审核弹窗:与编辑弹窗一致,灰底 + section-card 白卡分组 -.certification-audit, -.certification-audit__reason, +// 编辑弹窗:灰底 + section-card 白卡分组 .vehicle-form { :deep(.el-form-item__label) { height: auto; @@ -1723,60 +1391,10 @@ export default { line-height: 18px; margin-right: 4px; } -} -.certification-audit { - :deep(.el-form-item__label) { - flex: 0 0 calc(4em + 12px) !important; - width: calc(4em + 12px) !important; - max-width: calc(4em + 12px); - } -} - -.certification-audit__reason { :deep(.el-form-item__label) { flex: 0 0 calc(4em + 24px) !important; width: calc(4em + 24px) !important; - max-width: calc(4em + 24px); - } -} - -.vehicle-form { - :deep(.el-form-item__label) { - flex: 0 0 calc(4em + 24px) !important; - width: calc(4em + 24px) !important; - } -} - -.certification-audit { - &__value { - display: block; - color: #303133; - word-break: break-all; - } - - // 外廓尺寸长/宽/高组合,排版复用编辑弹窗的 dimension-group - .dimension-group { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 8px; - align-items: center; - } - - .dimension-group__item { - display: flex; - align-items: center; - gap: 8px; - min-width: 0; - } - - .dimension-group__label { - flex-shrink: 0; - color: #303133; - } - - &__reason { - margin-top: 12px; } } From b8d74de4a42f5515ec2f8e06dedb44ae963971cb Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Fri, 2 Oct 2026 19:58:17 +0800 Subject: [PATCH 03/47] =?UTF-8?q?:sparkles:=20=E8=AE=A4=E8=AF=81=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8=E5=88=97=E8=A1=A8=E6=96=B0=E5=A2=9E=E5=BE=85=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8/=E5=B7=B2=E9=80=9A=E8=BF=87/=E5=B7=B2=E9=A9=B3?= =?UTF-8?q?=E5=9B=9E=E9=A1=B5=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 审核对象是「一条认证记录」,每次提交产生一条,原列表只能看审核中的, 已通过与已驳回的记录无从查看。 三个页签默认停在待审核;切换页签回到第一页;只有审核中的记录显示「审核」, 终态记录显示「查看」(详情弹窗本就对终态禁用通过/驳回)。 --- .../transportCapacity/certification-audit.js | 3 +- .../components/cert-audit-list/main.vue | 59 +++++++++++++++++-- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/api/transportCapacity/certification-audit.js b/src/api/transportCapacity/certification-audit.js index 931505b..2501a8c 100644 --- a/src/api/transportCapacity/certification-audit.js +++ b/src/api/transportCapacity/certification-audit.js @@ -1,8 +1,9 @@ import request from '@/axios'; /** - * 待审列表:只含「审核中」的认证记录,按提交时间倒序 + * 认证记录列表:按审核状态分页,按提交时间倒序 * 审核对象是**一条认证记录**,不是一辆车或一名司机,故必须带 bizType 区分 + * status 为审核状态(0审核中 1已通过 2已驳回 3已作废);不传时后端按 0 处理 */ export const getPendingPage = (bizType, current, size, params) => { return request({ diff --git a/src/views/transportCapacity/components/cert-audit-list/main.vue b/src/views/transportCapacity/components/cert-audit-list/main.vue index 3f95103..4e34b59 100644 --- a/src/views/transportCapacity/components/cert-audit-list/main.vue +++ b/src/views/transportCapacity/components/cert-audit-list/main.vue @@ -13,6 +13,21 @@ @refresh-change="refreshChange" @on-load="onLoad" > + + @@ -38,14 +56,15 @@ + + From 715c0dc00c094441e6ee4afd647f6e75e8046e1c Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Sat, 3 Oct 2026 14:36:08 +0800 Subject: [PATCH 04/47] =?UTF-8?q?:sparkles:=20=E5=8F=B8=E6=9C=BA=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8=E4=B8=8E=E8=BD=A6=E8=BE=86=E5=AE=A1=E6=A0=B8=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=8F=B8=E6=9C=BA=E6=89=8B=E6=9C=BA=E5=8F=B7=EF=BC=8C?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=8C=89=E5=BD=93=E5=89=8D=E5=A7=93=E5=90=8D?= =?UTF-8?q?=E5=8F=96=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 两个审核页的表格与搜索栏都加上「司机手机号」,姓名列改绑司机档案的当前值 (原绑提交时的历史姓名)。同时去掉「提交人」列:真实路径下提交人就是司机本人, 两列必然同值,而存量记录本就没有提交人,留着一列空值加同值没有意义。 手机号不脱敏:审核员靠它联系司机核实材料,与司机管理页一致。 --- .../transportCapacity/cert-audit-driver.js | 18 +++++++++------- .../transportCapacity/cert-audit-vehicle.js | 21 +++++++++++++------ .../components/cert-audit-list/main.vue | 3 +++ 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/option/transportCapacity/cert-audit-driver.js b/src/option/transportCapacity/cert-audit-driver.js index ca64df4..71b9856 100644 --- a/src/option/transportCapacity/cert-audit-driver.js +++ b/src/option/transportCapacity/cert-audit-driver.js @@ -21,10 +21,17 @@ export const option = { column: [ { label: '司机姓名', - prop: 'bizKey', + prop: 'driverName', search: true, - searchOrder: 1, - minWidth: 140, + searchOrder: 3, + minWidth: 120, + }, + { + label: '司机手机号', + prop: 'driverMobile', + search: true, + searchOrder: 2, + minWidth: 130, }, { label: '审核状态', @@ -33,11 +40,6 @@ export const option = { width: 110, align: 'center', }, - { - label: '提交人', - prop: 'submitUserName', - minWidth: 120, - }, { label: '提交时间', prop: 'submitTime', diff --git a/src/option/transportCapacity/cert-audit-vehicle.js b/src/option/transportCapacity/cert-audit-vehicle.js index 8111393..5e93caf 100644 --- a/src/option/transportCapacity/cert-audit-vehicle.js +++ b/src/option/transportCapacity/cert-audit-vehicle.js @@ -23,9 +23,23 @@ export const option = { label: '车牌号', prop: 'bizKey', search: true, - searchOrder: 1, + searchOrder: 3, minWidth: 140, }, + { + label: '司机姓名', + prop: 'driverName', + search: true, + searchOrder: 2, + minWidth: 120, + }, + { + label: '司机手机号', + prop: 'driverMobile', + search: true, + searchOrder: 1, + minWidth: 130, + }, { label: '审核状态', prop: 'statusText', @@ -33,11 +47,6 @@ export const option = { width: 110, align: 'center', }, - { - label: '提交人', - prop: 'submitUserName', - minWidth: 120, - }, { label: '提交时间', prop: 'submitTime', diff --git a/src/views/transportCapacity/components/cert-audit-list/main.vue b/src/views/transportCapacity/components/cert-audit-list/main.vue index 4e34b59..4c66cdc 100644 --- a/src/views/transportCapacity/components/cert-audit-list/main.vue +++ b/src/views/transportCapacity/components/cert-audit-list/main.vue @@ -66,6 +66,9 @@ import CertAuditDetail from '../cert-audit-detail/main.vue'; * 审核对象是「一条认证记录」:每次提交产生一条,按审核状态分为 * 待审核 / 已通过 / 已驳回三个页签,默认停在待审核。 * 列表只负责挑选记录,具体改了什么由审核详情弹窗做字段级对比。 + * + * 列表上的司机姓名与手机号取的是**司机档案当前值**(认证记录里的姓名是提交时的历史值, + * 手机号则从来不在快照里)。审核员靠这两列认人、联系核实,故不脱敏。 */ export default { name: 'CertAuditList', From ce54d2134498d994a61a9c3693602f4c888ce82b Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Sat, 3 Oct 2026 16:34:59 +0800 Subject: [PATCH 05/47] =?UTF-8?q?:sparkles:=20=E6=96=B0=E5=A2=9E/=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E8=BD=A6=E8=BE=86=E8=A1=A8=E5=8D=95=E8=A1=A5=E3=80=8C?= =?UTF-8?q?=E6=89=80=E6=9C=89=E4=BA=BA=E3=80=8D=E4=B8=8E=E3=80=8C=E8=BD=A6?= =?UTF-8?q?=E8=BE=86=E8=AF=86=E5=88=AB=E4=BB=A3=E5=8F=B7=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 车辆表新增这两列(所有人:行驶证登记的单位名称,与「所属组织」是两个字段; 车辆识别代号:司机端车辆认证一直在收这个值,此前车辆档案里没有对应列, 只存在于认证快照中)。管理端表单同步补上,否则两个入口建出来的车字段不一致。 所有人为必填(行驶证上的登记主体),车辆识别代号选填、填了校验 17 位。 --- src/views/transportCapacity/vehicle.vue | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/views/transportCapacity/vehicle.vue b/src/views/transportCapacity/vehicle.vue index 087594a..42553a2 100644 --- a/src/views/transportCapacity/vehicle.vue +++ b/src/views/transportCapacity/vehicle.vue @@ -168,6 +168,24 @@ + + + + + + + + + +
@@ -560,8 +578,10 @@ const emptyForm = () => ({ organizationName: '', useDepartment: '', plateNo: '', + ownerName: '', plateColor: '', vehicleType: '', + vin: '', outerLength: undefined, outerWidth: undefined, outerHeight: undefined, @@ -650,6 +670,8 @@ export default { { validator: this.validatePlateNo, trigger: 'change' }, ], vehicleType: [{ required: true, message: '请选择车辆类型', trigger: 'change' }], + ownerName: [{ required: true, message: '请输入所有人', trigger: 'blur' }], + vin: [{ validator: this.validateVin, trigger: 'blur' }], approvedLoadKg: [{ required: true, message: '请输入核定载质量', trigger: 'blur' }], businessRelation: [{ required: true, message: '请选择业务关系', trigger: 'change' }], energyType: [{ required: true, message: '请选择能源类型', trigger: 'change' }], @@ -783,6 +805,24 @@ export default { } callback(); }, + // 车辆识别代号(车架号)为选填:留空放行,填了就必须是 17 位 + validateVin(rule, value, callback) { + const vin = String(value || '') + .trim() + .toUpperCase(); + if (!vin) { + callback(); + return; + } + if (!/^[A-HJ-NPR-Z0-9]{17}$/.test(vin)) { + callback(new Error('车架号必须是17位字母数字(不含 I、O、Q)')); + return; + } + if (this.vehicleForm.vin !== vin) { + this.vehicleForm.vin = vin; + } + callback(); + }, validateAnnualReviewEndDate(rule, value, callback) { if (this.vehicleForm.annualReviewLongTerm !== 1 && !value) { callback(new Error('请选择道路运输年审有效期')); From 069d0e92ff9921c54317acddb4fcab88a0f71798 Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Sat, 3 Oct 2026 17:57:01 +0800 Subject: [PATCH 06/47] =?UTF-8?q?:bug:=20=E4=BF=AE=E8=BD=A6=E8=BE=86?= =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E8=AF=A6=E6=83=85=E5=AD=97=E6=AE=B5=E5=85=83?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=9A=84=E6=97=A7=E9=94=AE=E5=90=8D=EF=BC=8C?= =?UTF-8?q?=E6=AD=A2=E6=9C=9F=E4=B9=8B=E5=A4=96=E4=B8=8D=E5=86=8D=E8=A2=AB?= =?UTF-8?q?=E9=95=BF=E6=9C=9F=E6=9C=89=E6=95=88=E9=81=AE=E8=94=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 车辆字段整组停留在改造前的旧键名(licenseNo / licenseValidStart / licenseValidEnd / licenseLongTerm / roadTransportNo / roadTransportValid* / engineNo),而快照早已改名成 drivingLicense* / roadTransportCert* / registration*。键名对不上不报错——每个字段都算「没有值」, 于是车辆审核详情里行驶证与道路运输证两组全显示「—」,真值全落到未翻译的「其他字段」组里 露着 camelCase 键名,审核员既看不到内容、也 diff 不出变更。 - 按 VehicleCertSubmitRequest 重写车辆四组字段(基本信息 / 行驶证 / 道路运输证 / 机动车登记本), 补上此前漏登记的所有人、外廓尺寸、核定载质量、准牵引总质量、能源类型、业务关系、机动车登记编号与六张图; - longTermKey 只挂在被长期有效作废掉的那个字段(有效期止、强制报废日期)上: 起期是独立日期,挂上去会把真实值显示成「长期有效」,还让审核员看不到起期的改动。 --- src/utils/cert-audit.js | 82 +++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/src/utils/cert-audit.js b/src/utils/cert-audit.js index f248e1f..9044ab4 100644 --- a/src/utils/cert-audit.js +++ b/src/utils/cert-audit.js @@ -93,6 +93,11 @@ export const formatCertDateTime = value => { * * 字段名与后端提交 DTO 的 JSON 键一致(司机 PersonCertSubmitRequest / 车辆 VehicleCertSubmitRequest), * 两侧快照共用同一套结构,故同时用于标签与比较规则。 + * + * `longTermKey` 只挂在**被长期有效作废掉的那个字段**上,也就是各组的「有效期止」与强制报废日期: + * 勾了长期有效,它们显示「长期有效」、比较时按同一个值算(避免每次审核都报「已变更」)。 + * 「有效期起」不挂:起期是一个独立的日期,长期有效并不会让它失效, + * 挂上去等于把它自己的值藏起来、还让审核员看不到起期的改动。 */ const CERT_SNAPSHOT_GROUPS = { driver: [ @@ -103,12 +108,7 @@ const CERT_SNAPSHOT_GROUPS = { { key: 'idCardNo', label: '身份证号' }, { key: 'idCardFrontUrl', label: '身份证正面照', type: TYPE_IMAGE }, { key: 'idCardBackUrl', label: '身份证反面照', type: TYPE_IMAGE }, - { - key: 'idCardValidStart', - label: '身份证有效期起', - type: TYPE_DATE, - longTermKey: 'idCardLongTerm', - }, + { key: 'idCardValidStart', label: '身份证有效期起', type: TYPE_DATE }, { key: 'idCardValidEnd', label: '身份证有效期止', @@ -126,12 +126,7 @@ const CERT_SNAPSHOT_GROUPS = { { key: 'driverClass', label: '准驾车型' }, { key: 'driverLicenseFrontUrl', label: '驾驶证主页照片', type: TYPE_IMAGE }, { key: 'driverLicenseBackUrl', label: '驾驶证副页照片', type: TYPE_IMAGE }, - { - key: 'driverValidStart', - label: '驾驶证有效期起', - type: TYPE_DATE, - longTermKey: 'driverLongTerm', - }, + { key: 'driverValidStart', label: '驾驶证有效期起', type: TYPE_DATE }, { key: 'driverValidEnd', label: '驾驶证有效期止', @@ -150,6 +145,13 @@ const CERT_SNAPSHOT_GROUPS = { ], }, ], + /* + * ⚠️ 车辆字段名与 PersonCertSubmitRequest 不同:这一组曾经整组用着改造前的旧键名 + * (licenseNo / licenseValidEnd / roadTransportNo …),而快照早已改名成 drivingLicense* / + * roadTransportCert*。键名对不上**不报错**——每个字段都算「没有值」, + * 于是车辆审核详情里行驶证与道路运输证两组全显示「—」,真值全落到未翻译的「其他字段」组, + * 审核员既看不到内容、也 diff 不出变更。字段名一律以 VehicleCertSubmitRequest 为准。 + */ vehicle: [ { title: '车辆基本信息', @@ -157,49 +159,57 @@ const CERT_SNAPSHOT_GROUPS = { { key: 'plateNo', label: '车牌号' }, { key: 'vehicleType', label: '车辆类型' }, { key: 'plateColor', label: '车牌颜色' }, + { key: 'ownerName', label: '所有人' }, { key: 'vin', label: '车辆识别代号(车架号)' }, - { key: 'engineNo', label: '发动机号' }, + { key: 'outerLength', label: '外廓长度(mm)' }, + { key: 'outerWidth', label: '外廓宽度(mm)' }, + { key: 'outerHeight', label: '外廓高度(mm)' }, + { key: 'approvedLoadKg', label: '核定载质量(KG)' }, + { key: 'tractionMassKg', label: '准牵引总质量(KG)' }, + { key: 'energyType', label: '能源类型' }, + { key: 'businessRelation', label: '业务关系' }, ], }, { title: '行驶证信息', fields: [ - { key: 'licenseNo', label: '行驶证档案编号' }, - { key: 'licenseFrontUrl', label: '行驶证主页照片', type: TYPE_IMAGE }, - { key: 'licenseBackUrl', label: '行驶证副页照片', type: TYPE_IMAGE }, + { key: 'compulsoryScrapDate', label: '强制报废日期', type: TYPE_DATE, longTermKey: 'compulsoryScrapLongTerm' }, + { key: 'compulsoryScrapLongTerm', label: '强制报废长期有效', type: TYPE_LONG_TERM }, + { key: 'drivingLicenseNo', label: '行驶证档案编号' }, + { key: 'drivingLicenseImage', label: '行驶证主页正面', type: TYPE_IMAGE }, + { key: 'drivingLicenseMainBack', label: '行驶证主页反面', type: TYPE_IMAGE }, + { key: 'drivingLicenseViceFront', label: '行驶证副页正面', type: TYPE_IMAGE }, + { key: 'drivingLicenseViceBack', label: '行驶证副页反面', type: TYPE_IMAGE }, + { key: 'drivingLicenseStartDate', label: '行驶证有效期起', type: TYPE_DATE }, { - key: 'licenseValidStart', - label: '行驶证有效期起', - type: TYPE_DATE, - longTermKey: 'licenseLongTerm', - }, - { - key: 'licenseValidEnd', + key: 'drivingLicenseEndDate', label: '行驶证有效期止', type: TYPE_DATE, - longTermKey: 'licenseLongTerm', + longTermKey: 'drivingLicenseLongTerm', }, - { key: 'licenseLongTerm', label: '行驶证长期有效', type: TYPE_LONG_TERM }, + { key: 'drivingLicenseLongTerm', label: '行驶证长期有效', type: TYPE_LONG_TERM }, ], }, { title: '道路运输证信息', fields: [ - { key: 'roadTransportNo', label: '道路运输证号' }, - { key: 'roadTransportUrl', label: '道路运输证照片', type: TYPE_IMAGE }, + { key: 'roadTransportCertNo', label: '道路运输证号' }, + { key: 'roadTransportCertImage', label: '道路运输证照片', type: TYPE_IMAGE }, + { key: 'roadTransportCertStartDate', label: '道路运输证有效期起', type: TYPE_DATE }, { - key: 'roadTransportValidStart', - label: '道路运输证有效期起', - type: TYPE_DATE, - longTermKey: 'roadTransportLongTerm', - }, - { - key: 'roadTransportValidEnd', + key: 'roadTransportCertEndDate', label: '道路运输证有效期止', type: TYPE_DATE, - longTermKey: 'roadTransportLongTerm', + longTermKey: 'roadTransportCertLongTerm', }, - { key: 'roadTransportLongTerm', label: '道路运输证长期有效', type: TYPE_LONG_TERM }, + { key: 'roadTransportCertLongTerm', label: '道路运输证长期有效', type: TYPE_LONG_TERM }, + ], + }, + { + title: '机动车登记本信息', + fields: [ + { key: 'registrationNo', label: '机动车登记编号' }, + { key: 'registrationImage', label: '机动车登记本照片', type: TYPE_IMAGE }, ], }, ], From 7751a735c556d865e83726e5a33f652f53d48f96 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Sun, 4 Oct 2026 20:26:19 +0800 Subject: [PATCH 07/47] =?UTF-8?q?:sparkles:=20=E4=B8=9A=E5=8A=A1=E6=95=B0?= =?UTF-8?q?=E9=87=8F=E5=8D=95=E4=BD=8D=E6=94=B9=E8=B5=B0=E8=AE=A1=E9=87=8F?= =?UTF-8?q?=E5=8D=95=E4=BD=8D=EF=BC=8C=E5=B9=B6=E6=94=BE=E5=BC=80=E8=BF=90?= =?UTF-8?q?=E8=BE=93=E8=AE=A1=E5=88=92=E6=97=A5=E6=9C=9F=E5=BF=85=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 发货模板、运输计划、运单与总单调度从计量单位取 unitName;修复调度提交顶层单位为空,并取消计划开始/结束日期必填。 Co-authored-by: Cursor --- src/option/base/measurement-unit.js | 3 + src/option/business/transport-plan.js | 2 - src/utils/measurement-unit.js | 27 ++++ .../components/billing-plan-editor.vue | 60 +++++---- .../components/business-crud-page.vue | 14 +-- .../components/master-order-dispatch.vue | 18 ++- .../components/master-order-editor.vue | 30 ++++- .../components/shipping-template-page.vue | 18 ++- .../components/transport-plan-page.vue | 115 ++++++++++++++---- .../components/waybill-manage-page.vue | 12 +- src/views/business/project-apply.vue | 17 ++- .../business/transport-plan-dispatch.vue | 73 +++++++---- src/views/system/dept.vue | 73 ++++++----- 13 files changed, 323 insertions(+), 139 deletions(-) create mode 100644 src/utils/measurement-unit.js diff --git a/src/option/base/measurement-unit.js b/src/option/base/measurement-unit.js index 7c68b87..b85e579 100644 --- a/src/option/base/measurement-unit.js +++ b/src/option/base/measurement-unit.js @@ -2,6 +2,9 @@ const dimensionOptions = [ { label: '重量', value: '重量' }, { label: '体积', value: '体积' }, { label: '数量', value: '数量' }, + { label: '整车', value: '整车' }, + { label: '里程', value: '里程' }, + { label: '吨·公里', value: '吨·公里' }, ]; const statusOptions = [ diff --git a/src/option/business/transport-plan.js b/src/option/business/transport-plan.js index 152054f..f395e93 100644 --- a/src/option/business/transport-plan.js +++ b/src/option/business/transport-plan.js @@ -400,7 +400,6 @@ export const option = { span: 6, order: 350, minWidth: 150, - rules: [{ required: true, message: '请选择计划开始日期', trigger: 'change' }], }, { label: '计划结束日期', @@ -411,7 +410,6 @@ export const option = { span: 6, order: 340, minWidth: 150, - rules: [{ required: true, message: '请选择计划结束日期', trigger: 'change' }], }, { label: '数据来源', diff --git a/src/utils/measurement-unit.js b/src/utils/measurement-unit.js new file mode 100644 index 0000000..a238460 --- /dev/null +++ b/src/utils/measurement-unit.js @@ -0,0 +1,27 @@ +import { getList as getMeasurementUnitList } from '@/api/base/measurement-unit'; + +/** + * 加载启用状态的计量单位选项,展示/取值均为「计量单位」字段(unitName)。 + * @returns {Promise>} + */ +export const loadMeasurementUnitOptions = () => { + return getMeasurementUnitList(1, 9999, { status: 1 }).then(res => { + const data = res?.data?.data || res?.data || {}; + const records = Array.isArray(data) ? data : data.records || []; + const names = new Set(); + return records + .filter(item => item.status === undefined || item.status === null || Number(item.status) === 1) + .map(item => { + const name = String(item.unitName || '').trim(); + if (!name || names.has(name)) return null; + names.add(name); + return { + id: item.id, + label: name, + value: name, + dimension: String(item.dimension || '').trim(), + }; + }) + .filter(Boolean); + }); +}; diff --git a/src/views/business/components/billing-plan-editor.vue b/src/views/business/components/billing-plan-editor.vue index b13e2ab..115798f 100644 --- a/src/views/business/components/billing-plan-editor.vue +++ b/src/views/business/components/billing-plan-editor.vue @@ -386,12 +386,15 @@ const BILLING_ELEMENT_DIMENSION_MAP = { 按重量: '重量', 按体积: '体积', 按数量: '数量', + 按里程: '里程', + '按吨·公里': '吨·公里', + '固定金额(整单一口价)': '整车', }; const BILLING_UNIT_SELECTABLE_ELEMENTS = Object.keys(BILLING_ELEMENT_DIMENSION_MAP); -/** 固定计费单位(不可编辑) */ -const FIXED_BILLING_UNIT_MAP = { +/** 对应维度下无计量单位时的默认计费单位 */ +const DEFAULT_BILLING_UNIT_MAP = { 按里程: '公里', - '按吨·公里': '吨公里', + '按吨·公里': '吨·公里', '固定金额(整单一口价)': '单', }; const defaultRule = () => ({ @@ -652,58 +655,61 @@ export default { }), ]).finally(() => { this.unitLoading = false; + (this.draft.rules || []).forEach(row => this.syncBillingUnit(row)); }); }, measurementDimension(billingElement) { return BILLING_ELEMENT_DIMENSION_MAP[billingElement] || ''; }, - fixedBillingUnit(billingElement) { - return FIXED_BILLING_UNIT_MAP[String(billingElement || '').trim()] || ''; + defaultBillingUnit(billingElement) { + return DEFAULT_BILLING_UNIT_MAP[String(billingElement || '').trim()] || ''; }, canSelectBillingUnit(row) { return BILLING_UNIT_SELECTABLE_ELEMENTS.includes(String(row?.billingElement || '').trim()); }, billingUnitPlaceholder(row) { if (!row?.billingElement) return '请先选择计费要素'; - if (this.fixedBillingUnit(row.billingElement)) return this.fixedBillingUnit(row.billingElement); if (!this.canSelectBillingUnit(row)) return '当前计费要素无需选择'; return '请选择'; }, unitOptionsFor(row) { - const fixedUnit = this.fixedBillingUnit(row?.billingElement); - if (fixedUnit) { - return [{ id: fixedUnit, label: fixedUnit, value: fixedUnit }]; - } if (!this.canSelectBillingUnit(row)) { return []; } const dimension = this.measurementDimension(row?.billingElement); - if (dimension) { - return this.measurementUnits - .filter(item => String(item.dimension || '').trim() === dimension) - .map(item => ({ - id: item.id, - label: item.unitName, - value: item.unitName, - })) - .filter(item => item.value); + const defaultUnit = this.defaultBillingUnit(row?.billingElement); + const options = dimension + ? this.measurementUnits + .filter(item => String(item.dimension || '').trim() === dimension) + .map(item => ({ + id: item.id, + label: item.unitName, + value: item.unitName, + })) + .filter(item => item.value) + : []; + // 对应维度未配置计量单位时,下拉仅展示默认值 + if (!options.length && defaultUnit) { + return [{ id: `default-${defaultUnit}`, label: defaultUnit, value: defaultUnit }]; } - return []; + return options; }, syncBillingUnit(row) { - const fixedUnit = this.fixedBillingUnit(row?.billingElement); - if (fixedUnit) { - row.billingUnit = fixedUnit; - return; - } if (!this.canSelectBillingUnit(row)) { row.billingUnit = ''; return; } const options = this.unitOptionsFor(row); - if (!options.some(item => String(item.value) === String(row.billingUnit || ''))) { - row.billingUnit = ''; + const defaultUnit = this.defaultBillingUnit(row?.billingElement); + const current = String(row.billingUnit || '').trim(); + if (options.some(item => String(item.value) === current)) { + return; } + if (defaultUnit && options.some(item => String(item.value) === defaultUnit)) { + row.billingUnit = defaultUnit; + return; + } + row.billingUnit = options[0]?.value || defaultUnit || ''; }, feeTypeKey(row) { const option = this.feeCategories.find( diff --git a/src/views/business/components/business-crud-page.vue b/src/views/business/components/business-crud-page.vue index 74613ad..069a237 100644 --- a/src/views/business/components/business-crud-page.vue +++ b/src/views/business/components/business-crud-page.vue @@ -5244,6 +5244,7 @@ import { formatUpdateUserName } from '@/utils/audit'; import { getToken, isAdministrator } from '@/utils/auth'; import { openImportDialog } from '@/utils/import-excel'; import { normalizeMapSearchResults, searchAmapPlaceText } from '@/utils/map-search'; +import { loadMeasurementUnitOptions } from '@/utils/measurement-unit'; import { applyTableMenuWidth } from '@/utils/table-menu'; import { downloadFileByUrl, downloadXls } from '@/utils/util'; import { isMobile } from '@/utils/validate'; @@ -5822,7 +5823,7 @@ export default { billCycleTypeOptions: ['固定截单日', '自然月', '自定义多周期'], reconciliationModeOptions: ['明细逐行核对', '按账单汇总核对', '跳过自动核对'], packageOptions, - quantityUnitOptions: ['吨', '千克', '立方米', '件', '车', '箱', '托盘'], + quantityUnitOptions: [], attachmentFileTypes: [ 'pdf', 'bmp', @@ -5865,7 +5866,6 @@ export default { this.ensureBillingUnitOptions(); } if (this.config.enableTaskInfoForm) { - this.loadTaskQuantityUnitOptions(); this.loadTaskFeeUnitOptions(); this.ensureBillingCargoTypeOptions(); } @@ -5886,6 +5886,7 @@ export default { } else { this.consumeTemplateCreatePayload(); } + this.loadTaskQuantityUnitOptions(); }, computed: { ...mapGetters(['permission', 'userInfo']), @@ -7789,8 +7790,6 @@ export default { ['contractName', '客户合同'], ['planName', '计划名称'], ['transportType', '运输方式'], - ['planStartDate', '计划开始日期'], - ['planEndDate', '计划结束日期'], ['departureAddress', '发货地址'], ['arrivalAddress', '收货地址'], ]; @@ -9270,9 +9269,10 @@ export default { return Promise.resolve(this.taskQuantityUnitOptions); } this.taskQuantityUnitLoading = true; - return getDictionary({ code: 'quantity_unit' }) - .then(res => { - this.taskQuantityUnitOptions = this.normalizeDictOptions(res.data.data || []); + return loadMeasurementUnitOptions() + .then(options => { + this.taskQuantityUnitOptions = options; + this.quantityUnitOptions = options.map(item => item.value); return this.taskQuantityUnitOptions; }) .finally(() => { diff --git a/src/views/business/components/master-order-dispatch.vue b/src/views/business/components/master-order-dispatch.vue index c5efbd2..9f6ad5f 100644 --- a/src/views/business/components/master-order-dispatch.vue +++ b/src/views/business/components/master-order-dispatch.vue @@ -317,6 +317,7 @@ + + diff --git a/src/views/business/components/waybill-manage-page.vue b/src/views/business/components/waybill-manage-page.vue index 70b6a03..d46a264 100644 --- a/src/views/business/components/waybill-manage-page.vue +++ b/src/views/business/components/waybill-manage-page.vue @@ -2221,7 +2221,7 @@ type="primary" :underline="false" @click="openWaybillPunchRecordEdit(record)" - >编辑{{ record.punched ? '编辑打卡' : '补卡' }}
暂未打卡
@@ -2787,138 +2777,99 @@ > - + {{ waybillPunchEditRecord.nodeName || waybillPunchEditRecord.nodeCode || '-' }} - - - - 已打卡 - 未打卡 - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 在途打卡只保留 1 张照片 +
+
-
@@ -3679,6 +3630,7 @@ import { trackVehicle, } from '@/api/business/waybill-manage'; import VoucherExecutionDetail from './voucher-execution-detail.vue'; +import PunchRecordPhotos from './punch-record-photos.vue'; import AddressMapPicker from '@/components/address-map-picker/main.vue'; import { getList as getDriverList } from '@/api/transportCapacity/driver'; import { @@ -3886,6 +3838,7 @@ export default { ElImageViewer, OpenFileViewer, VoucherExecutionDetail, + PunchRecordPhotos, AddressMapPicker, }, name: 'WaybillManagePage', @@ -3983,7 +3936,6 @@ export default { }, waybillPunchEditRecord: {}, waybillPunchEditForm: { - punched: true, punchTime: '', address: '', longitude: '', @@ -4386,11 +4338,21 @@ export default { return this.waybillPunchEditNode?.punch === false; }, waybillPunchEditRules() { - if (!this.waybillPunchEditForm.punched) return {}; return { punchTime: [{ required: true, message: '请选择打卡时间', trigger: 'change' }] }; }, - waybillPunchEditPhotoUrls() { - return (this.waybillPunchEditForm.photos || []).map(item => item.url).filter(Boolean); + // 照片类型只有两个来源:固定的「打卡照片」,和该节点过程配置勾选的凭证类型; + // 不接受配置外的类型。在途打卡表只有一个照片字段、也没有类型列,不按凭证类型分组 + waybillPunchEditPhotoTypes() { + if (this.waybillPunchEditRecord.type === 'enroute') return []; + const voucherTypes = Array.isArray(this.waybillPunchEditNode?.voucherTypes) + ? this.waybillPunchEditNode.voucherTypes + : []; + return ['打卡照片', ...voucherTypes]; + }, + // 打卡照片与各凭证类型各自最多 9 张,不另设节点总张数上限; + // 在途打卡用 0 表示不限制——它保存时只保留第一张,张数由那一步约束 + waybillPunchEditPhotoLimit() { + return this.waybillPunchEditRecord.type === 'enroute' ? 0 : 9; }, // 打卡时间轴按打卡时间先后展示:已打卡记录按打卡时间升序在前, // 未打卡记录保持流程节点原顺序排在其后。 @@ -5876,8 +5838,8 @@ export default { openWaybillPunchRecordEdit(record = {}) { if (!this.detailRow?.id || !record) return; this.waybillPunchEditRecord = { ...record }; + // 弹窗不提供打卡状态开关:未打卡的记录点「补卡」进来,保存即补出一条打卡流水 this.waybillPunchEditForm = { - punched: record.punched !== false, punchTime: record.punchTime || this.$dayjs().format('YYYY-MM-DD HH:mm:ss'), address: record.address || '', longitude: record.longitude || '', @@ -5914,26 +5876,40 @@ export default { this.waybillPunchEditForm.latitude = Number(selection.latitude).toFixed(6); } }, - beforeWaybillPunchPhotoUpload(file) { + beforeWaybillPunchPhotoUpload(file, type = '') { const validType = ['image/jpeg', 'image/png', 'image/jpg', 'image/bmp'].includes(file.type); if (!validType) { this.$message.error('仅支持 JPG、PNG、BMP 图片'); return false; } - if (file.size / 1024 / 1024 >= 5) { - this.$message.error('图片大小不能超过 5MB'); + // 单张 20MB 与司机端一致:凭证要原图,5MB 会把手机拍的正常凭证挡在门外 + if (file.size / 1024 / 1024 >= 20) { + this.$message.error('图片大小不能超过 20MB'); return false; } + // 每个照片类型各自最多 9 张。界面已经不给满员的类型「添加」入口,这里再兜一层。 + // 张数按界面上的分组口径统计:没带类型的照片算进兜底类型「凭证」 + const limit = this.waybillPunchEditPhotoLimit; + if (limit > 0 && type) { + const used = (this.waybillPunchEditForm.photos || []).filter( + photo => (String(photo?.type || '').trim() || '凭证') === type + ).length; + if (used >= limit) { + this.$message.error(`${type}最多上传 ${limit} 张`); + return false; + } + } return true; }, - handleWaybillPunchPhotoSuccess(res) { + handleWaybillPunchPhotoSuccess(res, type = '') { if (res.code !== 200 || !res.data) { this.$message.error(res.msg || '上传失败'); return; } const url = res.data.link || res.data.url || res.data.domain || ''; if (!url) return; - const photos = [...this.waybillPunchEditForm.photos, { url, type: '' }]; + // 照片落进点开的那一组并带上该类型的名字:类型写空,保存时会被按下标回推成别的类型 + const photos = [...this.waybillPunchEditForm.photos, { url, type }]; // 在途打卡表只有一个照片字段(单张),多传的保存时会被丢掉,这里先只留一张 this.waybillPunchEditForm.photos = this.waybillPunchEditRecord.type === 'enroute' ? photos.slice(0, 1) : photos; @@ -5950,7 +5926,6 @@ export default { await this.$refs.waybillPunchRecordEditFormRef.validate(); const record = this.waybillPunchEditRecord; const form = this.waybillPunchEditForm; - const keepDetail = form.punched; const submit = typeof this.api?.savePunchRecord === 'function' ? this.api.savePunchRecord : savePunchRecord; this.waybillPunchRecordEditDialog.submitting = true; @@ -5960,17 +5935,17 @@ export default { type: record.type === 'enroute' ? 'enroute' : 'node', nodeCode: record.nodeCode || '', nodeName: record.nodeName || '', - punched: form.punched, - // 未打卡时后端会清空该节点流水,明细项不再上传 - punchTime: keepDetail ? form.punchTime : '', - address: keepDetail ? form.address : '', - longitude: keepDetail ? form.longitude : '', - latitude: keepDetail ? form.latitude : '', - weight: keepDetail ? form.weight : '', - volume: keepDetail ? form.volume : '', - quantity: keepDetail ? form.quantity : '', - remark: keepDetail ? form.remark : '', - photos: keepDetail ? form.photos.map(photo => ({ url: photo.url, type: photo.type })) : [], + // 弹窗没有打卡状态开关:无论「补卡」还是「编辑打卡」,保存都落一条已打卡流水 + punched: true, + punchTime: form.punchTime, + address: form.address, + longitude: form.longitude, + latitude: form.latitude, + weight: form.weight, + volume: form.volume, + quantity: form.quantity, + remark: form.remark, + photos: form.photos.map(photo => ({ url: photo.url, type: photo.type })), }); this.$message.success('保存成功'); this.waybillPunchRecordEditDialog.visible = false; @@ -11606,50 +11581,6 @@ export default { color: #303133; } - &__punch-edit-photos { - display: flex; - flex-wrap: wrap; - gap: 10px; - } - - &__punch-edit-photo { - display: flex; - flex-direction: column; - align-items: center; - gap: 4px; - width: 96px; - - .el-image { - display: block; - width: 96px; - height: 96px; - border: 1px solid #eff1f7; - border-radius: 4px; - overflow: hidden; - } - } - - &__punch-edit-photo-label { - font-size: 12px; - line-height: 1.4; - color: #606266; - text-align: center; - word-break: break-all; - } - - &__punch-edit-photo-add { - display: flex; - align-items: center; - justify-content: center; - width: 96px; - height: 96px; - border: 1px dashed #dcdfe6; - border-radius: 4px; - font-size: 13px; - color: #909399; - cursor: pointer; - } - &__punch-edit-photo-tip { margin-top: 4px; font-size: 12px; @@ -11665,7 +11596,6 @@ export default { } } - &__punch-record-photos, &__driver-upload-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); diff --git a/src/views/business/loading-manage.vue b/src/views/business/loading-manage.vue index 15a63a9..645472c 100644 --- a/src/views/business/loading-manage.vue +++ b/src/views/business/loading-manage.vue @@ -694,22 +694,12 @@ > 备注:{{ record.remark }} -
-
- -
- {{ photo.label || `${record.nodeName}-凭证` }} -
-
-
+ :photos="record.photos" + lazy + @preview="previewDriverUploadPhoto(record.photos, $event)" + />
暂未打卡
@@ -1715,6 +1705,7 @@ import { getVoucherImages as getProcessConfigVoucherImages, } from '@/api/business/process-config'; import VoucherExecutionDetail from './components/voucher-execution-detail.vue'; +import PunchRecordPhotos from './components/punch-record-photos.vue'; import { carrierTypeOptions, loadingManageConfig, @@ -1925,6 +1916,7 @@ export default { Rank, Search, VoucherExecutionDetail, + PunchRecordPhotos, }, data() { return { @@ -4921,7 +4913,6 @@ export default { margin-left: 12px; } } - .loading-detail__punch-record-photos, .loading-detail__driver-upload-grid, .loading-detail__voucher-image-grid { display: grid; From d1f804056e171d394fa80dc6bacce901a24404e8 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Thu, 8 Oct 2026 18:57:49 +0800 Subject: [PATCH 26/47] =?UTF-8?q?:sparkles:=20=E5=AE=A1=E6=A0=B8=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E5=90=8E=E4=BF=9D=E7=95=99=E6=B5=81=E7=A8=8B=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E5=B9=B6=E8=B7=B3=E8=BD=ACMK=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- src/option/business/contract-manage.js | 2 +- src/option/business/project-apply.js | 2 +- src/views/business/contract-manage.vue | 33 ++++++++++++++++++- .../payment/components/bill-payment-table.vue | 2 +- src/views/payment/invoice-application.vue | 2 +- src/views/payment/invoice-receipt.vue | 2 +- src/views/payment/payment-application.vue | 2 +- .../formal-settlement-table-panel.vue | 6 +++- src/views/settlement/pre-settlement.vue | 2 +- src/views/vehicle/customer-archive.vue | 4 +-- 10 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/option/business/contract-manage.js b/src/option/business/contract-manage.js index 4a72be5..2bb7999 100644 --- a/src/option/business/contract-manage.js +++ b/src/option/business/contract-manage.js @@ -209,7 +209,7 @@ export const config = { label: '流程', statusProp: 'approvalStatus', status: ['approved', 'change_approved'], - stage: ['formal', 'terminated'], + stage: ['temporary', 'formal', 'terminated'], permission: 'contract_manage_view', }, { diff --git a/src/option/business/project-apply.js b/src/option/business/project-apply.js index 721413a..445f9cb 100644 --- a/src/option/business/project-apply.js +++ b/src/option/business/project-apply.js @@ -71,7 +71,7 @@ export const config = { action: 'mkAudit', label: '流程', statusProp: 'approvalStatus', - status: ['reviewing', 'change_reviewing'], + status: ['reviewing', 'change_reviewing', 'approved', 'change_approved'], permission: 'project_apply_approve', mkOnly: true, }, diff --git a/src/views/business/contract-manage.vue b/src/views/business/contract-manage.vue index c3b48e7..75130e4 100644 --- a/src/views/business/contract-manage.vue +++ b/src/views/business/contract-manage.vue @@ -1019,7 +1019,8 @@ import { getDictionary as getBizDictionary } from '@/api/system/dictbiz'; import { config, option } from '@/option/business/contract-manage'; import { getToken, isAdministrator } from '@/utils/auth'; import { downloadFileByUrl, downloadXls } from '@/utils/util'; -import { submitMkApprovalFlow } from '@/utils/mk-approval'; +import { getMKApprovalUrl } from '@/api/system/business-process'; +import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval'; import BillingPlanEditor from './components/billing-plan-editor.vue'; import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue'; import ContractAttachmentSection, { @@ -1301,6 +1302,7 @@ export default { flowBox: false, flowUrl: '', processInstanceId: '', + mkEnabled: false, // 页面形态在实例创建时锁定,避免 keep-alive 缓存后随全局 $route 翻转 formPageLocked: false, detailPageLocked: false, @@ -1520,6 +1522,13 @@ export default { this.loadProjectOptions(); this.loadSettlementDictionaries(); this.loadOrganizationOptions(); + loadMkEnabled() + .then(enabled => { + this.mkEnabled = !!enabled; + }) + .catch(() => { + this.mkEnabled = false; + }); } if (this.isFormPage) this.initFormPage(); if (this.isDetailPage) this.initDetailPage(); @@ -2858,6 +2867,10 @@ export default { return; } if (operation.action === 'flow') { + if (this.mkEnabled) { + this.handleMkAudit(row); + return; + } this.openFlow(row); return; } @@ -2911,6 +2924,24 @@ export default { query: { mode: 'add', copyId: row.id, name: '新增合同管理' }, }); }, + handleMkAudit(row) { + if (!row?.id) { + this.$message.warning('合同ID为空,无法打开审核页'); + return; + } + getMKApprovalUrl({ bizId: String(row.id) }) + .then(res => { + const url = res?.data?.data; + if (!url) { + this.$message.error('获取审核跳转地址失败'); + return; + } + window.open(url, '_blank'); + }) + .catch(() => { + this.$message.error('获取审核跳转地址失败'); + }); + }, openFlow(row, loaded = false) { const processInstanceId = row.processInstanceId || row.processInstanceID || row.procInstId; if (!processInstanceId && !loaded && row.id) { diff --git a/src/views/payment/components/bill-payment-table.vue b/src/views/payment/components/bill-payment-table.vue index a88608a..6e2a664 100644 --- a/src/views/payment/components/bill-payment-table.vue +++ b/src/views/payment/components/bill-payment-table.vue @@ -61,7 +61,7 @@ - 审核 + {{ row.approvalStatus === 'approved' ? '流程' : '审核' }} Date: Thu, 8 Oct 2026 19:37:27 +0800 Subject: [PATCH 27/47] =?UTF-8?q?:bug:=20=E8=BF=90=E5=8D=95=E8=BD=A8?= =?UTF-8?q?=E8=BF=B9=E4=B8=8E=E5=AE=9E=E6=97=B6=E5=AE=9A=E4=BD=8DWGS-84?= =?UTF-8?q?=E8=BD=ACGCJ-02=E5=90=8E=E5=86=8D=E7=BB=98=E5=88=B6=E9=AB=98?= =?UTF-8?q?=E5=BE=B7=E5=9C=B0=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- src/utils/coordinate.js | 86 +++++++++++++++++++ .../components/waybill-manage-page.vue | 23 +++-- 2 files changed, 103 insertions(+), 6 deletions(-) diff --git a/src/utils/coordinate.js b/src/utils/coordinate.js index b4654fb..01e7df3 100644 --- a/src/utils/coordinate.js +++ b/src/utils/coordinate.js @@ -13,6 +13,92 @@ const coordinateRanges = { }, }; +/** WGS-84 → GCJ-02(高德)偏移算法常量 */ +const WGS84_TO_GCJ02_PI = Math.PI; +const WGS84_TO_GCJ02_A = 6378245.0; +const WGS84_TO_GCJ02_EE = 0.00669342162296594323; + +const isOutOfChina = (longitude, latitude) => + longitude < 72.004 || longitude > 137.8347 || latitude < 0.8293 || latitude > 55.8271; + +const transformLatitude = (longitude, latitude) => { + let result = + -100.0 + + 2.0 * longitude + + 3.0 * latitude + + 0.2 * latitude * latitude + + 0.1 * longitude * latitude + + 0.2 * Math.sqrt(Math.abs(longitude)); + result += + ((20.0 * Math.sin(6.0 * longitude * WGS84_TO_GCJ02_PI) + + 20.0 * Math.sin(2.0 * longitude * WGS84_TO_GCJ02_PI)) * + 2.0) / + 3.0; + result += + ((20.0 * Math.sin(latitude * WGS84_TO_GCJ02_PI) + + 40.0 * Math.sin((latitude / 3.0) * WGS84_TO_GCJ02_PI)) * + 2.0) / + 3.0; + result += + ((160.0 * Math.sin((latitude / 12.0) * WGS84_TO_GCJ02_PI) + + 320 * Math.sin((latitude * WGS84_TO_GCJ02_PI) / 30.0)) * + 2.0) / + 3.0; + return result; +}; + +const transformLongitude = (longitude, latitude) => { + let result = + 300.0 + + longitude + + 2.0 * latitude + + 0.1 * longitude * longitude + + 0.1 * longitude * latitude + + 0.1 * Math.sqrt(Math.abs(longitude)); + result += + ((20.0 * Math.sin(6.0 * longitude * WGS84_TO_GCJ02_PI) + + 20.0 * Math.sin(2.0 * longitude * WGS84_TO_GCJ02_PI)) * + 2.0) / + 3.0; + result += + ((20.0 * Math.sin(longitude * WGS84_TO_GCJ02_PI) + + 40.0 * Math.sin((longitude / 3.0) * WGS84_TO_GCJ02_PI)) * + 2.0) / + 3.0; + result += + ((150.0 * Math.sin((longitude / 12.0) * WGS84_TO_GCJ02_PI) + + 300.0 * Math.sin((longitude / 30.0) * WGS84_TO_GCJ02_PI)) * + 2.0) / + 3.0; + return result; +}; + +/** + * WGS-84(GPS / LBS)转 GCJ-02(高德/国测局),供高德地图绘制使用。 + * 中国境外坐标不做偏移,原样返回。 + * @param {number} longitude + * @param {number} latitude + * @returns {[number, number]} [经度, 纬度] + */ +export const wgs84ToGcj02 = (longitude, latitude) => { + const lng = Number(longitude); + const lat = Number(latitude); + if (!Number.isFinite(lng) || !Number.isFinite(lat) || isOutOfChina(lng, lat)) { + return [lng, lat]; + } + let dLat = transformLatitude(lng - 105.0, lat - 35.0); + let dLng = transformLongitude(lng - 105.0, lat - 35.0); + const radLat = (lat / 180.0) * WGS84_TO_GCJ02_PI; + let magic = Math.sin(radLat); + magic = 1 - WGS84_TO_GCJ02_EE * magic * magic; + const sqrtMagic = Math.sqrt(magic); + dLat = + (dLat * 180.0) / + (((WGS84_TO_GCJ02_A * (1 - WGS84_TO_GCJ02_EE)) / (magic * sqrtMagic)) * WGS84_TO_GCJ02_PI); + dLng = (dLng * 180.0) / ((WGS84_TO_GCJ02_A / sqrtMagic) * Math.cos(radLat) * WGS84_TO_GCJ02_PI); + return [lng + dLng, lat + dLat]; +}; + export const normalizeCoordinateInput = value => { const rawValue = String(value ?? '').replace(/[^\d.-]/g, ''); const isNegative = rawValue.startsWith('-'); diff --git a/src/views/business/components/waybill-manage-page.vue b/src/views/business/components/waybill-manage-page.vue index d46a264..a565f62 100644 --- a/src/views/business/components/waybill-manage-page.vue +++ b/src/views/business/components/waybill-manage-page.vue @@ -3642,6 +3642,7 @@ import { addressTypeOptions } from '@/option/base/common-address'; import { packageOptions } from '@/option/business/common'; import { formatUpdateUserName } from '@/utils/audit'; import { getToken, isAdministrator } from '@/utils/auth'; +import { wgs84ToGcj02 } from '@/utils/coordinate'; import { openImportDialog } from '@/utils/import-excel'; import { normalizeMapSearchResults, searchAmapPlaceText } from '@/utils/map-search'; import { loadMeasurementUnitOptions } from '@/utils/measurement-unit'; @@ -6306,12 +6307,14 @@ export default { } }, async renderWaybillLocateMap(locateInfo) { - const longitude = Number(locateInfo?.longitude); - const latitude = Number(locateInfo?.latitude); - if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) { + const rawLongitude = Number(locateInfo?.longitude); + const rawLatitude = Number(locateInfo?.latitude); + if (!Number.isFinite(rawLongitude) || !Number.isFinite(rawLatitude)) { this.$message.warning('定位坐标无效,无法在地图上展示'); return; } + // LBS 返回 WGS-84,高德地图使用 GCJ-02,绘制前需转换 + const [longitude, latitude] = wgs84ToGcj02(rawLongitude, rawLatitude); try { await this.loadAmap(); await this.$nextTick(); @@ -6332,7 +6335,7 @@ export default { `
${locateInfo.vehicleNo || '车辆位置'}
`, locateInfo.locateTime ? `
时间:${locateInfo.locateTime}
` : '', locateInfo.address ? `
地址:${locateInfo.address}
` : '', - `
坐标:${longitude}, ${latitude}
`, + `
坐标:${rawLongitude}, ${rawLatitude}
`, ``, ].join(''); this.waybillLocateInfoWindow = new window.AMap.InfoWindow({ @@ -6359,9 +6362,17 @@ export default { } }, async renderWaybillTrackMap(points) { + // LBS 轨迹为 WGS-84,绘制到高德前转为 GCJ-02 const path = (points || []) - .map(item => [Number(item.longitude), Number(item.latitude)]) - .filter(item => Number.isFinite(item[0]) && Number.isFinite(item[1])); + .map(item => { + const longitude = Number(item.longitude); + const latitude = Number(item.latitude); + if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) { + return null; + } + return wgs84ToGcj02(longitude, latitude); + }) + .filter(Boolean); if (!path.length) { this.$message.warning('轨迹坐标无效,无法在地图上展示'); return; From 2500540933b5c1e2bbb2466712faf2862e116823 Mon Sep 17 00:00:00 2001 From: weicw1996 Date: Fri, 9 Oct 2026 10:01:19 +0800 Subject: [PATCH 28/47] =?UTF-8?q?:bug:=20=E4=BB=8E=E4=B8=9A=E8=B5=84?= =?UTF-8?q?=E6=A0=BC=E8=AF=81=E5=AD=97=E6=AE=B5=E6=94=B9=E5=90=8D=E5=90=8E?= =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E9=A1=B5=E5=85=BC=E5=AE=B9=E6=97=A7=E9=94=AE?= =?UTF-8?q?=EF=BC=8C=E8=A1=A5=E5=85=A8=E5=B0=81=E9=9D=A2/=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E9=A1=B5=E4=B8=8E=E6=9C=89=E6=95=88=E6=9C=9F=E8=B5=B7?= =?UTF-8?q?=E6=AD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 从业资格证信息组原先只列证号、照片、有效期止三项,且照片用的是字段改名前的旧键 qualificationUrl。改名之前提交的记录里仍存旧键,只读新键会得到空值,审核页把已填的 封面页显示成「—」,审核员据此以为对方没传材料。现按 VehicleCertSubmitRequest 的 字段名补全该组,并新增 readFieldValue 统一兼容旧键,新旧键并存时以新键为准。 旧键已并入所属字段展示,knownKeys 同步登记,避免同一张封面页出现两行。 --- src/utils/cert-audit.js | 48 +++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/src/utils/cert-audit.js b/src/utils/cert-audit.js index d57cb9b..331eced 100644 --- a/src/utils/cert-audit.js +++ b/src/utils/cert-audit.js @@ -139,9 +139,23 @@ const CERT_SNAPSHOT_GROUPS = { { title: '从业资格证信息', fields: [ + { key: 'qualificationType', label: '从业资格认证类型' }, { key: 'qualificationNo', label: '从业资格证号' }, - { key: 'qualificationUrl', label: '从业资格证照片', type: TYPE_IMAGE }, - { key: 'qualificationValidEnd', label: '从业资格证有效期止', type: TYPE_DATE }, + { + key: 'qualificationFront', + label: '从业资格证封面页', + type: TYPE_IMAGE, + legacyKeys: ['qualificationUrl'], + }, + { key: 'qualificationBack', label: '从业资格证内容页', type: TYPE_IMAGE }, + { key: 'qualificationValidStart', label: '从业资格证有效期起', type: TYPE_DATE }, + { + key: 'qualificationValidEnd', + label: '从业资格证有效期止', + type: TYPE_DATE, + longTermKey: 'qualificationLongTerm', + }, + { key: 'qualificationLongTerm', label: '从业资格证长期有效', type: TYPE_LONG_TERM }, ], }, ], @@ -268,9 +282,28 @@ const normalizeDate = value => { return text; }; +/** + * 取字段值,兼容字段改名前的旧键 + * + * 后端把某字段改名后,改名之前提交的记录里存的仍是旧键(例:从业资格证封面页 + * 由 `qualificationUrl` 改为 `qualificationFront`)。只读新键会得到空, + * 审核页就把已填的封面页显示成「—」,误导审核员以为对方没传。 + * 新旧键都存在时以新键为准。 + */ +const readFieldValue = (snapshot, field) => { + if (!snapshot) return undefined; + const value = snapshot[field.key]; + if (!isEmptyValue(value)) return value; + const legacyKeys = field.legacyKeys || []; + for (let i = 0; i < legacyKeys.length; i += 1) { + if (!isEmptyValue(snapshot[legacyKeys[i]])) return snapshot[legacyKeys[i]]; + } + return value; +}; + /** 比较用的归一化取值:展示相同即视为未变更 */ const canonicalOf = (snapshot, field) => { - const value = snapshot ? snapshot[field.key] : undefined; + const value = readFieldValue(snapshot, field); if (field.type === TYPE_LONG_TERM) { if (isEmptyValue(value)) return ''; return isLongTermOn(value) ? '1' : '0'; @@ -286,7 +319,7 @@ const canonicalOf = (snapshot, field) => { /** 展示文案:日期型受「长期有效」影响,图片型返回 URL 由组件渲染缩略图 */ const textOf = (snapshot, field) => { - const value = snapshot ? snapshot[field.key] : undefined; + const value = readFieldValue(snapshot, field); if (field.type === TYPE_IMAGE) { return isEmptyValue(value) ? '' : String(value); } @@ -323,11 +356,11 @@ const buildRow = (field, pendingSnapshot, effectiveSnapshot, hasBaseline) => { label: field.label, type, effective: { - raw: effectiveSnapshot ? effectiveSnapshot[field.key] : undefined, + raw: readFieldValue(effectiveSnapshot, target), text: textOf(effectiveSnapshot, target), }, pending: { - raw: pendingSnapshot ? pendingSnapshot[field.key] : undefined, + raw: readFieldValue(pendingSnapshot, target), text: textOf(pendingSnapshot, target), }, changed: Boolean(changeType), @@ -363,7 +396,8 @@ export const buildSnapshotDiff = (bizType, pendingJson, effectiveJson) => { const groups = groupsConfig.map(group => { const rows = group.fields.map(field => { - knownKeys.push(field.key); + // 旧键已由所属字段并入展示,不能再作为独立字段出现,否则同一张封面页会出现两行 + knownKeys.push(field.key, ...(field.legacyKeys || [])); return buildRow(field, pending.value, effective.value, hasBaseline); }); return { From a1da644c4952a3d871a6933ff7c6ab92d2375b2c Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Fri, 9 Oct 2026 17:07:56 +0800 Subject: [PATCH 29/47] =?UTF-8?q?:sparkles:=20=E4=BC=98=E5=8C=96=E6=B8=AF?= =?UTF-8?q?=E5=8F=A3=E5=9C=B0=E5=9B=BE=E5=9B=9E=E5=A1=AB=E3=80=81=E5=90=88?= =?UTF-8?q?=E5=90=8C=E5=B1=95=E7=A4=BA=E4=B8=8E=E8=BF=90=E5=8D=95=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E7=A9=BA=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 地图选点回填国家省市区;合同选择显示编号/名称;常用数据组织筛选修正;运单详情将-1费用字段显示为空。 Co-authored-by: Cursor --- pnpm-lock.yaml | 113 +++---- src/components/address-map-picker/main.vue | 37 ++- src/option/base/common-address.js | 4 + src/option/system/user.js | 3 + src/utils/contract.js | 14 + src/views/base/common-address.vue | 37 ++- src/views/base/port-terminal.vue | 301 +++++++++++++++--- src/views/base/railway-station.vue | 26 +- src/views/business/common-cargo.vue | 3 +- src/views/business/common-route.vue | 25 +- .../components/business-crud-page.vue | 6 +- .../components/master-order-dispatch.vue | 9 +- .../components/master-order-editor.vue | 4 +- .../components/shipping-template-page.vue | 4 +- .../components/transport-plan-page.vue | 6 +- .../components/waybill-import-dialog.vue | 56 ++-- .../components/waybill-manage-page.vue | 68 ++-- src/views/business/loading-manage.vue | 15 +- src/views/business/project-apply.vue | 20 +- src/views/business/transport-plan-import.vue | 4 +- .../payment/payment-application-form.vue | 4 +- .../components/formal-settlement-editor.vue | 4 +- .../components/pre-settlement-editor.vue | 4 +- .../settlement/receivable-payable-detail.vue | 5 +- src/views/system/user.vue | 13 +- 25 files changed, 568 insertions(+), 217 deletions(-) create mode 100644 src/utils/contract.js diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f83bac..864311c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,9 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - '@smallwei/avue': 5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db - importers: .: @@ -31,16 +28,16 @@ importers: version: 1.7.0 '@smallwei/avue': specifier: ~3.9.2 - version: 3.9.2(patch_hash=5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db)(@element-plus/icons-vue@2.3.2(vue@3.5.34))(debug@4.4.3(supports-color@7.2.0))(element-plus@2.14.2(vue@3.5.34))(vue@3.5.34) + version: 3.9.2(@element-plus/icons-vue@2.3.2(vue@3.5.34))(element-plus@2.14.2(vue@3.5.34))(vue@3.5.34) animate.css: specifier: ^4.1.1 version: 4.1.1 avue-plugin-ueditor: specifier: ^1.0.4 - version: 1.0.4(axios@1.16.0(debug@4.4.3(supports-color@7.2.0)))(vue@3.5.34) + version: 1.0.4(axios@1.16.0)(vue@3.5.34) axios: specifier: ^1.13.5 - version: 1.16.0(debug@4.4.3(supports-color@7.2.0)) + version: 1.16.0 codemirror: specifier: ^5.65.21 version: 5.65.21 @@ -128,7 +125,7 @@ importers: version: 5.4.21(sass@1.99.0)(terser@5.46.2) vite-plugin-compression: specifier: ^0.5.1 - version: 0.5.1(supports-color@7.2.0)(vite@5.4.21(sass@1.99.0)(terser@5.46.2)) + version: 0.5.1(vite@5.4.21(sass@1.99.0)(terser@5.46.2)) vite-plugin-vue-setup-extend: specifier: ^0.4.0 version: 0.4.0(vite@5.4.21(sass@1.99.0)(terser@5.46.2)) @@ -407,35 +404,30 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@napi-rs/canvas-linux-arm64-musl@1.0.2': resolution: {integrity: sha512-qwROoDIC9upfvDoRLuPn2aNg9CGW1x0Ygr4k2Or+8paA9d0qBLwk87U+g8KQpoOviKoPoiwl97kvBYuYD7qZoA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@napi-rs/canvas-linux-riscv64-gnu@1.0.2': resolution: {integrity: sha512-fXRjnPihdnbO6qy1QQOgxAonb68A0TCEG7rj1x7v7rxNElsE8EVIKIEUTvyDtU+sthYSbX+8e7g3oZiLGnOmxw==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] - libc: [glibc] '@napi-rs/canvas-linux-x64-gnu@1.0.2': resolution: {integrity: sha512-nPR97DXhbWIAy7yazF3jc06kEPMqYMLmPzFOVNlwKPfIoSChnI+x7dc0hTLaihz3jxrjL6j4BbA7earxfx4X3g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@napi-rs/canvas-linux-x64-musl@1.0.2': resolution: {integrity: sha512-l7zZY5+jL5qnBZtDz7CoBtY6p7EkHu422g/0zWwrOrzIwWyWxZFRfZZORY1UG7YApymPLx+UbOkN206xXn/c1Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@napi-rs/canvas-win32-arm64-msvc@1.0.2': resolution: {integrity: sha512-yE0koHCFF4PIbMc2o2SEALhnipz7WBISh5glLvQiomtIoCcW0np3H4Lw93ceJAfJttTTeIIWFbwH84F7EVzjMQ==} @@ -511,42 +503,36 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.6': resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.6': resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.6': resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.6': resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.6': resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@parcel/watcher-win32-arm64@2.5.6': resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} @@ -613,79 +599,66 @@ packages: resolution: {integrity: sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.60.3': resolution: {integrity: sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.60.3': resolution: {integrity: sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.60.3': resolution: {integrity: sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.60.3': resolution: {integrity: sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.60.3': resolution: {integrity: sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==} cpu: [loong64] os: [linux] - libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.60.3': resolution: {integrity: sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.60.3': resolution: {integrity: sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==} cpu: [ppc64] os: [linux] - libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.60.3': resolution: {integrity: sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.60.3': resolution: {integrity: sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.60.3': resolution: {integrity: sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.60.3': resolution: {integrity: sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.60.3': resolution: {integrity: sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openbsd-x64@4.60.3': resolution: {integrity: sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==} @@ -2654,10 +2627,10 @@ snapshots: countup.js: 2.10.0 signature_pad: 5.1.3 - '@smallwei/avue@3.9.2(patch_hash=5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db)(@element-plus/icons-vue@2.3.2(vue@3.5.34))(debug@4.4.3(supports-color@7.2.0))(element-plus@2.14.2(vue@3.5.34))(vue@3.5.34)': + '@smallwei/avue@3.9.2(@element-plus/icons-vue@2.3.2(vue@3.5.34))(element-plus@2.14.2(vue@3.5.34))(vue@3.5.34)': dependencies: '@element-plus/icons-vue': 2.3.2(vue@3.5.34) - axios: 0.21.4(debug@4.4.3(supports-color@7.2.0)) + axios: 0.21.4 countup.js: 1.9.3 dayjs: 1.11.20 element-plus: 2.14.2(vue@3.5.34) @@ -2793,25 +2766,25 @@ snapshots: dependencies: vue: 3.5.34 - '@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)': + '@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)': dependencies: - '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) + '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3) dom7: 3.0.0 is-url: 1.2.4 lodash.throttle: 4.1.1 - nanoid: 5.1.11 - slate: 0.123.0 + nanoid: 3.3.12 + slate: 0.72.8 snabbdom: 3.6.3 - '@wangeditor-next/code-highlight@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)': + '@wangeditor-next/code-highlight@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)': dependencies: - '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) + '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3) dom7: 4.0.6 prismjs: 1.30.0 slate: 0.123.0 snabbdom: 3.6.3 - '@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)': + '@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)': dependencies: '@types/event-emitter': 0.3.5 '@uppy/core': 2.3.4 @@ -2827,9 +2800,9 @@ snapshots: lodash.foreach: 4.5.0 lodash.throttle: 4.1.1 lodash.toarray: 4.4.0 - nanoid: 5.1.11 + nanoid: 3.3.12 scroll-into-view-if-needed: 3.1.0 - slate: 0.123.0 + slate: 0.72.8 slate-history: 0.115.0(slate@0.123.0) snabbdom: 3.6.3 @@ -2837,13 +2810,13 @@ snapshots: dependencies: '@uppy/core': 2.3.4 '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) - '@wangeditor-next/basic-modules': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) - '@wangeditor-next/code-highlight': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3) - '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) - '@wangeditor-next/list-module': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3) - '@wangeditor-next/table-module': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) - '@wangeditor-next/upload-image-module': 2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.123.0)(snabbdom@3.6.3) - '@wangeditor-next/video-module': 2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) + '@wangeditor-next/basic-modules': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor-next/code-highlight': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3) + '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor-next/list-module': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3) + '@wangeditor-next/table-module': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) + '@wangeditor-next/upload-image-module': 2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.123.0)(snabbdom@3.6.3) + '@wangeditor-next/video-module': 2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) dom7: 4.0.6 is-hotkey: 0.2.0 lodash.camelcase: 4.3.0 @@ -2856,16 +2829,16 @@ snapshots: slate: 0.123.0 snabbdom: 3.6.3 - '@wangeditor-next/list-module@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)': + '@wangeditor-next/list-module@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)': dependencies: - '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) + '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3) dom7: 4.0.6 slate: 0.123.0 snabbdom: 3.6.3 - '@wangeditor-next/table-module@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)': + '@wangeditor-next/table-module@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)': dependencies: - '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) + '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3) dom7: 4.0.6 lodash.debounce: 4.0.8 lodash.throttle: 4.1.1 @@ -2873,22 +2846,22 @@ snapshots: slate: 0.123.0 snabbdom: 3.6.3 - '@wangeditor-next/upload-image-module@2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.123.0)(snabbdom@3.6.3)': + '@wangeditor-next/upload-image-module@2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.123.0)(snabbdom@3.6.3)': dependencies: '@uppy/core': 2.3.4 '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) - '@wangeditor-next/basic-modules': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) - '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) + '@wangeditor-next/basic-modules': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3) dom7: 4.0.6 lodash.foreach: 4.5.0 slate: 0.123.0 snabbdom: 3.6.3 - '@wangeditor-next/video-module@2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)': + '@wangeditor-next/video-module@2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)': dependencies: '@uppy/core': 2.3.4 '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) - '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3) + '@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3) dom7: 4.0.6 nanoid: 5.1.11 slate: 0.123.0 @@ -3065,22 +3038,22 @@ snapshots: asynckit@0.4.0: {} - avue-plugin-ueditor@1.0.4(axios@1.16.0(debug@4.4.3(supports-color@7.2.0)))(vue@3.5.34): + avue-plugin-ueditor@1.0.4(axios@1.16.0)(vue@3.5.34): dependencies: '@wangeditor/editor': 5.1.23 '@wangeditor/editor-for-vue': 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.34) - axios: 1.16.0(debug@4.4.3(supports-color@7.2.0)) + axios: 1.16.0 vue: 3.5.34 - axios@0.21.4(debug@4.4.3(supports-color@7.2.0)): + axios@0.21.4: dependencies: - follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) + follow-redirects: 1.16.0 transitivePeerDependencies: - debug - axios@1.16.0(debug@4.4.3(supports-color@7.2.0)): + axios@1.16.0: dependencies: - follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) + follow-redirects: 1.16.0 form-data: 4.0.5 proxy-from-env: 2.1.0 transitivePeerDependencies: @@ -3248,11 +3221,9 @@ snapshots: dayjs@1.11.20: {} - debug@4.4.3(supports-color@7.2.0): + debug@4.4.3: dependencies: ms: 2.1.3 - optionalDependencies: - supports-color: 7.2.0 delayed-stream@1.0.0: {} @@ -3460,9 +3431,7 @@ snapshots: dependencies: to-regex-range: 5.0.1 - follow-redirects@1.16.0(debug@4.4.3(supports-color@7.2.0)): - optionalDependencies: - debug: 4.4.3(supports-color@7.2.0) + follow-redirects@1.16.0: {} form-data@4.0.5: dependencies: @@ -4157,10 +4126,10 @@ snapshots: uuid@8.3.2: {} - vite-plugin-compression@0.5.1(supports-color@7.2.0)(vite@5.4.21(sass@1.99.0)(terser@5.46.2)): + vite-plugin-compression@0.5.1(vite@5.4.21(sass@1.99.0)(terser@5.46.2)): dependencies: chalk: 4.1.2 - debug: 4.4.3(supports-color@7.2.0) + debug: 4.4.3 fs-extra: 10.1.0 vite: 5.4.21(sass@1.99.0)(terser@5.46.2) transitivePeerDependencies: diff --git a/src/components/address-map-picker/main.vue b/src/components/address-map-picker/main.vue index 85fcef8..3f2aa90 100644 --- a/src/components/address-map-picker/main.vue +++ b/src/components/address-map-picker/main.vue @@ -271,7 +271,12 @@ export default { this.runGeocode('address', lnglat) .then(result => { const detailAddress = result.regeocode?.formattedAddress || fallbackAddress; - this.selected = { ...this.selected, address: detailAddress }; + const region = this.resolveRegionFromGeocode(result); + this.selected = { + ...this.selected, + address: detailAddress, + ...region, + }; this.keyword = detailAddress || this.keyword; this.status = detailAddress || '已选点,可确认回填'; }) @@ -285,6 +290,26 @@ export default { this.resolving = false; }); }, + resolveRegionFromGeocode(result = {}) { + const component = result.regeocode?.addressComponent || {}; + const city = Array.isArray(component.city) ? '' : String(component.city || '').trim(); + const province = String(component.province || '').trim(); + const district = String(component.district || '').trim(); + const country = String(component.country || '').trim(); + const adcode = String(component.adcode || '').trim(); + return { + country, + province, + city: city || province, + district, + adcode, + regionCode: adcode, + provinceName: province, + cityName: city || province, + districtName: district, + countryName: country, + }; + }, renderMarker(point) { this.clearMarker(); this.marker = new window.AMap.Marker({ position: point }); @@ -310,6 +335,16 @@ export default { address: this.selected.address, longitude: this.selected.longitude, latitude: this.selected.latitude, + country: this.selected.country || this.selected.countryName || '', + province: this.selected.province || this.selected.provinceName || '', + city: this.selected.city || this.selected.cityName || '', + district: this.selected.district || this.selected.districtName || '', + adcode: this.selected.adcode || this.selected.regionCode || '', + regionCode: this.selected.regionCode || this.selected.adcode || '', + countryName: this.selected.countryName || this.selected.country || '', + provinceName: this.selected.provinceName || this.selected.province || '', + cityName: this.selected.cityName || this.selected.city || '', + districtName: this.selected.districtName || this.selected.district || '', }); this.visible = false; }, diff --git a/src/option/base/common-address.js b/src/option/base/common-address.js index 6860fb7..2b8f6d3 100644 --- a/src/option/base/common-address.js +++ b/src/option/base/common-address.js @@ -305,6 +305,10 @@ export const createOption = () => ({ searchOrder: 3, searchPlaceholder: '请选择', dicData: [], + props: { + label: 'label', + value: 'value', + }, filterable: true, hide: true, display: false, diff --git a/src/option/system/user.js b/src/option/system/user.js index 9cc07c3..786fa5e 100644 --- a/src/option/system/user.js +++ b/src/option/system/user.js @@ -39,6 +39,7 @@ export const userOption = safe => { prop: 'account', search: true, searchOrder: 2, + searchPlaceholder: '支持模糊搜索', display: false, }, { @@ -46,6 +47,7 @@ export const userOption = safe => { prop: 'realName', search: true, searchOrder: 4, + searchPlaceholder: '支持模糊搜索', display: false, }, { @@ -53,6 +55,7 @@ export const userOption = safe => { prop: 'phone', search: true, searchOrder: 3, + searchPlaceholder: '支持模糊搜索', display: false, }, { label: '工号', prop: 'code', display: false }, diff --git a/src/utils/contract.js b/src/utils/contract.js new file mode 100644 index 0000000..302996f --- /dev/null +++ b/src/utils/contract.js @@ -0,0 +1,14 @@ +/** + * 合同下拉/选择展示:合同编号/合同名称 + * + * @param {Object} contract 合同对象 + * @returns {string} + */ +export function formatContractOptionLabel(contract = {}) { + const contractNo = String(contract?.contractNo ?? '').trim(); + const contractName = String(contract?.contractName ?? '').trim(); + if (contractNo && contractName) { + return `${contractNo}/${contractName}`; + } + return contractNo || contractName || ''; +} diff --git a/src/views/base/common-address.vue b/src/views/base/common-address.vue index da340c2..cf00e6f 100644 --- a/src/views/base/common-address.vue +++ b/src/views/base/common-address.vue @@ -344,15 +344,21 @@ export default { getDeptTree(this.userInfo.tenantId).then(res => { this.deptOptions = this.flattenDept(res.data.data || []); const deptColumn = this.findColumn(this.option.column, 'searchDeptId'); - deptColumn.dicData = this.deptOptions; + if (deptColumn) { + deptColumn.dicData = this.deptOptions; + } }); }, flattenDept(tree, level = 0) { const result = []; tree.forEach(item => { + // 雪花 ID 必须保持字符串,避免 JS Number 精度丢失导致筛错组织。 + const id = + item.id === undefined || item.id === null || item.id === '' ? '' : String(item.id); + if (!id) return; result.push({ label: `${' '.repeat(level)}${item.title || item.deptName || item.name}`, - value: item.id, + value: id, rawLabel: item.title || item.deptName || item.name, }); if (item.children && item.children.length) { @@ -361,6 +367,19 @@ export default { }); return result; }, + normalizeListQuery(params = {}) { + // 搜索条件优先于缓存的 query,避免旧组织 ID 覆盖用户新选择的组织。 + const query = { ...this.query, ...params }; + const rawDeptId = query.deptId ?? query.searchDeptId; + delete query.searchDeptId; + if (rawDeptId !== undefined && rawDeptId !== null && String(rawDeptId).trim() !== '') { + query.deptId = String(rawDeptId).split(',')[0].trim(); + } else { + delete query.deptId; + } + query.allDept = this.isAdmin ? 1 : 0; + return query; + }, getRecords(res) { const data = res && res.data && res.data.data; if (Array.isArray(data)) return data; @@ -967,13 +986,15 @@ export default { }, searchReset() { this.query = {}; + this.allDept = this.isAdmin ? 1 : 0; this.page.currentPage = 1; this.onLoad(this.page); }, searchChange(params, done) { - this.query = params; + this.query = this.normalizeListQuery(params); + this.allDept = this.query.allDept; this.page.currentPage = 1; - this.onLoad(this.page, params); + this.onLoad(this.page, this.query); done(); }, selectionChange(list) { @@ -994,7 +1015,10 @@ export default { }, onLoad(page, params = {}) { this.loading = true; - getList(page.currentPage, page.pageSize, { ...params, ...this.query, allDept: this.allDept }) + const query = this.normalizeListQuery(params); + this.query = query; + this.allDept = query.allDept; + getList(page.currentPage, page.pageSize, query) .then(res => { const data = res.data.data; this.page.total = data.total; @@ -1027,8 +1051,7 @@ export default { }, buildExportParams() { return { - ...this.query, - allDept: this.allDept, + ...this.normalizeListQuery(this.query), ids: this.ids, [this.website.tokenHeader]: getToken(), }; diff --git a/src/views/base/port-terminal.vue b/src/views/base/port-terminal.vue index 736c648..162a954 100644 --- a/src/views/base/port-terminal.vue +++ b/src/views/base/port-terminal.vue @@ -264,7 +264,7 @@ import { changeStatus, getPortSelect, } from '@/api/base/port-terminal'; -import { getLazyTree } from '@/api/base/region'; +import { getDetail as getRegionDetail, getLazyTree } from '@/api/base/region'; import { exportBlob } from '@/api/common'; import { mapGetters } from 'vuex'; import { downloadXls } from '@/utils/util'; @@ -841,10 +841,60 @@ export default { }, handleCountryNameChange(value) { const country = this.countryOptions.find(item => item.title === value); - this.setProvinceOptions([]); this.setCityOptions([]); this.setDistrictOptions([]); - this.loadProvinceOptions(country ? country.id : ''); + this.clearSearchCityAndDistrict(); + // 搜索栏为国家 → 城市 → 区县(无省份),选中国家后需直接加载该国下全部城市 + this.loadCityOptionsByCountry(country ? country.id : ''); + }, + clearSearchCityAndDistrict() { + const searchForm = this.$refs.crud && this.$refs.crud.searchForm; + if (searchForm) { + searchForm.city = undefined; + searchForm.districtName = undefined; + } + if (this.query) { + this.query.city = undefined; + this.query.districtName = undefined; + } + }, + loadCityOptionsByCountry(countryCode) { + if (!countryCode) { + this.setCityOptions([]); + return Promise.resolve([]); + } + this.cityLoading = true; + return this.loadProvinceOptions(countryCode) + .then(provinces => + Promise.all((provinces || []).map(province => this.fetchCityOptions(province.id))) + ) + .then(cityGroups => { + const cityMap = new Map(); + cityGroups.flat().forEach(city => { + if (city && city.id && !cityMap.has(city.id)) { + cityMap.set(city.id, city); + } + }); + const cities = Array.from(cityMap.values()); + this.setCityOptions(cities); + return cities; + }) + .finally(() => { + this.cityLoading = false; + }); + }, + fetchCityOptions(provinceCode) { + if (!provinceCode) { + return Promise.resolve([]); + } + if (this.cityOptionCache[provinceCode]) { + return Promise.resolve(this.cityOptionCache[provinceCode]); + } + return getLazyTree(provinceCode).then(res => { + const options = (res.data.data || []).map(this.normalizeRegionOption); + this.cityOptionCache[provinceCode] = options; + return options; + }); }, syncCountryName(countryCode) { const country = this.countryOptions.find(item => item.id === countryCode); @@ -925,10 +975,8 @@ export default { return Promise.resolve(this.cityOptionCache[provinceCode]); } this.cityLoading = true; - return getLazyTree(provinceCode) - .then(res => { - const options = (res.data.data || []).map(this.normalizeRegionOption); - this.cityOptionCache[provinceCode] = options; + return this.fetchCityOptions(provinceCode) + .then(options => { this.setCityOptions(options); return options; }) @@ -1047,6 +1095,13 @@ export default { handleCityNameChange(value) { const city = this.cityOptions.find(item => item.title === value); this.setDistrictOptions([]); + const searchForm = this.$refs.crud && this.$refs.crud.searchForm; + if (searchForm) { + searchForm.districtName = undefined; + } + if (this.query) { + this.query.districtName = undefined; + } this.loadDistrictOptions(city ? city.id : ''); }, handleDistrictChange(value) { @@ -1071,6 +1126,190 @@ export default { if (selection.latitude !== undefined && selection.latitude !== null && selection.latitude !== '') { this.form.latitude = Number(selection.latitude).toFixed(6); } + this.applyMapRegionSelection(selection); + }, + normalizeAmapCityName(city, province) { + if (Array.isArray(city)) { + return String(province || '').trim(); + } + const cityName = String(city || '').trim(); + return cityName || String(province || '').trim(); + }, + buildRegionLookupCodes(adcode) { + const code = String(adcode || '').trim(); + if (!code) { + return []; + } + const codes = [code]; + if (/^\d{6}$/.test(code)) { + if (code.endsWith('00')) { + const cityCode = code.slice(0, 4); + if (cityCode && !codes.includes(cityCode)) { + codes.push(cityCode); + } + } + if (code.endsWith('0000')) { + const provinceCode = code.slice(0, 2); + if (provinceCode && !codes.includes(provinceCode)) { + codes.push(provinceCode); + } + } + } + return codes; + }, + fetchRegionDetailByCodes(codes = []) { + const tryNext = index => { + if (index >= codes.length) { + return Promise.resolve(null); + } + return getRegionDetail(codes[index]) + .then(res => { + const detail = res.data && res.data.data; + if (detail && detail.code) { + return detail; + } + return tryNext(index + 1); + }) + .catch(() => tryNext(index + 1)); + }; + return tryNext(0); + }, + resolveCountryFromRegionDetail(detail = {}) { + const ancestors = String(detail.ancestors || '') + .split(',') + .map(item => String(item || '').trim()) + .filter(Boolean); + for (let index = 0; index < ancestors.length; index += 1) { + const ancestorCode = ancestors[index]; + if (this.countryOptions.some(item => item.id === ancestorCode)) { + return ancestorCode; + } + } + if ( + detail.parentCode && + Number(detail.regionLevel) === 1 && + this.countryOptions.some(item => item.id === String(detail.parentCode)) + ) { + return String(detail.parentCode); + } + if (/^\d{6}$/.test(String(detail.code || ''))) { + return DEFAULT_COUNTRY_CODE; + } + return ''; + }, + applyRegionDetail(detail = {}) { + const countryHint = this.resolveCountryFromRegionDetail(detail); + return this.resolveCountryCode(countryHint || DEFAULT_COUNTRY_CODE) + .then(countryCode => { + this.form.countryCode = countryCode; + this.syncCountryName(countryCode); + return this.loadProvinceOptions(countryCode); + }) + .then(() => { + this.form.provinceCode = + String(detail.provinceCode || '') || this.resolveProvinceCode(detail.provinceName); + const province = this.provinceOptions.find(item => item.id === this.form.provinceCode); + this.form.provinceName = province ? province.title : detail.provinceName || ''; + return this.loadCityOptions(this.form.provinceCode); + }) + .then(() => { + this.form.cityCode = + String(detail.cityCode || '') || + this.resolveCityCode(detail.cityName, detail.districtCode || detail.code); + const city = this.cityOptions.find(item => item.id === this.form.cityCode); + this.form.city = city ? city.title : detail.cityName || ''; + return this.loadDistrictOptions(this.form.cityCode); + }) + .then(() => { + this.form.districtCode = this.resolveDistrictCode( + detail.districtName || detail.name, + detail.districtCode || detail.code + ); + this.handleDistrictChange(this.form.districtCode); + }); + }, + applyMapRegionByNames(selection = {}) { + const provinceName = String(selection.province || selection.provinceName || '').trim(); + const cityName = this.normalizeAmapCityName( + selection.city || selection.cityName, + provinceName + ); + const districtName = String(selection.district || selection.districtName || '').trim(); + const countryName = String(selection.country || selection.countryName || '').trim(); + const adcode = String(selection.adcode || selection.regionCode || '').trim(); + const countryPromise = countryName + ? this.resolveCountryCode(countryName) + : Promise.resolve(/^\d{6}$/.test(adcode) ? DEFAULT_COUNTRY_CODE : ''); + return countryPromise + .then(countryCode => { + if (!countryCode) { + return null; + } + this.form.countryCode = countryCode; + this.syncCountryName(countryCode); + return this.loadProvinceOptions(countryCode); + }) + .then(provinces => { + if (!provinces) { + return null; + } + this.form.provinceCode = this.resolveProvinceCode(provinceName); + const province = this.provinceOptions.find(item => item.id === this.form.provinceCode); + this.form.provinceName = province ? province.title : provinceName; + if (!this.form.provinceCode) { + this.clearCityValue(); + return null; + } + return this.loadCityOptions(this.form.provinceCode); + }) + .then(cities => { + if (!cities) { + return null; + } + this.form.cityCode = this.resolveCityCode(cityName, adcode); + const city = this.cityOptions.find(item => item.id === this.form.cityCode); + this.form.city = city ? city.title : cityName; + if (!this.form.cityCode) { + this.clearDistrictValue(); + return null; + } + return this.loadDistrictOptions(this.form.cityCode); + }) + .then(districts => { + if (!districts) { + return; + } + this.form.districtCode = this.resolveDistrictCode(districtName, adcode); + this.handleDistrictChange(this.form.districtCode); + }); + }, + applyMapRegionSelection(selection = {}) { + if (this.isParentRegionLocked) { + return Promise.resolve(); + } + const adcode = String(selection.adcode || selection.regionCode || '').trim(); + const hasNameHint = Boolean( + selection.country || + selection.countryName || + selection.province || + selection.provinceName || + selection.city || + selection.cityName || + selection.district || + selection.districtName + ); + if (!adcode && !hasNameHint) { + return Promise.resolve(); + } + const lookupCodes = this.buildRegionLookupCodes(adcode); + return this.fetchRegionDetailByCodes(lookupCodes) + .then(detail => { + if (detail) { + return this.applyRegionDetail(detail); + } + return this.applyMapRegionByNames(selection); + }) + .catch(() => this.applyMapRegionByNames(selection)); }, resolveCountryCode(country) { if (!country) { @@ -1443,42 +1682,14 @@ export default { diff --git a/src/views/base/railway-station.vue b/src/views/base/railway-station.vue index 336e5f1..8997fd6 100644 --- a/src/views/base/railway-station.vue +++ b/src/views/base/railway-station.vue @@ -171,7 +171,12 @@ export default { } }; const validateTelegraphCode = (rule, value, callback) => { - if (!/^[A-Z]{3}$/.test(String(value || '').toUpperCase())) { + const telegraphCode = String(value || '').trim(); + if (!telegraphCode) { + callback(); + return; + } + if (!/^[A-Z]{3}$/.test(telegraphCode.toUpperCase())) { callback(new Error('电报略码格式不正确或已存在')); } else { callback(); @@ -273,10 +278,7 @@ export default { prop: 'telegraphCode', minWidth: 100, maxlength: 3, - rules: [ - { required: true, message: '电报略码格式不正确或已存在', trigger: 'blur' }, - { validator: validateTelegraphCode, trigger: 'blur' }, - ], + rules: [{ validator: validateTelegraphCode, trigger: 'blur' }], change: ({ value }) => this.handleTelegraphChange(value), blur: ({ value }) => this.handleTelegraphChange(value), }, @@ -703,6 +705,7 @@ export default { }, normalizeRow(row) { row.code = row.tmisCode ? `TL${row.tmisCode}` : row.code; + row.telegraphCode = String(row.telegraphCode || '').trim() || undefined; row.regionCode = String(row.regionCode || '').trim(); row.regionName = String(row.regionName || '').trim(); row.districtCode = String(row.districtCode || '').trim(); @@ -735,14 +738,15 @@ export default { return records.some(item => item[prop] === row[prop] && String(item.id || '') !== rowId); }); }; - return Promise.all([ - hasDuplicate({ tmisCode: row.tmisCode }, 'tmisCode'), - hasDuplicate({ telegraphCode: row.telegraphCode }, 'telegraphCode'), - ]).then(([tmisExists, telegraphExists]) => { - if (tmisExists) { + const uniqueChecks = [hasDuplicate({ tmisCode: row.tmisCode }, 'tmisCode')]; + if (row.telegraphCode) { + uniqueChecks.push(hasDuplicate({ telegraphCode: row.telegraphCode }, 'telegraphCode')); + } + return Promise.all(uniqueChecks).then(results => { + if (results[0]) { return Promise.reject(new Error('该TMIS编码已存在')); } - if (telegraphExists) { + if (results[1]) { return Promise.reject(new Error('电报略码格式不正确或已存在')); } return Promise.resolve(); diff --git a/src/views/business/common-cargo.vue b/src/views/business/common-cargo.vue index 9ed40a1..ef871c0 100644 --- a/src/views/business/common-cargo.vue +++ b/src/views/business/common-cargo.vue @@ -352,7 +352,8 @@ export default { return result; }, normalizeListQuery(params = {}) { - const query = { ...params, ...this.query }; + // 搜索条件优先于缓存的 query,避免默认组织 ID 覆盖用户新选择的组织。 + const query = { ...this.query, ...params }; const rawDeptId = query.deptId ?? query.searchDeptId; delete query.searchDeptId; if (rawDeptId !== undefined && rawDeptId !== null && String(rawDeptId).trim() !== '') { diff --git a/src/views/business/common-route.vue b/src/views/business/common-route.vue index b1c7b14..99095ac 100644 --- a/src/views/business/common-route.vue +++ b/src/views/business/common-route.vue @@ -415,19 +415,27 @@ export default { }); }, buildDeptTree(tree) { - return tree.map(item => ({ - label: item.title || item.deptName || item.name, - value: item.id, - children: - item.children && item.children.length ? this.buildDeptTree(item.children) : undefined, - })); + return tree.map(item => { + // 雪花 ID 必须保持字符串,避免 JS Number 精度丢失导致筛错组织。 + const id = + item.id === undefined || item.id === null || item.id === '' ? '' : String(item.id); + return { + label: item.title || item.deptName || item.name, + value: id, + children: + item.children && item.children.length ? this.buildDeptTree(item.children) : undefined, + }; + }).filter(item => item.value); }, flattenDept(tree, level = 0) { const result = []; tree.forEach(item => { + const id = + item.id === undefined || item.id === null || item.id === '' ? '' : String(item.id); + if (!id) return; result.push({ label: `${' '.repeat(level)}${item.title || item.deptName || item.name}`, - value: item.id, + value: id, }); if (item.children && item.children.length) { result.push(...this.flattenDept(item.children, level + 1)); @@ -445,7 +453,8 @@ export default { ); }, normalizeListQuery(params = {}) { - const query = { ...params, ...this.query }; + // 搜索条件优先于缓存的 query,避免默认组织 ID 覆盖用户新选择的组织。 + const query = { ...this.query, ...params }; const rawDeptId = query.deptId ?? query.searchDeptId; delete query.searchDeptId; if (rawDeptId !== undefined && rawDeptId !== null && String(rawDeptId).trim() !== '') { diff --git a/src/views/business/components/business-crud-page.vue b/src/views/business/components/business-crud-page.vue index 069a237..67810db 100644 --- a/src/views/business/components/business-crud-page.vue +++ b/src/views/business/components/business-crud-page.vue @@ -215,7 +215,7 @@ @@ -3056,7 +3056,7 @@ @@ -5242,6 +5242,7 @@ import { addressTypeOptions } from '@/option/base/common-address'; import { packageOptions } from '@/option/business/common'; import { formatUpdateUserName } from '@/utils/audit'; import { getToken, isAdministrator } from '@/utils/auth'; +import { formatContractOptionLabel } from '@/utils/contract'; import { openImportDialog } from '@/utils/import-excel'; import { normalizeMapSearchResults, searchAmapPlaceText } from '@/utils/map-search'; import { loadMeasurementUnitOptions } from '@/utils/measurement-unit'; @@ -6590,6 +6591,7 @@ export default { }, }, methods: { + formatContractOptionLabel, initStandaloneFormPage() { if (!this.isStandaloneFormPage) return; const mode = this.$route.query.mode === 'edit' ? 'edit' : 'add'; diff --git a/src/views/business/components/master-order-dispatch.vue b/src/views/business/components/master-order-dispatch.vue index 9f6ad5f..e3040bf 100644 --- a/src/views/business/components/master-order-dispatch.vue +++ b/src/views/business/components/master-order-dispatch.vue @@ -317,6 +317,7 @@ + + diff --git a/src/views/system/user.vue b/src/views/system/user.vue index 7aeee32..533db3e 100644 --- a/src/views/system/user.vue +++ b/src/views/system/user.vue @@ -139,7 +139,7 @@ - + Date: Mon, 12 Oct 2026 14:47:23 +0800 Subject: [PATCH 37/47] =?UTF-8?q?:sparkles:=20=E5=B0=8F=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=94=AF=E6=8C=81=E9=87=8D=E7=BD=AE=E5=AF=86?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E5=88=97=E8=A1=A8=E9=9A=90=E8=97=8F=20openid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- src/option/system/mpUser.js | 4 +++- src/option/system/user.js | 2 +- src/views/system/mp-user.vue | 34 ++++++++++++++++++++++++++++++++++ src/views/system/user.vue | 2 +- 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/option/system/mpUser.js b/src/option/system/mpUser.js index a5b4b9a..994f76f 100644 --- a/src/option/system/mpUser.js +++ b/src/option/system/mpUser.js @@ -56,7 +56,7 @@ export default { addBtn: false, selection: true, dialogClickModal: false, - menuWidth: 280, + menuWidth: 320, column: [ { label: '手机号', @@ -140,8 +140,10 @@ export default { prop: 'openid', minWidth: 180, overHidden: true, + hide: true, addDisplay: false, editDisplay: true, + viewDisplay: true, order: 40, placeholder: '可清空以解绑微信', }, diff --git a/src/option/system/user.js b/src/option/system/user.js index f3956d7..93e3152 100644 --- a/src/option/system/user.js +++ b/src/option/system/user.js @@ -97,7 +97,7 @@ export const userOption = safe => { { label: '账号名', prop: 'account', - span: 12, + span: 6, rules: [ { required: true, diff --git a/src/views/system/mp-user.vue b/src/views/system/mp-user.vue index bf66361..a67fd36 100644 --- a/src/views/system/mp-user.vue +++ b/src/views/system/mp-user.vue @@ -77,6 +77,13 @@ > {{ row.status === 1 ? '停用' : '启用' }}
+ + 重置密码 + + @@ -105,6 +121,7 @@ export default { data() { return { form: {}, + dialogType: '', query: {}, loading: true, page: { @@ -160,6 +177,7 @@ export default { return payload; }, beforeOpen(done, type) { + this.dialogType = type; if (['edit', 'view'].includes(type) && this.form.id) { getDetail(this.form.id).then(res => { const detail = res.data.data || {}; @@ -258,6 +276,22 @@ export default { this.$refs.crud.toggleSelection(); }); }, + handleResetForRow(row) { + if (!row || !row.id) { + this.$message.warning('请先保存用户后再重置密码'); + return; + } + const account = row.account || row.phone || row.id; + this.$confirm(`确定将账号【${account}】的密码重置为随机密码?`, '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }) + .then(() => resetPassword(row.id)) + .then(res => { + this.showResetPassword(res.data.data); + }); + }, showResetPassword(passwordMap) { const passwordList = Object.keys(passwordMap || {}).map(account => h('div', null, `${account}:${passwordMap[account]}`) diff --git a/src/views/system/user.vue b/src/views/system/user.vue index 533db3e..7aeee32 100644 --- a/src/views/system/user.vue +++ b/src/views/system/user.vue @@ -139,7 +139,7 @@ - + Date: Tue, 13 Oct 2026 12:44:40 +0800 Subject: [PATCH 38/47] =?UTF-8?q?:zap:=20=E4=BC=98=E5=8C=96=E8=A1=A8?= =?UTF-8?q?=E6=A0=BC=E5=B7=A5=E5=85=B7=E6=A0=8F=E3=80=81=E9=99=84=E4=BB=B6?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=90=8D=E5=B1=95=E7=A4=BA=E4=B8=8E=E5=90=88?= =?UTF-8?q?=E5=90=8C=E9=87=91=E9=A2=9D=E4=B8=A4=E4=BD=8D=E5=B0=8F=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- src/components/table-header-tools/main.vue | 113 +++++++++++ .../vehicle-attachment-table/main.vue | 23 +-- src/styles/element-ui.scss | 33 +++- .../components/business-crud-page.vue | 64 +++++-- .../contract-attachment-section.vue | 11 +- .../components/master-order-detail.vue | 13 +- .../components/master-order-editor.vue | 11 +- .../components/shipping-template-page.vue | 35 +++- .../components/transport-plan-page.vue | 52 ++++-- .../components/waybill-manage-page.vue | 23 ++- src/views/business/contract-manage-change.vue | 28 ++- src/views/business/contract-manage.vue | 34 +++- src/views/business/loading-manage.vue | 175 ++++++++++++++---- src/views/business/master-order.vue | 28 ++- src/views/business/project-apply.vue | 11 +- src/views/business/temporary-credit-limit.vue | 40 +++- src/views/business/voucher-manage.vue | 125 +++++++++++-- .../payment/payment-application-form.vue | 59 ++---- .../components/formal-settlement-editor.vue | 16 +- .../formal-settlement-table-panel.vue | 60 ++++-- .../components/pre-settlement-editor.vue | 11 +- .../settlement-adjustment-editor.vue | 11 +- .../components/settlement-table-tools.vue | 13 ++ .../transport-reconciliation-table-panel.vue | 71 +++++-- src/views/settlement/formal-settlement.vue | 5 +- src/views/settlement/pre-settlement.vue | 36 ++-- .../settlement/receivable-payable-detail.vue | 67 +++++-- .../settlement/settlement-adjustment.vue | 48 +++-- .../settlement/transport-reconciliation.vue | 8 +- src/views/transit/exception-disposal.vue | 15 +- src/views/vehicle/customer-archive.vue | 72 +++---- 31 files changed, 967 insertions(+), 344 deletions(-) create mode 100644 src/components/table-header-tools/main.vue create mode 100644 src/views/settlement/components/settlement-table-tools.vue diff --git a/src/components/table-header-tools/main.vue b/src/components/table-header-tools/main.vue new file mode 100644 index 0000000..8bfb63a --- /dev/null +++ b/src/components/table-header-tools/main.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/src/components/vehicle-attachment-table/main.vue b/src/components/vehicle-attachment-table/main.vue index f725c49..4ca1d88 100644 --- a/src/components/vehicle-attachment-table/main.vue +++ b/src/components/vehicle-attachment-table/main.vue @@ -9,18 +9,21 @@ - + @@ -308,16 +311,6 @@ export default { margin-bottom: 16px; } - &__file-name { - color: #409eff; - cursor: pointer; - } - - &__file-name:hover, - &__file-name:focus-visible { - text-decoration: underline; - } - &__upload { display: flex; justify-content: flex-start; diff --git a/src/styles/element-ui.scss b/src/styles/element-ui.scss index 7ec1472..56985dc 100644 --- a/src/styles/element-ui.scss +++ b/src/styles/element-ui.scss @@ -1003,7 +1003,7 @@ td.el-table-fixed-column--right { text-overflow: clip; } -// 全站统一:内容型长文本列(文件名 / 单号 / 备注等)单元格内也放了 el-link, +// 全站统一:内容型长文本列(文件名 / 单号 / 备注等)单元格内也放了 el-link / span.attachment-file-name, // 会被上面两条「含 el-link 的单元格」规则一并命中,导致: // ① 上面第 950 行的 display: flex 让链接成为 flex item,flex item 默认 min-width: auto 无法收缩, // 超长文本(如未规范命名的附件名)会直接把列撑破; @@ -1011,7 +1011,7 @@ td.el-table-fixed-column--right { // 因此对「内容列」做显式兜底:单元格自身恢复块级 + 省略号截断,链接参与收缩并显示省略号。 // 用法:在 上配置 class-name="is-ellipsis-link"(动态列可 :class-name 绑定)。 // 操作列不要加此类,否则会失去自动换行能力。 -.el-table .el-table__body .cell.is-ellipsis-link:has(.el-link) { +.el-table .el-table__body .cell.is-ellipsis-link { display: block; overflow: hidden; text-overflow: ellipsis; @@ -1034,6 +1034,33 @@ td.el-table-fixed-column--right { white-space: nowrap; } } + + .attachment-file-name { + display: inline-block; + min-width: 0; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: middle; + } +} + +// 附件上传表格:文件名统一用 span,视觉与可点击行为对齐原 el-link primary +.attachment-file-name { + color: var(--el-color-primary, #409eff); + cursor: pointer; + + &:hover, + &:focus-visible { + text-decoration: underline; + } + + &.is-disabled { + color: var(--el-text-color-placeholder, #c0c4cc); + cursor: not-allowed; + text-decoration: none; + } } // 全站统一:Avue 内置行操作按钮(查看 / 编辑 / 删除 / 复制等)视觉对齐为 el-link 链接样式。 @@ -1294,6 +1321,6 @@ td.el-table-fixed-column--right { cursor: not-allowed; } } -.is-ellipsis-link{ +.is-ellipsis-link { overflow: hidden; } diff --git a/src/views/business/components/business-crud-page.vue b/src/views/business/components/business-crud-page.vue index 92b7702..077b574 100644 --- a/src/views/business/components/business-crud-page.vue +++ b/src/views/business/components/business-crud-page.vue @@ -2073,9 +2073,14 @@ class-name="is-ellipsis-link" > @@ -2344,14 +2349,16 @@
附件
- - {{ attachmentName(file) }} - + @keydown.enter="previewAttachment(file)" + @keydown.space.prevent="previewAttachment(file)" + >{{ attachmentName(file) }} -
@@ -2537,14 +2544,16 @@
附件
- - {{ file.originalName || file.name || '-' }} - + @keydown.enter="previewAttachment(file)" + @keydown.space.prevent="previewAttachment(file)" + >{{ file.originalName || file.name || '-' }} -
@@ -2826,11 +2835,21 @@ - + diff --git a/src/views/business/components/contract-attachment-section.vue b/src/views/business/components/contract-attachment-section.vue index f245546..bd3f02f 100644 --- a/src/views/business/components/contract-attachment-section.vue +++ b/src/views/business/components/contract-attachment-section.vue @@ -48,9 +48,14 @@ class-name="is-ellipsis-link" > diff --git a/src/views/business/components/master-order-detail.vue b/src/views/business/components/master-order-detail.vue index 5e43eb5..f881fc3 100644 --- a/src/views/business/components/master-order-detail.vue +++ b/src/views/business/components/master-order-detail.vue @@ -44,13 +44,16 @@
附件
-
@@ -330,7 +333,7 @@ export default { .transport-flow-tag { margin-left: 8px; } -.detail-meta { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px 24px; margin: 0; padding: 0 24px 20px; dt { margin-bottom: 6px; color: #909399; font-size: 13px; } dd { display: flex; flex-wrap: wrap; gap: 8px 16px; margin: 0; color: #303133; font-size: 14px; word-break: break-all; } :deep(.el-link) { font-size: 14px; } } +.detail-meta { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px 24px; margin: 0; padding: 0 24px 20px; dt { margin-bottom: 6px; color: #909399; font-size: 13px; } dd { display: flex; flex-wrap: wrap; gap: 8px 16px; margin: 0; color: #303133; font-size: 14px; word-break: break-all; } .attachment-file-name { font-size: 14px; max-width: 100%; } } .route-map { display: flex; align-items: flex-start; padding: 16px 24px 20px; border-top: 1px solid #eff1f7; overflow-x: auto; } .route-map__node { display: grid; flex: 0 0 150px; justify-items: center; gap: 6px; text-align: center; strong { font-size: 16px; white-space: nowrap; } small { color: #606266; white-space: nowrap; } } .route-map__progress { color: #303133 !important; } diff --git a/src/views/business/components/master-order-editor.vue b/src/views/business/components/master-order-editor.vue index ee62fc4..74ce647 100644 --- a/src/views/business/components/master-order-editor.vue +++ b/src/views/business/components/master-order-editor.vue @@ -269,9 +269,14 @@ class-name="is-ellipsis-link" >
diff --git a/src/views/business/components/shipping-template-page.vue b/src/views/business/components/shipping-template-page.vue index 9b799af..a2d2901 100644 --- a/src/views/business/components/shipping-template-page.vue +++ b/src/views/business/components/shipping-template-page.vue @@ -454,11 +454,20 @@ > - {{ attachmentName(row) }} - + diff --git a/src/views/business/components/transport-plan-page.vue b/src/views/business/components/transport-plan-page.vue index e6336a7..4d12b86 100644 --- a/src/views/business/components/transport-plan-page.vue +++ b/src/views/business/components/transport-plan-page.vue @@ -500,9 +500,14 @@ class-name="is-ellipsis-link" > @@ -679,14 +684,16 @@
附件
- - {{ attachmentName(file) }} - + @keydown.enter="previewAttachment(file)" + @keydown.space.prevent="previewAttachment(file)" + >{{ attachmentName(file) }} -
@@ -861,11 +868,21 @@ - + diff --git a/src/views/business/components/waybill-manage-page.vue b/src/views/business/components/waybill-manage-page.vue index 01834e1..5886577 100644 --- a/src/views/business/components/waybill-manage-page.vue +++ b/src/views/business/components/waybill-manage-page.vue @@ -1481,9 +1481,14 @@ class-name="is-ellipsis-link" > @@ -1864,14 +1869,16 @@
附件
- - {{ file.originalName || file.name || '-' }} - + @keydown.enter="previewAttachment(file)" + @keydown.space.prevent="previewAttachment(file)" + >{{ file.originalName || file.name || '-' }} -
diff --git a/src/views/business/contract-manage-change.vue b/src/views/business/contract-manage-change.vue index 08752b5..5507c25 100644 --- a/src/views/business/contract-manage-change.vue +++ b/src/views/business/contract-manage-change.vue @@ -106,6 +106,7 @@ placeholder="请输入" clearable @input="handleContractAmountInput" + @blur="formatContractAmountField" @clear="form.contractAmount = null" />
@@ -386,6 +387,19 @@ const normalizeOptionalAmount = value => { const number = Number(value); return Number.isFinite(number) && number >= 0 ? Number(number.toFixed(2)) : null; }; +const formatContractAmountInput = (value, emptyValue = null) => { + if ( + value === undefined || + value === null || + value === '' || + Number(value) === -1 || + String(value) === '-1' + ) { + return emptyValue; + } + const number = Number(value); + return Number.isFinite(number) && number >= 0 ? number.toFixed(2) : emptyValue; +}; const normalizeCustomPeriods = (periods = []) => @@ -541,6 +555,9 @@ export default { parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 2)}` : parts[0]; this.form.contractAmount = normalized === '' ? null : normalized; }, + formatContractAmountField() { + this.form.contractAmount = formatContractAmountInput(this.form.contractAmount); + }, loadDictionaries() { Promise.all([ getSystemDictionary({ code: 'currency_type' }), @@ -583,7 +600,7 @@ export default { copyCount: normalizeOptionalPositiveInteger(data.copyCount), invoiceCycle: normalizeOptionalPositiveInteger(data.invoiceCycle), paymentDays: normalizeOptionalPositiveInteger(data.paymentDays), - contractAmount: normalizeOptionalAmount(data.contractAmount), + contractAmount: formatContractAmountInput(data.contractAmount), templateFlag: data.templateFlag ?? 0, electronicSealFlag: data.electronicSealFlag ?? 0, archiveStatus: data.archiveStatus || '未归档', @@ -735,7 +752,14 @@ export default { .contract-basic-section > .dialog-section-title { margin-bottom: 20px; color: #303133; } .contract-basic-section__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 48px; } .contract-basic-section__change-type { grid-column: 1 / -1; } -.contract-basic-section__remark { width: 100%; margin-bottom: 0 !important; } +.contract-basic-section__remark { + width: 100%; + margin-bottom: 24px !important; + + &:last-child { + margin-bottom: 0 !important; + } +} .contract-basic-section__date-range { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 8px; width: 360px; max-width: 100%; } .contract-basic-section__date-range span { color: #606266; } .contract-basic-section__date-range :deep(.el-date-editor) { width: auto; min-width: 0; } diff --git a/src/views/business/contract-manage.vue b/src/views/business/contract-manage.vue index 1c81ff3..098377a 100644 --- a/src/views/business/contract-manage.vue +++ b/src/views/business/contract-manage.vue @@ -525,6 +525,7 @@ placeholder="请输入" clearable @input="handleContractAmountInput" + @blur="formatContractAmountField" @clear="form.contractAmount = null" />
@@ -1098,6 +1099,19 @@ const normalizeOptionalAmount = (value, emptyValue = null) => { const number = Number(value); return Number.isFinite(number) && number >= 0 ? Number(number.toFixed(2)) : emptyValue; }; +const formatContractAmountInput = (value, emptyValue = null) => { + if ( + value === undefined || + value === null || + value === '' || + Number(value) === -1 || + String(value) === '-1' + ) { + return emptyValue; + } + const number = Number(value); + return Number.isFinite(number) && number >= 0 ? number.toFixed(2) : emptyValue; +}; const normalizeProjectValue = value => { if (value === undefined || value === null || value === '' || String(value) === '-1') return ''; return value; @@ -1794,7 +1808,7 @@ export default { copyCount: normalizeOptionalInteger(detail.copyCount, ''), invoiceCycle: normalizeOptionalInteger(detail.invoiceCycle, ''), paymentDays: normalizeOptionalInteger(detail.paymentDays, ''), - contractAmount: normalizeOptionalAmount(detail.contractAmount, null), + contractAmount: formatContractAmountInput(detail.contractAmount, null), archiveStatus: detail.archiveStatus || '未归档', feeGenerationMode: detail.feeGenerationMode || (Number(detail.billingEnabled) === 0 ? 'manual' : 'system'), @@ -2356,6 +2370,9 @@ export default { parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 2)}` : parts[0]; this.form.contractAmount = normalized === '' ? null : normalized; }, + formatContractAmountField() { + this.form.contractAmount = formatContractAmountInput(this.form.contractAmount, null); + }, positiveIntegerInput(prop, value) { const normalized = String(value || '').replace(/\D/g, '').replace(/^0+/, ''); this.form[prop] = normalized; @@ -2929,13 +2946,8 @@ export default { detailValue(prop) { const value = this.detailRow[prop]; if (prop === 'contractAmount') { - return value === undefined || - value === null || - value === '' || - Number(value) === -1 || - String(value) === '-1' - ? '-' - : value; + const amountText = formatContractAmountInput(value, ''); + return amountText === '' ? '-' : amountText; } if ( prop === 'contractStage' || @@ -3248,7 +3260,11 @@ export default { &__remark { width: 100%; - margin-bottom: 0 !important; + margin-bottom: 24px !important; + + &:last-child { + margin-bottom: 0 !important; + } } &__date-range { diff --git a/src/views/business/loading-manage.vue b/src/views/business/loading-manage.vue index 9a79744..b3973e1 100644 --- a/src/views/business/loading-manage.vue +++ b/src/views/business/loading-manage.vue @@ -1,6 +1,6 @@