diff --git a/src/api/business/waybill-manage.js b/src/api/business/waybill-manage.js index e90982c..3778653 100644 --- a/src/api/business/waybill-manage.js +++ b/src/api/business/waybill-manage.js @@ -95,3 +95,5 @@ export const exportImportFailures = data => }); export const confirmImport = data => request({ url: `${baseUrl}/import-batch/confirm`, method: 'post', data, timeout: 120000 }); +export const checkImportDuplicates = data => + request({ url: `${baseUrl}/import-batch/duplicates`, method: 'post', data, timeout: 120000 }); diff --git a/src/mixins/organization-search.js b/src/mixins/organization-search.js index fd06b59..b9c6760 100644 --- a/src/mixins/organization-search.js +++ b/src/mixins/organization-search.js @@ -1,4 +1,4 @@ -import { getDeptTree } from '@/api/system/dept'; +import { getDeptTree, getPlatformCompanySelect } from '@/api/system/dept'; import { ElCascader } from 'element-plus'; import { h } from 'vue'; @@ -14,6 +14,10 @@ import { h } from 'vue'; * 3. 在 created / 初始化时调用 this.loadOrganizationOptions() * 4. 需要「未分配组织」筛选项的页面在 data 里置 enableUnassignedOrgFilter: true; * 该项只追加进搜索树 organizationSearchTreeOptions,表单级联 organizationTreeOptions 不含哨兵 + * 5. 表单「所属组织」只允许选平台公司的页面(运力管理)在 data 里置 + * restrictFormOrgToPlatformCompany: true,表单级联样式改用 + * buildFormOrganizationTree(...) 的结果,并在打开表单时把回显的组织名写进 + * formOrganizationLegacyName;只影响表单,搜索栏仍是全量组织 * * 说明: * - 自动识别 organizationName / deptName 两种 prop,并挂载 column.renderSearch @@ -34,6 +38,11 @@ const ORG_PROPS = ['organizationName', 'deptName']; // 后端换成空串会让整段组织条件不生效,筛选静默失效。前端与后端这几处必须同步修改。 const UNASSIGNED_ORG_FILTER = '__UNASSIGNED_ORG__'; +// 表单「所属组织」限定只能选平台公司时,存量记录里的旧组织名在组织树里已经找不到 +// (节点被删或改名),用这个 id 补一个只读节点,否则编辑老数据时所属组织显示为空、 +// 保存即把原值丢掉。 +const FORM_LEGACY_ORG_ID = '__LEGACY_ORG__'; + export default { data() { return { @@ -44,6 +53,14 @@ export default { organizationLoading: false, // 组织筛选是否追加「未分配组织」一项;页面在 data 里覆盖为 true 才生效 enableUnassignedOrgFilter: false, + // 表单「所属组织」是否只允许选平台公司;页面在 data 里覆盖为 true 才生效 + restrictFormOrgToPlatformCompany: false, + // 平台公司部门 id(字符串),loadOrganizationOptions 时一并拉取 + platformCompanyOrgIds: [], + // 表单当前回显的组织名,树里没有同名节点时用它补一个只读选项 + formOrganizationLegacyName: '', + // 进行中的组织数据请求,并发调用共用(不是响应式状态,只做去重) + organizationOptionsPromise: null, }; }, computed: { @@ -54,9 +71,12 @@ export default { ? [this.unassignedOrgOption(), ...this.organizationTreeOptions] : this.organizationTreeOptions; }, - // 所属组织级联配置:checkStrictly 允许选中任意层级,不必选到末级 + // 所属组织级联配置:checkStrictly 允许选中任意层级,不必选到末级。 + // 限定只能选平台公司时用 disabled 函数只放开平台公司:非平台公司的非末级节点 + // 在 checkStrictly 下仍可展开(el-cascader 的 expandable 判断),末级节点点击无效, + // 两者都选不中。disabled 在节点渲染时求值,平台公司 id 到手后节点会自行重算。 organizationCascaderProps() { - return { + const props = { value: 'id', label: 'label', children: 'children', @@ -64,15 +84,25 @@ export default { checkStrictly: true, expandTrigger: 'click', }; + if (this.restrictFormOrgToPlatformCompany) { + props.disabled = data => this.platformCompanyOrgDisabled(data); + } + return props; }, }, methods: { loadOrganizationOptions() { - if (this.organizationLoading) return; + // 并发调用共用同一次请求,返回的 Promise 在数据就绪后才 resolve + if (this.organizationOptionsPromise) return this.organizationOptionsPromise; this.organizationLoading = true; const tenantId = this.userInfo?.tenantId; - getDeptTree(tenantId) - .then(res => { + // 平台公司下拉失败不能拖垮组织树,单独兜住,失败时保持上一次的可选范围 + const platformCompanyRequest = this.restrictFormOrgToPlatformCompany + ? getPlatformCompanySelect().catch(() => null) + : Promise.resolve(null); + this.organizationOptionsPromise = Promise.all([getDeptTree(tenantId), platformCompanyRequest]) + .then(([res, platformRes]) => { + this.applyPlatformCompanyOptions(platformRes); const source = res.data?.data || res.data || []; const tree = this.excludeExternalOrganization ? this.excludeExternalOrganization(source) @@ -90,7 +120,21 @@ export default { }) .finally(() => { this.organizationLoading = false; + this.organizationOptionsPromise = null; }); + return this.organizationOptionsPromise; + }, + // 组织树与平台公司列表就绪后执行回调;已加载过则立即执行 + whenOrganizationOptionsReady(callback) { + const pending = this.organizationTreeFlatOptions.length + ? Promise.resolve() + : this.loadOrganizationOptions(); + return Promise.resolve(pending).then(callback, () => callback()); + }, + applyPlatformCompanyOptions(res) { + if (!res) return; + const list = res.data?.data || res.data || []; + this.platformCompanyOrgIds = (Array.isArray(list) ? list : []).map(item => String(item.id)); }, // 默认排除「外部组织」节点;页面可覆盖 excludeExternalOrganization(tree = []) { @@ -143,7 +187,9 @@ export default { for (const node of tree || []) { const path = [...parents, String(node.id)]; if (String(node.id) === String(target)) return path; - const matched = this.findOrgPath(target, node.children, path); + // 必须显式传 node.children || []:叶节点的 children 是 undefined, + // 传 undefined 会触发默认参数重新拿到整棵树,逐层重复扫描直到爆栈 + const matched = this.findOrgPath(target, node.children || [], path); if (matched) return matched; } return null; @@ -152,6 +198,52 @@ export default { findOrgName(id) { return this.findOrgById(id)?.rawLabel || ''; }, + // 级联节点的 disabled 判定:只有平台公司可选。页面自建 props 时也复用它 + platformCompanyOrgDisabled(data) { + return !this.platformCompanyOrgIds.includes(String(data?.id ?? '')); + }, + // 组织名对应的节点是否为平台公司;名称在树里不存在时按「不是」处理 + isPlatformCompanyOrgName(name) { + const value = String(name ?? '').trim(); + if (!value) return false; + const node = this.organizationTreeFlatOptions.find(item => item.rawLabel === value); + return !!node && this.platformCompanyOrgIds.includes(String(node.id)); + }, + // 新增表单的默认组织:只有落在平台公司上才预填,否则留空(不能让用户从默认值 + // 直接带出一个选不中的组织名) + defaultFormOrganizationName(name) { + return this.isPlatformCompanyOrgName(name) ? String(name).trim() : ''; + }, + // 表单级联的选项树:限定平台公司时,把回显的原组织名补成一个只读节点, + // 否则存量记录的组织在树里找不到,编辑时显示为空、保存即丢原值 + buildFormOrganizationTree(tree = this.organizationTreeOptions) { + const source = tree || []; + if (!this.restrictFormOrgToPlatformCompany) return source; + const name = String(this.formOrganizationLegacyName || '').trim(); + if (!name) return source; + if (this.organizationTreeFlatOptions.some(item => item.rawLabel === name)) return source; + return [...source, { id: FORM_LEGACY_ORG_ID, label: name, rawLabel: name }]; + }, + // 表单级联回显用的路径:树里有该组织就用真实路径,否则落到原组织兜底节点 + resolveFormOrgPath(name) { + const path = this.resolveOrgPath(name); + if (path.length) return path; + const value = String(name ?? '').trim(); + if (!value) return []; + return value === String(this.formOrganizationLegacyName || '').trim() + ? [FORM_LEGACY_ORG_ID] + : []; + }, + // 记录表单当前回显的组织名:能匹配真实节点(或已清空)就不需要兜底节点, + // 否则记下来,由 buildFormOrganizationTree 补成只读选项 + syncFormOrganizationLegacy(name) { + const value = String(name ?? '').trim(); + if (!value || this.organizationTreeFlatOptions.some(item => item.rawLabel === value)) { + this.formOrganizationLegacyName = ''; + return; + } + this.formOrganizationLegacyName = value; + }, // 组织名称 → 级联路径(表单回显用;部门树异步加载完成后会自动重算) resolveOrgPath(name) { const value = String(name ?? '').trim(); @@ -164,7 +256,13 @@ export default { const value = Array.isArray(path) ? path.filter(item => item !== undefined && item !== null && item !== '') : []; - return value.length ? this.findOrgById(value)?.rawLabel || '' : ''; + if (!value.length) return ''; + const node = this.findOrgById(value); + if (node) return node.rawLabel || ''; + // 原组织兜底节点不在扁平表里,直接取它代表的历史名称 + return String(value[value.length - 1]) === FORM_LEGACY_ORG_ID + ? String(this.formOrganizationLegacyName || '').trim() + : ''; }, // 搜索提交前把级联路径数组就地转成组织名称(后端按名称过滤) normalizeOrganizationSearch(params, prop = 'organizationName') { diff --git a/src/option/business/transport-plan.js b/src/option/business/transport-plan.js index b7d1200..39da9e6 100644 --- a/src/option/business/transport-plan.js +++ b/src/option/business/transport-plan.js @@ -208,6 +208,9 @@ export const config = { transportPlanImportTitle: '导入运输计划', transportPlanTemplateUrl: '/blade-transport/transport-plan/export-template', attachmentTitle: '附件', + defaultForm: { + transportType: 'road', + }, searchRangeMap: { planStartDateRange: ['planStartDateStart', 'planStartDateEnd'], createTimeRange: ['createTimeStart', 'createTimeEnd', '00:00:00', '23:59:59'], diff --git a/src/views/business/components/transport-plan-page.vue b/src/views/business/components/transport-plan-page.vue index 374c9e4..799ec3a 100644 --- a/src/views/business/components/transport-plan-page.vue +++ b/src/views/business/components/transport-plan-page.vue @@ -1861,7 +1861,7 @@ /> - + @@ -2104,7 +2103,7 @@ /> - + - + this.isBillingFieldEmpty(row[prop])); if (emptyField) return requiredMessage(emptyField[1]); diff --git a/src/views/business/components/waybill-import-dialog.vue b/src/views/business/components/waybill-import-dialog.vue index e5db21c..33339d6 100644 --- a/src/views/business/components/waybill-import-dialog.vue +++ b/src/views/business/components/waybill-import-dialog.vue @@ -484,20 +484,24 @@ 保存取消 rows.value.filter(row => row._duplicate)); -const previewRows = computed(() => - previewTab.value === 'duplicate' ? duplicateRows.value : rows.value -); +const historyDuplicateRows = computed(() => rows.value.filter(row => row._historyWaybillNo)); +const previewRows = computed(() => { + if (previewTab.value === 'duplicate') return duplicateRows.value; + if (previewTab.value === 'history') return historyDuplicateRows.value; + return rows.value; +}); const carrierContractsLoaded = ref(false), hasCarrierContracts = ref(null); const carrierTypes = ['承运商', '自运']; @@ -795,6 +800,16 @@ const detailColumns = [ { prop: 'remark', label: '备注', editor: 'textarea', minWidth: 200 }, { prop: 'waybillIdentifier', label: '同一运单标识号', editor: 'input', minWidth: 150 }, ]; +// 与已有运单重复页签的列与明细一致,只在运单批次号后多一列已有运单号。 +const previewColumns = computed(() => + previewTab.value === 'history' + ? [ + detailColumns[0], + { prop: '_historyWaybillNo', label: '已有运单号', minWidth: 220 }, + ...detailColumns.slice(1), + ] + : detailColumns +); const requiredDetailFields = [ { prop: 'vehicleNo', label: '车牌号/船班号/船次号/班次号' }, { prop: 'transportType', label: '运输方式' }, @@ -1187,6 +1202,7 @@ const loadBatchRows = async batchId => { batchNo: item.batchNo || form.batchNo, })); refreshDuplicateFlags(); + await refreshHistoryDuplicates(); rowSelection.value = []; previewTab.value = 'all'; }; @@ -1509,6 +1525,8 @@ const fileChange = async (file, list) => { if (isValid) { // 校验通过,填充表格数据 rows.value = parsedRows; + // 传 rows.value(响应式代理),否则后续写入不触发重新渲染 + await refreshHistoryDuplicates(rows.value); } else { // 校验失败,清空数据 rows.value = []; @@ -1526,16 +1544,12 @@ const IMPORT_BATCH_SIZE = 100; const importFieldText = value => String(value ?? '').trim(); -/** 同一运单批次号下,全部导入字段一致时标记为疑似重复。已在线确认的行不再标为重复。 */ +/** 同一运单批次号下,全部导入字段一致时标记为 Excel 内重复。 */ const refreshDuplicateFlags = (list = rows.value) => { const groups = new Map(); list.forEach(row => { const signature = detailColumns.map(column => importFieldText(row[column.prop])).join('\u0001'); const batchNo = importFieldText(row.batchNo || form.batchNo); - if (row._duplicateConfirmed && row._duplicateSignature !== signature) { - row._duplicateConfirmed = false; - } - row._duplicateSignature = signature; const groupKey = `${batchNo}\u0002${signature}`; const group = groups.get(groupKey) || []; group.push(row); @@ -1544,19 +1558,39 @@ const refreshDuplicateFlags = (list = rows.value) => { groups.forEach(group => { const duplicated = group.length > 1; group.forEach(row => { - row._duplicate = duplicated && !row._duplicateConfirmed; + row._duplicate = duplicated; }); }); }; -const confirmDuplicateRow = row => { - row._duplicateConfirmed = true; - refreshDuplicateFlags(); - ElMessage.success('已确认,该行将按正常数据导入'); +/** 逐行提交给后端与项目下已有运单比对,命中的行带上已有运单号。比对失败不影响导入。 */ +const refreshHistoryDuplicates = async (list = rows.value) => { + if (!list.length || !form.projectId) { + list.forEach(row => { + row._historyWaybillNo = ''; + }); + return; + } + let duplicates = []; + try { + const res = await api.checkImportDuplicates(buildImportPayload(form.status, list)); + const data = res?.data?.data ?? res?.data ?? res; + duplicates = Array.isArray(data) ? data : []; + } catch (error) { + console.error('与已有运单重复校验失败:', error); + duplicates = []; + } + const numbersByRow = new Map( + duplicates.map(item => [item.rowIndex, (item.waybillNos || []).filter(Boolean)]) + ); + list.forEach((row, index) => { + row._historyWaybillNo = (numbersByRow.get(index) || []).join('、'); + }); }; const stripInternalRowFields = row => { - const { _key, _duplicate, _duplicateKey, _duplicateConfirmed, _duplicateSignature, _editing, _editSnapshot, ...data } = row; + const { _key, _duplicate, _duplicateKey, _editing, _editSnapshot, _historyWaybillNo, ...data } = + row; return data; }; @@ -1771,6 +1805,7 @@ const saveRow = row => { row._editing = false; delete row._editSnapshot; refreshDuplicateFlags(); + refreshHistoryDuplicates(); }; const cancelRow = row => { if (row._editSnapshot) Object.assign(row, row._editSnapshot); @@ -1780,12 +1815,14 @@ const cancelRow = row => { const deleteRow = row => { rows.value = rows.value.filter(item => item !== row); refreshDuplicateFlags(); + refreshHistoryDuplicates(); }; const removeSelectedRows = () => { const keys = new Set(rowSelection.value.map(row => row._key)); rows.value = rows.value.filter(row => !keys.has(row._key)); rowSelection.value = []; refreshDuplicateFlags(); + refreshHistoryDuplicates(); }; const normalizeOptionalPlanId = value => { if (value === undefined || value === null || value === '') return null; @@ -1849,7 +1886,11 @@ const confirmImport = async () => { await formRef.value?.validate(); if (!validateImportRows()) return; const count = rows.value.filter(row => row._duplicate).length; - if (count) await ElMessageBox.confirm(`当前导入有${count}条疑似重复数据,是否确认导入?`, '提示'); + if (count) + await ElMessageBox.confirm(`当前导入有${count}条Excel内重复数据,是否确认导入?`, '提示'); + const historyCount = rows.value.filter(row => row._historyWaybillNo).length; + if (historyCount) + await ElMessageBox.confirm(`当前导入有${historyCount}条与已有运单重复,是否确认导入?`, '提示'); if (form.importType === 'settlement' && form.status === 'completed') await ElMessageBox.confirm('确认后将同时生成运单、应收应付明细、计结算单,是否继续?', '提示'); diff --git a/src/views/business/components/waybill-manage-page.vue b/src/views/business/components/waybill-manage-page.vue index d1958d3..1001517 100644 --- a/src/views/business/components/waybill-manage-page.vue +++ b/src/views/business/components/waybill-manage-page.vue @@ -1337,7 +1337,7 @@ @@ -1345,7 +1345,7 @@ @@ -1599,14 +1599,7 @@ 取消 - - {{ completeActionLabel }} - - + {{ completeActionLabel }} @@ -5178,12 +5171,9 @@ export default { return false; } const status = this.statusValue(row); - // 待回单确认的「完成」由确认回单入口承担(走打卡节点与未了结异常校验); - // 只有确认回单不可用时才退回这里的普通完成 - if (status === 'pending_return_confirm') { - return !this.canConfirmReturn(row); - } - return status === 'processing' || status === 'running'; + // 同一枚「完成」按钮覆盖两种状态:待回单确认走确认回单(后端校验司机打卡节点齐全且无未了结异常), + // 进行中/处理中二次确认后直接置完成 + return status === 'processing' || status === 'running' || status === 'pending_return_confirm'; }, canConfirmReturn(row) { return ( @@ -8453,7 +8443,6 @@ export default { } if (!this.isTaskFullMode) { requiredFields.push(['cargoName', '货物名称']); - requiredFields.push(['model', '型号']); requiredFields.push(['quantity', '数量']); } const emptyField = requiredFields.find(([prop]) => this.isBillingFieldEmpty(row[prop])); @@ -10253,6 +10242,14 @@ export default { this.mileageForm = { id: '', mileage: '', mileageRemark: '' }; this.$refs.mileageFormRef?.clearValidate(); }, + // 行操作「完成」的唯一入口:待回单确认的运单走确认回单(后端校验司机打卡节点齐全且无未了结异常, + // 配置里没有确认回单动作时退回普通完成);进行中/处理中的运单二次确认后直接置完成。 + handleComplete(row) { + if (this.canConfirmReturn(row)) { + return this.handleAction('confirmReturn', row); + } + return this.handleAction('complete', row); + }, handleAction(action, row) { if (action === 'reassign') { this.openReassignDrawer(row); @@ -10266,11 +10263,13 @@ export default { reassign: '重新派单', confirmReturn: '完成', }[action]; - // 回单确认是终态动作(本轮不做撤销),把后果写在提示里,别让人以为还能退回 - const confirmText = - action === 'confirmReturn' - ? '回单确认后该运单将置为已完成,且不可撤销。是否继续?' - : `确定${actionName}该${this.config.title}?`; + // 回单确认与「完成」都是终态动作(本轮不做撤销),把后果写在提示里,别让人以为还能退回 + let confirmText = `确定${actionName}该${this.config.title}?`; + if (action === 'confirmReturn') { + confirmText = '回单确认后该运单将置为已完成,且不可撤销。是否继续?'; + } else if (action === 'complete') { + confirmText = `确定完成该${this.config.title}?完成后将置为已完成,且不可撤销。`; + } this.$confirm(confirmText, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', diff --git a/src/views/business/transport-plan-dispatch.vue b/src/views/business/transport-plan-dispatch.vue index 6acc086..d1738f2 100644 --- a/src/views/business/transport-plan-dispatch.vue +++ b/src/views/business/transport-plan-dispatch.vue @@ -577,7 +577,7 @@ export default { this.$message.warning(`第${index + 1}条调度明细缺少承运商信息`); return; } - // 司机是否必填由调度明细弹窗按承运类型判定(承运商承运时司机选填),此处不再重复校验 + // 司机与手机号对全部承运类型都是选填,由调度明细弹窗校验已填部分的格式,此处不重复校验 if (!row.vehicleNo) { this.$message.warning(`第${index + 1}条调度明细缺少车牌号`); return; diff --git a/src/views/business/voucher-manage.vue b/src/views/business/voucher-manage.vue index 54fb8b2..376915b 100644 --- a/src/views/business/voucher-manage.vue +++ b/src/views/business/voucher-manage.vue @@ -389,6 +389,18 @@ v-model="batchQuery.batchNo" clearable placeholder="请输入" /> { batchDialogMode.value = voucher?.id ? 'change' : 'upload'; batchTarget.value = voucher?.id ? voucher : undefined; batchSelection.value = []; + // 弹窗内的搜索条件是模块级状态,不重置就会带着上一次的筛选条件打开。 + resetBatchSearch(); + batchRows.value = []; + batchPage.total = 0; batchVisible.value = true; + if (!projectOptions.value.length) await loadProjects(); await loadBatches(); if (voucher?.waybillBatchNo) { const currentBatch = batchRows.value.find(item => item.batchNo === voucher.waybillBatchNo); @@ -1033,6 +1059,7 @@ const openBatchDialog = async row => { const loadBatches = async () => { const res = await api.getWaybillBatches(batchPage.current, batchPage.size, { batchNo: batchQuery.batchNo, + projectId: batchQuery.projectId, createUser: batchQuery.createUser, waybillCount: batchQuery.waybillCount, createTimeStart: batchCreateRange.value?.[0] ? `${batchCreateRange.value[0]} 00:00:00` : '', @@ -1053,10 +1080,18 @@ const handleWaybillCountInput = value => { batchQuery.waybillCount = filtered.replace(/^0+/, ''); } }; -const resetBatchQuery = () => { - Object.assign(batchQuery, { batchNo: '', createUser: '', waybillCount: undefined }); +const resetBatchSearch = () => { + Object.assign(batchQuery, { + batchNo: '', + projectId: '', + createUser: '', + waybillCount: undefined, + }); batchCreateRange.value = []; batchPage.current = 1; +}; +const resetBatchQuery = () => { + resetBatchSearch(); loadBatches(); }; const updateWaybillBatch = async (target, selected) => { diff --git a/src/views/transportCapacity/driver.vue b/src/views/transportCapacity/driver.vue index ab82766..c99b1bd 100644 --- a/src/views/transportCapacity/driver.vue +++ b/src/views/transportCapacity/driver.vue @@ -589,7 +589,7 @@ { + if (!this.driverBox || this.driverForm.id || this.driverForm.organizationName) return; + const name = this.getDefaultOrganizationName(); + this.driverForm.organizationName = name; + this.syncFormOrganizationLegacy(name); + }); return; } getDetail(row.id).then(res => { @@ -1218,11 +1237,13 @@ export default { postList: detail.posts ? detail.posts.split(',').filter(Boolean) : [], }; this.syncAddressRegionPath(); + this.syncFormOrganizationLegacy(this.driverForm.organizationName); this.driverBox = true; }); }, resetDriver() { this.driverForm = emptyForm(); + this.formOrganizationLegacyName = ''; this.drivingLicenseUploads = {}; this.readonly = false; this.submitAttempted = false; diff --git a/src/views/transportCapacity/ship.vue b/src/views/transportCapacity/ship.vue index 42895c8..c8acb8e 100644 --- a/src/views/transportCapacity/ship.vue +++ b/src/views/transportCapacity/ship.vue @@ -147,7 +147,7 @@ { + if (!this.shipBox || this.shipForm.id || this.shipForm.organizationName) return; + this.shipForm.organizationName = this.getDefaultOrganizationName(); + }); return; } getDetail(row.id).then(res => { this.shipForm = { ...emptyForm(), ...(res.data.data || {}) }; + this.syncFormOrganizationLegacy(this.shipForm.organizationName); this.shipBox = true; }); }, @@ -890,6 +907,7 @@ export default { }, resetShip() { this.shipForm = emptyForm(); + this.formOrganizationLegacyName = ''; this.readonly = false; this.submitLoading = false; this.$refs.shipForm?.clearValidate(); diff --git a/src/views/transportCapacity/vehicle.vue b/src/views/transportCapacity/vehicle.vue index c83075d..96da88e 100644 --- a/src/views/transportCapacity/vehicle.vue +++ b/src/views/transportCapacity/vehicle.vue @@ -347,7 +347,7 @@ this.platformCompanyOrgDisabled(data) } + : this.formOrgCascaderProps; + }, // 所属组织级联:本地保存 id 路径,名称同步到表单字段供后端使用 organizationCascaderValue: { get() { @@ -769,7 +777,9 @@ export default { }, set(path) { this.organizationCascaderPath = Array.isArray(path) ? [...path] : []; - this.vehicleForm.organizationName = this.resolveOrgName(this.organizationCascaderPath); + const name = this.resolveOrgName(this.organizationCascaderPath); + this.vehicleForm.organizationName = name; + this.syncFormOrganizationLegacy(name); }, }, // 使用部门级联:独立路径状态,避免与所属组织共用计算回写导致选不中 @@ -810,7 +820,6 @@ export default { }, created() { this.initDeptTree(); - this.loadOrganizationOptions(); }, watch: { organizationTreeOptions() { @@ -852,17 +861,15 @@ export default { })); }, syncOrgCascaderPaths() { - this.organizationCascaderPath = this.resolveOrgPath(this.vehicleForm.organizationName); + this.organizationCascaderPath = this.resolveFormOrgPath(this.vehicleForm.organizationName); this.useDepartmentCascaderPath = this.resolveOrgPath(this.vehicleForm.useDepartment); }, initDeptTree() { + this.loadOrganizationOptions(); getDeptTree(this.userInfo.tenantId).then(res => { this.organizationOptions = this.flattenDeptOptions(res.data.data || []); const column = this.findColumn(this.option.column, 'organizationName'); column.dicData = this.organizationOptions; - if (this.vehicleBox && !this.vehicleForm.id && !this.vehicleForm.organizationName) { - this.vehicleForm.organizationName = this.getCurrentOrganizationName(); - } if (this.vehicleBox && !this.vehicleForm.useDepartment) { this.vehicleForm.useDepartment = this.getCurrentOrganizationName(); } @@ -894,6 +901,10 @@ export default { ); return currentDept?.rawLabel || this.userInfo.deptName || this.userInfo.dept_name || ''; }, + // 新增时的默认组织:当前登录人组织是平台公司才预填,否则留空 + getDefaultOrganizationName() { + return this.defaultFormOrganizationName(this.getCurrentOrganizationName()); + }, digitsOnly(value = '') { return String(value).replace(/\D/g, ''); }, @@ -961,11 +972,18 @@ export default { this.readonly = readonly; if (!row?.id) { this.vehicleForm = emptyForm(); - this.vehicleForm.organizationName = this.getCurrentOrganizationName(); + // 默认组织要先有组织树和平台公司列表才能判定,先留空、数据到位再补 + this.vehicleForm.organizationName = ''; + this.formOrganizationLegacyName = ''; this.vehicleForm.useDepartment = this.getCurrentOrganizationName(); this.syncOrgCascaderPaths(); this.syncPlateNo(false); this.vehicleBox = true; + this.whenOrganizationOptionsReady(() => { + if (!this.vehicleBox || this.vehicleForm.id || this.vehicleForm.organizationName) return; + this.vehicleForm.organizationName = this.getDefaultOrganizationName(); + this.syncOrgCascaderPaths(); + }); return; } getDetail(row.id).then(res => { @@ -977,6 +995,7 @@ export default { if (!this.vehicleForm.useDepartment) { this.vehicleForm.useDepartment = this.getCurrentOrganizationName(); } + this.syncFormOrganizationLegacy(this.vehicleForm.organizationName); this.syncOrgCascaderPaths(); this.splitPlateNo(); this.syncPlateNo(false); @@ -989,6 +1008,7 @@ export default { }, resetVehicle() { this.vehicleForm = emptyForm(); + this.formOrganizationLegacyName = ''; this.organizationCascaderPath = []; this.useDepartmentCascaderPath = []; this.vehicleCertificateUploads = {}; @@ -1610,6 +1630,16 @@ export default { } } +// 列表组织列超长时单行省略 +.org-cell-text { + display: inline-block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: bottom; +} + // 编辑弹窗:灰底 + section-card 白卡分组 .vehicle-form { :deep(.el-form-item__label) { @@ -1641,18 +1671,7 @@ export default { flex: 0 0 calc(4em + 24px) !important; width: calc(4em + 24px) !important; } -} -.org-cell-text { - display: inline-block; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - vertical-align: bottom; -} - -.vehicle-form { .org-cascader-tooltip { width: 100%; }