✨ 优化港口地图回填、合同展示与运单导入空值

地图选点回填国家省市区;合同选择显示编号/名称;常用数据组织筛选修正;运单详情将-1费用字段显示为空。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-09 17:07:56 +08:00
parent 570fbeb537
commit a1da644c49
25 changed files with 568 additions and 217 deletions
+30 -7
View File
@@ -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(),
};
+256 -45
View File
@@ -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 {
</style>
<style lang="scss">
/* 港口码头编辑/新增弹窗:对齐 railway-station 统一风格(灰底 + 白卡表单 + 浮动底栏,padding 24px 与全局弹窗标准一致) */
//.port-terminal-edit-dialog {
// /* 灰底内容区 */
// .el-dialog__body {
// background-color: #f2f2f2;
// padding: 8px;
// }
// /* 表单白卡(与全局 .el-dialog .avue-form 一致,padding 24px 对齐 customer-archive / railway-station) */
// .avue-form {
// background: #fff;
// border-radius: 6px;
// box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
// padding: 24px !important;
// margin-bottom: 0;
// }
// /* 底部按钮区白卡(Avue 弹窗 footer 在 body 内,接续白卡) */
// .avue-dialog__footer {
// background: #fff;
// border-radius: 0 0 6px 6px;
// box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
// padding: 16px 24px !important;
// display: flex;
// justify-content: flex-end;
// }
// /* label 宽度统一 108px(对齐 railway-station,标签对齐更整齐) */
// .el-form-item__label {
// width: 108px !important;
// }
// /* 所有 input 文本框、select 下拉控件统一宽度 250px */
// .el-input,
// .el-select {
// width: 250px !important;
// }
// /* 备注宽度 85% */
// .remark-input {
// width: 85% !important;
// }
//}
/* 港口码头编辑/新增弹窗:表单项行间距略高于全局默认 */
.port-terminal-edit-dialog {
.el-form-item,
.el-form-item--default.el-form-item,
.el-form-item--small.el-form-item,
.el-form-item--large.el-form-item,
.el-form-item--medium.el-form-item {
margin-bottom: 22px;
}
}
</style>
+15 -11
View File
@@ -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();