diff --git a/src/components/empty-pagination/main.vue b/src/components/empty-pagination/main.vue new file mode 100644 index 0000000..f999b9b --- /dev/null +++ b/src/components/empty-pagination/main.vue @@ -0,0 +1,57 @@ + + + + + + + + + diff --git a/src/main.js b/src/main.js index 9d34f1d..a9fd7fa 100644 --- a/src/main.js +++ b/src/main.js @@ -27,6 +27,7 @@ import codeEditor from './components/code-editor/main.vue'; import cronEditor from './components/cron-editor/main.vue'; import basicBlock from './components/basic-block/main.vue'; import basicContainer from './components/basic-container/main.vue'; +import emptyPagination from './components/empty-pagination/main.vue'; import thirdRegister from './components/third-register/main.vue'; import flowDesign from './components/flow-design/main.vue'; import flowDesignStep from './components/flow-design-step/main.vue'; @@ -45,6 +46,7 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) { } app.component('basicContainer', basicContainer); app.component('basicBlock', basicBlock); +app.component('emptyPagination', emptyPagination); app.component('highlight', highlight); app.component('codeEditor', codeEditor); app.component('cronEditor', cronEditor); diff --git a/src/option/vehicle/accident-record.js b/src/option/vehicle/accident-record.js index 9c1ef49..d7ebc04 100644 --- a/src/option/vehicle/accident-record.js +++ b/src/option/vehicle/accident-record.js @@ -36,6 +36,10 @@ export const option = { dialogWidth: 1050, labelPosition: 'top', tip: false, + searchBtnText: '查询', + emptyBtnText: '重置', + saveBtnText: '提交', + updateBtnText: '提交', searchShow: true, searchMenuSpan: 6, border: true, @@ -90,6 +94,7 @@ export const option = { dicData: accidentNatureDic, search: true, minWidth: 120, + rules: [{ required: true, message: '请选择事故性质', trigger: 'change' }], }, { label: '事故责任', @@ -98,12 +103,14 @@ export const option = { dicData: accidentResponsibilityDic, search: true, minWidth: 120, + rules: [{ required: true, message: '请选择事故责任', trigger: 'change' }], }, { label: '直接经济损失', prop: 'directEconomicLoss', type: 'number', precision: 2, + append: '元', minWidth: 140, rules: [{ validator: validateNonNegative, trigger: 'blur' }], }, @@ -112,6 +119,7 @@ export const option = { prop: 'insuranceClaimAmount', type: 'number', precision: 2, + append: '元', minWidth: 140, rules: [{ validator: validateNonNegative, trigger: 'blur' }], }, @@ -122,7 +130,8 @@ export const option = { minRows: 3, span: 24, search: true, - hide: true, + minWidth: 220, + overHidden: true, rules: [{ max: 500, message: '最多 500 个字符', trigger: 'blur' }], }, { @@ -131,7 +140,8 @@ export const option = { type: 'textarea', minRows: 3, span: 24, - hide: true, + minWidth: 180, + overHidden: true, rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }], }, { diff --git a/src/option/vehicle/annual-inspection-record.js b/src/option/vehicle/annual-inspection-record.js index 96144d8..f94bdf2 100644 --- a/src/option/vehicle/annual-inspection-record.js +++ b/src/option/vehicle/annual-inspection-record.js @@ -35,6 +35,10 @@ export const option = { dialogWidth: 1050, labelPosition: 'top', tip: false, + searchBtnText: '查询', + emptyBtnText: '重置', + saveBtnText: '提交', + updateBtnText: '提交', searchShow: true, searchMenuSpan: 6, border: true, @@ -105,7 +109,6 @@ export const option = { label: '客车类型及等级', prop: 'passengerTypeLevel', minWidth: 150, - hide: true, rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }], }, { @@ -119,6 +122,7 @@ export const option = { prop: 'fee', type: 'number', precision: 2, + append: '元', minWidth: 100, rules: [ { required: true, message: '请输入费用', trigger: 'blur' }, @@ -129,7 +133,6 @@ export const option = { label: '评定(复核)单位', prop: 'assessmentUnit', minWidth: 160, - hide: true, rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }], }, { @@ -138,7 +141,8 @@ export const option = { type: 'textarea', minRows: 3, span: 24, - hide: true, + minWidth: 180, + overHidden: true, rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }], }, { diff --git a/src/option/vehicle/insurance-record.js b/src/option/vehicle/insurance-record.js index 9aaf874..ff8b9cf 100644 --- a/src/option/vehicle/insurance-record.js +++ b/src/option/vehicle/insurance-record.js @@ -30,11 +30,16 @@ export const ocrTemplateDic = [ ]; export const option = { + title: '保险记录(OCR)', height: 'auto', calcHeight: 32, dialogWidth: 1100, labelPosition: 'top', tip: false, + searchBtnText: '查询', + emptyBtnText: '重置', + saveBtnText: '提交', + updateBtnText: '提交', searchShow: true, searchMenuSpan: 6, border: true, @@ -123,6 +128,7 @@ export const option = { prop: 'insuredAmount', type: 'number', precision: 2, + append: '元', minWidth: 120, rules: [{ validator: validateNonNegative, trigger: 'blur' }], }, @@ -131,6 +137,7 @@ export const option = { prop: 'premium', type: 'number', precision: 2, + append: '元', minWidth: 120, rules: [ { required: true, message: '请输入保费', trigger: 'blur' }, diff --git a/src/option/vehicle/mileage-record.js b/src/option/vehicle/mileage-record.js index 62535a8..15de4f1 100644 --- a/src/option/vehicle/mileage-record.js +++ b/src/option/vehicle/mileage-record.js @@ -13,11 +13,16 @@ const validateNonNegative = (rule, value, callback) => { }; export const option = { + title: '里程记录(无船)', height: 'auto', calcHeight: 32, dialogWidth: 980, labelPosition: 'top', tip: false, + searchBtnText: '查询', + emptyBtnText: '重置', + saveBtnText: '提交', + updateBtnText: '提交', searchShow: true, searchMenuSpan: 6, border: true, @@ -57,7 +62,7 @@ export const option = { rules: [{ validator: validateNonNegative, trigger: 'blur' }], }, { - label: '本月行驶里程(km)', + label: '本月行驶里程(km)', prop: 'monthlyMileage', type: 'number', precision: 2, @@ -65,7 +70,7 @@ export const option = { rules: [{ validator: validateNonNegative, trigger: 'blur' }], }, { - label: '累计行驶里程(km)', + label: '累计行驶里程(km)', prop: 'totalMileage', type: 'number', precision: 2, diff --git a/src/option/vehicle/tire-replacement-record.js b/src/option/vehicle/tire-replacement-record.js index 5127cbb..352a890 100644 --- a/src/option/vehicle/tire-replacement-record.js +++ b/src/option/vehicle/tire-replacement-record.js @@ -21,11 +21,16 @@ const validatePositiveInteger = (rule, value, callback) => { }; export const option = { + title: '换胎记录(无船)', height: 'auto', calcHeight: 32, dialogWidth: 980, labelPosition: 'top', tip: false, + searchBtnText: '查询', + emptyBtnText: '重置', + saveBtnText: '提交', + updateBtnText: '提交', searchShow: true, searchMenuSpan: 6, border: true, @@ -81,6 +86,7 @@ export const option = { prop: 'replacementCost', type: 'number', precision: 2, + append: '元', minWidth: 120, rules: [ { required: true, message: '请输入换胎费用', trigger: 'blur' }, diff --git a/src/option/vehicle/violation-record.js b/src/option/vehicle/violation-record.js index 96fe013..6972aec 100644 --- a/src/option/vehicle/violation-record.js +++ b/src/option/vehicle/violation-record.js @@ -44,6 +44,10 @@ export const option = { dialogWidth: 1100, labelPosition: 'top', tip: false, + searchBtnText: '查询', + emptyBtnText: '重置', + saveBtnText: '提交', + updateBtnText: '提交', searchShow: true, searchMenuSpan: 6, border: true, @@ -129,6 +133,7 @@ export const option = { prop: 'fineAmount', type: 'number', precision: 2, + append: '元', minWidth: 100, rules: [{ validator: validateNonNegative, trigger: 'blur' }], }, @@ -137,14 +142,13 @@ export const option = { prop: 'deductPoints', type: 'number', precision: 0, - hide: true, + minWidth: 110, rules: [{ validator: validateDeductPoints, trigger: 'blur' }], }, { label: '被罚单位', prop: 'penaltyUnit', minWidth: 150, - hide: true, rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }], }, { @@ -152,8 +156,9 @@ export const option = { prop: 'processStatus', type: 'radio', dicData: processStatusDic, - value: '已处理', + value: '未处理', search: true, + clearable: true, minWidth: 120, rules: [{ required: true, message: '请选择状态', trigger: 'change' }], }, @@ -163,7 +168,8 @@ export const option = { type: 'textarea', minRows: 3, span: 24, - hide: true, + minWidth: 220, + overHidden: true, rules: [ { required: true, message: '请输入过程描述', trigger: 'blur' }, { max: 500, message: '最多 500 个字符', trigger: 'blur' }, @@ -175,7 +181,8 @@ export const option = { type: 'textarea', minRows: 3, span: 24, - hide: true, + minWidth: 220, + overHidden: true, rules: [{ max: 500, message: '最多 500 个字符', trigger: 'blur' }], }, { diff --git a/src/views/base/airport-master.vue b/src/views/base/airport-master.vue index d4c4dba..49b9ccf 100644 --- a/src/views/base/airport-master.vue +++ b/src/views/base/airport-master.vue @@ -109,6 +109,8 @@ import { getToken } from '@/utils/auth'; import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; +const DEFAULT_COUNTRY_CODE = '+86'; + export default { data() { const validateIataCode = (rule, value, callback) => { @@ -165,6 +167,8 @@ export default { dialogWidth: 980, tip: false, searchShow: true, + labelWidth: 120, + searchLabelWidth: 120, searchMenuSpan: 6, border: true, index: true, @@ -266,11 +270,35 @@ export default { addDisplay: false, editDisplay: false, }, + { + label: '行政区划', + prop: 'regionName', + search: true, + minWidth: 160, + overHidden: false, + maxlength: 128, + showWordLimit: true, + rules: [{ max: 128, message: '行政区划不能超过128字', trigger: 'blur' }], + }, + { + label: '详细地址', + prop: 'detailAddress', + type: 'textarea', + minRows: 3, + span: 24, + minWidth: 220, + overHidden: false, + maxlength: 255, + showWordLimit: true, + rules: [{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' }], + }, { label: '经度', prop: 'longitude', type: 'number', precision: 6, + minWidth: 130, + overHidden: false, rules: [{ validator: validateLongitude, trigger: 'blur' }], }, { @@ -278,6 +306,8 @@ export default { prop: 'latitude', type: 'number', precision: 6, + minWidth: 130, + overHidden: false, rules: [{ validator: validateLatitude, trigger: 'blur' }], }, { @@ -285,6 +315,8 @@ export default { prop: 'dataSource', type: 'select', search: true, + minWidth: 120, + overHidden: false, dicData: [ { label: '全部', value: '' }, { label: '初始导入', value: '初始导入' }, @@ -299,6 +331,8 @@ export default { search: true, slot: true, dataType: 'number', + minWidth: 110, + overHidden: false, dicData: [ { label: '全部', value: '' }, { label: '启用', value: 1 }, @@ -406,7 +440,7 @@ export default { return this.option.column.find(item => item.prop === prop); }, loadProvinceOptions() { - getLazyTree('00').then(res => { + getLazyTree(DEFAULT_COUNTRY_CODE).then(res => { this.provinceOptions = res.data.data; this.findColumn('provinceCode').dicData = this.provinceOptions; }); @@ -448,6 +482,8 @@ export default { }, normalizeRow(row) { row.code = row.iataCode ? `JC-${row.iataCode}` : row.code; + row.regionName = String(row.regionName || '').trim(); + row.detailAddress = String(row.detailAddress || '').trim(); if (!row.dataSource) row.dataSource = '手动录入'; if (!row.status) row.status = 1; return row; diff --git a/src/views/base/currency.vue b/src/views/base/currency.vue index 72a7e47..76fcf0d 100644 --- a/src/views/base/currency.vue +++ b/src/views/base/currency.vue @@ -189,8 +189,7 @@ export default { prop: 'name', minWidth: 150, search: true, - disabled: true, - rules: [{ required: true, message: '请选择币种编码带出币种名称', trigger: 'blur' }], + rules: [{ required: true, message: '请输入币种名称', trigger: 'blur' }], }, { label: '汇率', diff --git a/src/views/base/customer-type.vue b/src/views/base/customer-type.vue index 30eba90..289c163 100644 --- a/src/views/base/customer-type.vue +++ b/src/views/base/customer-type.vue @@ -56,6 +56,12 @@ + @@ -72,6 +78,7 @@ export default { data: [], page: { pageSize: 10, + pageSizes: [10, 20, 50, 100], currentPage: 1, total: 0, }, @@ -81,6 +88,10 @@ export default { calcHeight: 32, dialogWidth: 680, tip: false, + searchBtnText: '查询', + emptyBtnText: '重置', + saveBtnText: '提交', + updateBtnText: '提交', searchShow: true, searchMenuSpan: 6, border: true, @@ -119,6 +130,7 @@ export default { { label: '启用', value: 1 }, { label: '停用', value: 2 }, ], + clearable: true, value: 1, }, { @@ -196,7 +208,7 @@ export default { ); }, rowDel(row) { - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除该条数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', @@ -212,7 +224,7 @@ export default { this.$message.warning('请选择至少一条数据'); return; } - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除所选数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', diff --git a/src/views/base/port-terminal.vue b/src/views/base/port-terminal.vue index 4476cd9..ee3a483 100644 --- a/src/views/base/port-terminal.vue +++ b/src/views/base/port-terminal.vue @@ -65,6 +65,24 @@ {{ row.status === 1 ? '启用' : '停用' }} + + + + + @@ -133,6 +152,8 @@ import { getToken } from '@/utils/auth'; import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; +const DEFAULT_COUNTRY_CODE = '+86'; + export default { data() { const validateCode = (rule, value, callback) => { @@ -183,6 +204,8 @@ export default { excelBox: false, excelForm: {}, portOptions: [], + countryOptions: [], + regionCascaderKey: 0, regionNodeMap: {}, regionNameMap: {}, regionProps: { @@ -206,6 +229,7 @@ export default { dialogWidth: 980, tip: false, searchShow: true, + searchLabelWidth: 120, searchMenuSpan: 6, border: true, index: true, @@ -279,6 +303,15 @@ export default { slot: true, disabled: true, }, + { + label: '国家', + prop: 'country', + search: true, + formslot: true, + span: 12, + minWidth: 100, + rules: [{ required: true, message: '请选择国家', trigger: 'change' }], + }, { label: '城市', prop: 'regionCode', @@ -287,14 +320,6 @@ export default { span: 12, rules: [{ required: true, message: '请选择所属城市', trigger: 'change' }], }, - { - label: '国家', - prop: 'country', - search: true, - addDisplay: false, - editDisplay: false, - minWidth: 100, - }, { label: '城市', prop: 'city', @@ -309,6 +334,7 @@ export default { type: 'number', precision: 6, minWidth: 130, + overHidden: false, rules: [{ validator: validateLongitude, trigger: 'blur' }], }, { @@ -317,6 +343,7 @@ export default { type: 'number', precision: 6, minWidth: 130, + overHidden: false, rules: [{ validator: validateLatitude, trigger: 'blur' }], }, { @@ -324,6 +351,8 @@ export default { prop: 'dataSource', type: 'select', search: true, + minWidth: 120, + overHidden: false, dicData: [ { label: '全部', value: '' }, { label: '初始化导入', value: '初始化导入' }, @@ -338,6 +367,8 @@ export default { search: true, slot: true, dataType: 'number', + minWidth: 110, + overHidden: false, dicData: [ { label: '全部', value: '' }, { label: '启用', value: 1 }, @@ -423,11 +454,16 @@ export default { }, isAdmin() { const authority = this.userInfo && this.userInfo.authority; - return Array.isArray(authority) ? authority.includes('admin') : String(authority || '').includes('admin'); + return Array.isArray(authority) + ? authority.includes('admin') + : String(authority || '').includes('admin'); }, isParentRegionLocked() { return this.form.category === '码头' && !!this.form.parentId; }, + isRegionDisabled() { + return this.isParentRegionLocked || !this.form.countryCode; + }, ids() { let ids = []; this.selectionList.forEach(ele => { @@ -437,6 +473,7 @@ export default { }, }, created() { + this.loadCountryOptions(); this.loadPortOptions(); }, methods: { @@ -454,7 +491,11 @@ export default { } }, loadRegionNode(node, resolve) { - const parentCode = node.level === 0 ? '00' : node.value; + const parentCode = node.level === 0 ? this.form.countryCode : node.value; + if (!parentCode) { + resolve([]); + return; + } getLazyTree(parentCode).then(res => { const regionList = res.data.data || []; const nodes = regionList.map(region => { @@ -469,6 +510,15 @@ export default { resolve(nodes); }); }, + loadCountryOptions() { + return getLazyTree('0').then(res => { + this.countryOptions = res.data.data || []; + this.countryOptions.forEach(country => { + this.regionNodeMap[country.id] = country; + this.regionNameMap[country.title] = country.id; + }); + }); + }, loadPortOptions() { getPortSelect().then(res => { this.portOptions = res.data.data; @@ -485,6 +535,19 @@ export default { } this.handleCodeChange(this.form.code); }, + handleCountryChange(value) { + const country = this.countryOptions.find(item => item.id === value); + this.form.country = country ? country.title : ''; + this.form.regionCode = []; + this.form.city = ''; + this.regionCascaderKey += 1; + }, + syncCountryName(countryCode) { + const country = this.countryOptions.find(item => item.id === countryCode); + if (country) { + this.form.country = country.title; + } + }, handleParentChange(value) { const parent = this.portOptions.find(item => item.id === value); if (!parent) return; @@ -499,43 +562,68 @@ export default { } this.form.country = parent.country; this.form.city = parent.city; - this.resolveRegionCode(parent.country, parent.city).then(regionCode => { - this.form.regionCode = regionCode; - }); + this.resolveCountryCode(parent.country) + .then(countryCode => { + this.form.countryCode = countryCode; + this.syncCountryName(countryCode); + this.regionCascaderKey += 1; + return this.resolveRegionCode(parent.country, parent.city); + }) + .then(regionCode => { + this.form.regionCode = regionCode; + }); }, handleRegionChange(value) { const regionCode = Array.isArray(value) ? value : []; const provinceCode = regionCode[0]; const cityCode = regionCode[1]; - const province = this.regionNodeMap[provinceCode]; const city = this.regionNodeMap[cityCode]; - this.form.country = province ? province.title : ''; this.form.city = city ? city.title : ''; }, + resolveCountryCode(country) { + if (!country) { + return Promise.resolve(''); + } + const countryOption = this.countryOptions.find( + item => item.title === country || item.id === country + ); + if (countryOption) { + return Promise.resolve(countryOption.id); + } + return this.loadCountryOptions().then(() => { + const option = this.countryOptions.find( + item => item.title === country || item.id === country + ); + return option ? option.id : DEFAULT_COUNTRY_CODE; + }); + }, resolveRegionCode(country, city) { if (!country || !city) { return Promise.resolve([]); } - const provinceCode = this.regionNameMap[country]; - if (provinceCode) { - const cityCode = this.regionNameMap[city]; - if (cityCode) { - return Promise.resolve([provinceCode, cityCode]); - } - return this.loadRegionList(provinceCode).then(cityList => { - const cityNode = cityList.find(item => item.title === city); - return cityNode ? [provinceCode, cityNode.id] : [provinceCode]; - }); + const countryCode = this.form.countryCode || this.regionNameMap[country]; + if (!countryCode) { + return Promise.resolve([]); } - return this.loadRegionList('00').then(provinceList => { - const province = provinceList.find(item => item.title === country); - if (!province) { - return []; + return this.loadRegionList(countryCode).then(provinceList => { + const province = provinceList.find(item => item.title === country || item.title === city); + if (province) { + return this.loadRegionList(province.id).then(cityList => { + const cityNode = cityList.find(item => item.title === city); + return cityNode ? [province.id, cityNode.id] : [province.id]; + }); } - return this.loadRegionList(province.id).then(cityList => { - const cityNode = cityList.find(item => item.title === city); - return cityNode ? [province.id, cityNode.id] : [province.id]; - }); + return provinceList.reduce((promise, item) => { + return promise.then(result => { + if (result.length > 0) { + return result; + } + return this.loadRegionList(item.id).then(cityList => { + const cityNode = cityList.find(child => child.title === city); + return cityNode ? [item.id, cityNode.id] : []; + }); + }); + }, Promise.resolve([])); }); }, loadRegionList(parentCode) { @@ -682,18 +770,32 @@ export default { if (['edit', 'view'].includes(type)) { getDetail(this.form.id).then(res => { this.form = res.data.data; - this.resolveRegionCode(this.form.country, this.form.city).then(regionCode => { - this.form.regionCode = regionCode; - this.handleCategoryChange(this.form.category); - done(); - }); + this.resolveCountryCode(this.form.country) + .then(countryCode => { + this.form.countryCode = countryCode; + this.syncCountryName(countryCode); + this.regionCascaderKey += 1; + return this.resolveRegionCode(this.form.country, this.form.city); + }) + .then(regionCode => { + this.form.regionCode = regionCode; + this.handleCategoryChange(this.form.category); + done(); + }); }); return; } else { - this.resolveRegionCode(this.form.country, this.form.city).then(regionCode => { - this.form.regionCode = regionCode; - done(); - }); + this.resolveCountryCode(this.form.country) + .then(countryCode => { + this.form.countryCode = countryCode; + this.syncCountryName(countryCode); + this.regionCascaderKey += 1; + return this.resolveRegionCode(this.form.country, this.form.city); + }) + .then(regionCode => { + this.form.regionCode = regionCode; + done(); + }); return; } }, diff --git a/src/views/base/railway-station.vue b/src/views/base/railway-station.vue index cf8e20b..0e3b9d1 100644 --- a/src/views/base/railway-station.vue +++ b/src/views/base/railway-station.vue @@ -109,6 +109,8 @@ import { getToken } from '@/utils/auth'; import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; +const DEFAULT_COUNTRY_CODE = '+86'; + export default { data() { const validateTmisCode = (rule, value, callback) => { @@ -152,6 +154,7 @@ export default { excelForm: {}, provinceOptions: [], cityOptions: [], + regionInitializing: false, page: { pageSize: 10, pageSizes: [10, 20, 50, 100], @@ -180,6 +183,8 @@ export default { { label: '编码', prop: 'code', + minWidth: 120, + overHidden: false, search: true, addDisabled: true, editDisabled: true, @@ -216,11 +221,11 @@ export default { search: true, rules: [{ required: true, message: '请输入车站名称', trigger: 'blur' }], }, - { - label: '所属铁路线路', - prop: 'railwayLine', - minWidth: 130, - }, + // { + // label: '所属铁路线路', + // prop: 'railwayLine', + // minWidth: 130, + // }, { label: '所属省份', prop: 'provinceCode', @@ -265,11 +270,35 @@ export default { addDisplay: false, editDisplay: false, }, + { + label: '行政区划', + prop: 'regionName', + search: true, + minWidth: 160, + overHidden: false, + maxlength: 128, + showWordLimit: true, + rules: [{ max: 128, message: '行政区划不能超过128字', trigger: 'blur' }], + }, + { + label: '详细地址', + prop: 'detailAddress', + type: 'textarea', + minRows: 3, + span: 24, + minWidth: 220, + overHidden: false, + maxlength: 255, + showWordLimit: true, + rules: [{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' }], + }, { label: '经度', prop: 'longitude', type: 'number', precision: 6, + minWidth: 130, + overHidden: false, rules: [{ validator: validateLongitude, trigger: 'blur' }], }, { @@ -277,6 +306,8 @@ export default { prop: 'latitude', type: 'number', precision: 6, + minWidth: 130, + overHidden: false, rules: [{ validator: validateLatitude, trigger: 'blur' }], }, { @@ -284,6 +315,8 @@ export default { prop: 'dataSource', type: 'select', search: true, + minWidth: 120, + overHidden: false, dicData: [ { label: '全部', value: '' }, { label: '初始化导入', value: '初始化导入' }, @@ -298,6 +331,8 @@ export default { search: true, slot: true, dataType: 'number', + minWidth: 110, + overHidden: false, dicData: [ { label: '全部', value: '' }, { label: '启用', value: 1 }, @@ -405,8 +440,8 @@ export default { return this.option.column.find(item => item.prop === prop); }, loadProvinceOptions() { - getLazyTree('00').then(res => { - this.provinceOptions = res.data.data; + getLazyTree(DEFAULT_COUNTRY_CODE).then(res => { + this.provinceOptions = (res.data.data || []).map(this.normalizeRegionOption); this.findColumn('provinceCode').dicData = this.provinceOptions; }); }, @@ -417,10 +452,40 @@ export default { return Promise.resolve(); } return getLazyTree(provinceCode).then(res => { - this.cityOptions = res.data.data; + this.cityOptions = (res.data.data || []).map(this.normalizeRegionOption); this.findColumn('cityCode').dicData = this.cityOptions; }); }, + normalizeRegionOption(region) { + return { + ...region, + id: region.id === undefined || region.id === null ? region.id : String(region.id), + }; + }, + normalizeRegionForm(row) { + return { + ...row, + provinceCode: + row.provinceCode === undefined || row.provinceCode === null + ? row.provinceCode + : String(row.provinceCode), + cityCode: + row.cityCode === undefined || row.cityCode === null ? row.cityCode : String(row.cityCode), + }; + }, + ensureCityOption(row) { + if (!row.cityCode || !row.cityName) return; + const exists = this.cityOptions.some(item => item.id === row.cityCode); + if (exists) return; + this.cityOptions = [ + ...this.cityOptions, + { + id: row.cityCode, + title: row.cityName, + }, + ]; + this.findColumn('cityCode').dicData = this.cityOptions; + }, handleTmisChange(value) { this.form.tmisCode = String(value || '') .replace(/\D/g, '') @@ -436,6 +501,10 @@ export default { handleProvinceChange(value) { const province = this.provinceOptions.find(item => item.id === value); this.form.provinceName = province ? province.title : ''; + if (this.regionInitializing) { + this.loadCityOptions(value).then(() => this.ensureCityOption(this.form)); + return; + } this.form.cityCode = undefined; this.form.cityName = ''; this.loadCityOptions(value); @@ -446,6 +515,8 @@ export default { }, normalizeRow(row) { row.code = row.tmisCode ? `TL${row.tmisCode}` : row.code; + row.regionName = String(row.regionName || '').trim(); + row.detailAddress = String(row.detailAddress || '').trim(); if (!row.dataSource) row.dataSource = '手动'; if (!row.status) row.status = 1; return row; @@ -546,9 +617,19 @@ export default { }, beforeOpen(done, type) { if (['edit', 'view'].includes(type)) { + this.regionInitializing = true; getDetail(this.form.id).then(res => { - this.form = res.data.data; - this.loadCityOptions(this.form.provinceCode).then(() => done()); + const detail = this.normalizeRegionForm(res.data.data || {}); + this.loadCityOptions(detail.provinceCode).then(() => { + this.ensureCityOption(detail); + this.form = detail; + done(); + this.$nextTick(() => { + window.setTimeout(() => { + this.regionInitializing = false; + }, 0); + }); + }); }); return; } diff --git a/src/views/base/region.vue b/src/views/base/region.vue index 60541b2..b105a94 100644 --- a/src/views/base/region.vue +++ b/src/views/base/region.vue @@ -12,6 +12,13 @@ + 新增国家 + 同步 - 调试 - + + + + + + + - + + + {{ regionForm.parentCode }} @@ -92,10 +109,12 @@ import { getToken } from '@/utils/auth'; import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; +const ROOT_PARENT_CODE = '0'; + export default { data() { return { - topCode: '00', + topCode: ROOT_PARENT_CODE, treeCode: '', treeParentCode: '', treeData: [], @@ -104,7 +123,7 @@ export default { nodeKey: 'id', lazy: true, treeLoad: function (node, resolve) { - const parentCode = node.level === 0 ? '00' : node.data.id; + const parentCode = node.level === 0 ? ROOT_PARENT_CODE : node.data.id; getLazyTree(parentCode).then(res => { resolve( res.data.data.map(item => { @@ -323,7 +342,11 @@ export default { }, watch: { 'regionForm.subCode'() { - this.regionForm.code = this.regionForm.parentCode + this.regionForm.subCode; + if (this.isCountryRegion) { + return; + } + const parentCode = this.isProvinceRegion ? '' : this.regionForm.parentCode; + this.regionForm.code = parentCode + this.regionForm.subCode; }, 'excelForm.isCovered'() { if (this.excelForm.isCovered !== '') { @@ -334,6 +357,12 @@ export default { }, computed: { ...mapGetters(['permission']), + isCountryRegion() { + return Number(this.regionForm.regionLevel) === 0 || this.regionForm.parentCode === this.topCode; + }, + isProvinceRegion() { + return Number(this.regionForm.regionLevel) === 1; + }, permissionList() { return { addBtn: this.validData(this.permission.region_add, false), @@ -369,9 +398,27 @@ export default { this.treeParentCode = data.parentId; getDetail(this.treeCode).then(res => { this.regionForm = res.data.data; - this.regionForm.subCode = this.regionForm.code.replace(this.regionForm.parentCode, ''); + this.regionForm.subCode = + this.regionForm.parentCode === this.topCode + ? this.regionForm.code + : this.regionForm.code.replace(this.regionForm.parentCode, ''); }); }, + addCountry() { + const column = this.findColumn(this.regionOption.column, 'parentCode'); + column.disabled = true; + this.treeCode = ''; + this.treeParentCode = this.topCode; + this.regionForm = { + parentCode: this.topCode, + parentName: '根节点', + code: '', + subCode: '', + name: '', + regionLevel: 0, + sort: 1, + }; + }, addChildren() { if (validatenull(this.regionForm.code) || validatenull(this.regionForm.name)) { this.$message.warning('请先选择一项区划'); @@ -386,8 +433,14 @@ export default { this.regionForm.regionLevel === 5 ? 5 : this.regionForm.regionLevel + 1; }, handleSubmit(form, done) { - const parentCode = form.parentCode === this.topCode ? '' : form.parentCode; - form.code = parentCode + form.subCode; + if (Number(form.regionLevel) === 0) { + form.parentCode = this.topCode; + form.ancestors = this.topCode; + form.subCode = form.code; + } else { + const parentCode = Number(form.regionLevel) === 1 ? '' : form.parentCode; + form.code = parentCode + form.subCode; + } submit(form).then( () => { this.$message({ diff --git a/src/views/vehicle/accident-record.vue b/src/views/vehicle/accident-record.vue index ab7207a..5a889a0 100644 --- a/src/views/vehicle/accident-record.vue +++ b/src/views/vehicle/accident-record.vue @@ -73,6 +73,12 @@ {{ formatAttachments(row.attachments) }} + @@ -235,6 +241,14 @@ export default { this.$message.warning('保险理赔金额不能小于 0'); return false; } + if (!row.accidentNature) { + this.$message.warning('请选择事故性质'); + return false; + } + if (!row.accidentResponsibility) { + this.$message.warning('请选择事故责任'); + return false; + } if ( row.directEconomicLoss !== undefined && row.directEconomicLoss !== null && @@ -286,7 +300,7 @@ export default { ); }, rowDel(row) { - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除该条数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', @@ -302,7 +316,7 @@ export default { this.$message.warning('请选择至少一条数据'); return; } - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除所选数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', diff --git a/src/views/vehicle/annual-inspection-record.vue b/src/views/vehicle/annual-inspection-record.vue index cf26a86..6496bbc 100644 --- a/src/views/vehicle/annual-inspection-record.vue +++ b/src/views/vehicle/annual-inspection-record.vue @@ -73,6 +73,12 @@ {{ formatAttachments(row.attachments) }} + @@ -297,7 +303,7 @@ export default { ); }, rowDel(row) { - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除该条数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', @@ -313,7 +319,7 @@ export default { this.$message.warning('请选择至少一条数据'); return; } - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除所选数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', diff --git a/src/views/vehicle/customer-archive.vue b/src/views/vehicle/customer-archive.vue index 2d515f8..a080237 100644 --- a/src/views/vehicle/customer-archive.vue +++ b/src/views/vehicle/customer-archive.vue @@ -131,6 +131,12 @@ + 基本信息 + @@ -196,10 +209,9 @@ - - - - + + + @@ -215,7 +227,7 @@ - + - - - + @@ -330,20 +343,66 @@ 资质附件 - - 添加附件 - + + OCR上传识别 + 添加附件 + 批量下载 + + + - + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + @@ -358,7 +417,7 @@ - 读取评分量化表 + 添加评分记录 @@ -376,6 +435,11 @@ + + + + + @@ -431,6 +495,8 @@ {{ readonly ? '关闭' : '取消' }} 保存 + 提交 + 返回 @@ -566,6 +632,13 @@ export default { callback(); } }; + const validateBusinessEndDate = (rule, value, callback) => { + if (this.archiveForm.businessTermType === '固定期限' && !value) { + callback(new Error('请选择营业期限截止日')); + } else { + callback(); + } + }; return { form: {}, query: {}, @@ -585,11 +658,11 @@ export default { archiveForm: this.emptyArchive(), currentScore: { details: [] }, qualificationFiles: [], + deptTree: [], deptOptions: [], customerTypeOptions: [ { label: '客户', value: '客户' }, - { label: '供应商', value: '供应商' }, - { label: '客户/供应商', value: '客户/供应商' }, + { label: '承运商', value: '承运商' }, ], accessTypeMap: { temporary: '临时客商', @@ -648,7 +721,9 @@ export default { trigger: 'blur', }, ], - customerType: [{ required: true, message: '请选择客商类型', trigger: 'change' }], + customerType: [ + { type: 'array', required: true, message: '请选择客商类型', trigger: 'change' }, + ], legalPerson: [{ required: true, message: '请输入法人/负责人', trigger: 'blur' }], contactPhone: [ { required: true, message: '请输入联系电话', trigger: 'blur' }, @@ -659,6 +734,7 @@ export default { registeredCapital: [{ validator: validateNonNegative, trigger: 'blur' }], maxCreditLimit: [{ validator: validateNonNegative, trigger: 'blur' }], applyCreditLimit: [{ validator: validateNonNegative, trigger: 'blur' }], + businessEndDate: [{ validator: validateBusinessEndDate, trigger: 'change' }], registeredAddress: [{ max: 200, message: '地址最多200个字符', trigger: 'blur' }], businessScope: [{ max: 500, message: '经营范围最多500个字符', trigger: 'blur' }], remark: [{ max: 500, message: '备注最多500个字符', trigger: 'blur' }], @@ -667,6 +743,8 @@ export default { height: 'auto', calcHeight: 32, tip: false, + searchBtnText: '查询', + emptyBtnText: '重置', searchShow: true, searchMenuSpan: 6, border: true, @@ -691,9 +769,8 @@ export default { search: true, minWidth: 120, dicData: [ - { label: '企业', value: '企业' }, - { label: '个人', value: '个人' }, - { label: '事业单位', value: '事业单位' }, + { label: '有限公司', value: '有限公司' }, + { label: '个体工商户', value: '个体工商户' }, ], }, { label: '统一信用代码', prop: 'unifiedCreditCode', search: true, minWidth: 190 }, @@ -746,6 +823,7 @@ export default { { label: '启用', value: 1 }, { label: '停用', value: 2 }, ], + clearable: true, }, { label: '创建开始时间', @@ -797,6 +875,14 @@ export default { if (this.readonly) return '查看客商档案'; return this.archiveForm.id ? '编辑客商档案' : '新增客商档案'; }, + missingQualificationText() { + if (this.archiveForm.accessType !== 'formal') return ''; + const types = this.qualificationFiles.map(item => item.type || item.name).filter(Boolean); + const missing = ['公司营业执照', '道路运输证'].filter( + item => !types.some(type => String(type).includes(item)) + ); + return missing.length ? `缺失核心资质材料:${missing.join('、')}` : ''; + }, }, methods: { hasPermission(code) { @@ -807,6 +893,7 @@ export default { accessType: 'temporary', approvalStatus: 'draft', status: 1, + customerType: [], contacts: [], receiptAccounts: [], invoices: [], @@ -845,7 +932,8 @@ export default { }, initDeptTree() { getDeptTree(this.userInfo.tenantId).then(res => { - this.deptOptions = this.flattenDept(res.data.data || []); + this.deptTree = res.data.data || []; + this.deptOptions = this.flattenDept(this.deptTree); }); }, initCustomerTypes() { @@ -856,6 +944,11 @@ export default { label: item.name, value: item.name, })); + ['客户', '承运商'].forEach(type => { + if (!this.customerTypeOptions.some(item => item.value === type)) { + this.customerTypeOptions.push({ label: type, value: type }); + } + }); } }); }, @@ -894,19 +987,47 @@ export default { stringifyAttachments(list) { const files = (list || []) .map(item => ({ + type: String(item.type || '').trim(), name: String(item.name || '').trim(), + description: String(item.description || '').trim(), + size: String(item.size || '').trim(), + uploadUserName: String(item.uploadUserName || '').trim(), + uploadTime: String(item.uploadTime || '').trim(), url: String(item.url || '').trim(), })) - .filter(item => item.name || item.url); + .filter(item => item.type || item.name || item.url); return files.length ? JSON.stringify(files) : ''; }, addAttachment() { - this.qualificationFiles.push({ name: '', url: '' }); + this.qualificationFiles.push({ + type: '', + name: '', + description: '', + size: '', + uploadUserName: this.userInfo.realName || this.userInfo.userName || '', + uploadTime: this.$dayjs().format('YYYY-MM-DD HH:mm:ss'), + url: '', + }); + }, + downloadAttachments() { + const files = this.qualificationFiles.filter(item => item.url); + if (!files.length) { + this.$message.warning('暂无可下载附件'); + return; + } + files.forEach(item => window.open(item.url, '_blank')); }, normalizeDetail(detail) { + const customerType = detail.customerType + ? String(detail.customerType) + .split(',') + .map(item => item.trim()) + .filter(Boolean) + : []; return { ...this.emptyArchive(), ...detail, + customerType, contacts: detail.contacts || [], receiptAccounts: detail.receiptAccounts || [], invoices: detail.invoices || [], @@ -964,6 +1085,9 @@ export default { }, normalizeArchive() { const archive = JSON.parse(JSON.stringify(this.archiveForm)); + archive.customerType = Array.isArray(archive.customerType) + ? archive.customerType.join(',') + : archive.customerType; archive.qualificationAttachments = this.stringifyAttachments(this.qualificationFiles); archive.contacts = (archive.contacts || []).filter(item => item.contactName || item.contactPhone); archive.receiptAccounts = (archive.receiptAccounts || []).filter( @@ -986,6 +1110,31 @@ export default { }); }); }, + saveAndSubmitArchive() { + this.$refs.archiveForm.validate(valid => { + if (!valid) { + this.$message.warning('请先完整填写必填项'); + return; + } + const archive = this.normalizeArchive(); + if (!this.validateFormalForApproval(archive)) return; + submit(archive).then(res => { + const data = res.data.data; + const id = (data && typeof data === 'object' ? data.id : data) || this.archiveForm.id; + if (!id) { + this.archiveBox = false; + this.onLoad(this.page); + this.$message({ type: 'success', message: '保存成功,请在列表提交审批' }); + return; + } + submitApproval(id).then(() => { + this.archiveBox = false; + this.onLoad(this.page); + this.$message({ type: 'success', message: '提交成功!' }); + }); + }); + }); + }, addScore() { getScoreTemplate().then(res => { const score = this.normalizeScore(res.data.data); @@ -1228,6 +1377,17 @@ export default { margin: 18px 0 12px; } +.section-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + justify-content: flex-end; +} + +.archive-rule-alert { + margin-bottom: 12px; +} + .archive-tabs { margin-top: 18px; } diff --git a/src/views/vehicle/insurance-record.vue b/src/views/vehicle/insurance-record.vue index 7a58726..e5ff4a3 100644 --- a/src/views/vehicle/insurance-record.vue +++ b/src/views/vehicle/insurance-record.vue @@ -91,6 +91,12 @@ + @@ -265,7 +271,7 @@ export default { ); }, rowDel(row) { - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除该条数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', @@ -281,7 +287,7 @@ export default { this.$message.warning('请选择至少一条数据'); return; } - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除所选数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', diff --git a/src/views/vehicle/mileage-record.vue b/src/views/vehicle/mileage-record.vue index d2c114f..5a25790 100644 --- a/src/views/vehicle/mileage-record.vue +++ b/src/views/vehicle/mileage-record.vue @@ -74,6 +74,12 @@ {{ formatAttachments(row.attachments) }} + @@ -296,7 +302,7 @@ export default { ); }, rowDel(row) { - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除该条数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', @@ -312,7 +318,7 @@ export default { this.$message.warning('请选择至少一条数据'); return; } - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除所选数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', diff --git a/src/views/vehicle/tire-replacement-record.vue b/src/views/vehicle/tire-replacement-record.vue index 08ce24e..edf1dbe 100644 --- a/src/views/vehicle/tire-replacement-record.vue +++ b/src/views/vehicle/tire-replacement-record.vue @@ -73,6 +73,12 @@ {{ formatAttachments(row.attachments) }} + @@ -254,7 +260,7 @@ export default { ); }, rowDel(row) { - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除该条数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', @@ -270,7 +276,7 @@ export default { this.$message.warning('请选择至少一条数据'); return; } - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除所选数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', diff --git a/src/views/vehicle/violation-record.vue b/src/views/vehicle/violation-record.vue index ff951ff..1b6eee6 100644 --- a/src/views/vehicle/violation-record.vue +++ b/src/views/vehicle/violation-record.vue @@ -90,6 +90,12 @@ {{ formatAttachments(row.attachments) }} + @@ -170,7 +176,7 @@ export default { }, 'form.processStatus': { handler(value) { - this.updateProcessResultDisplay(value || '已处理'); + this.updateProcessResultDisplay(value || '未处理'); }, immediate: true, }, @@ -269,7 +275,7 @@ export default { normalizeRow(row) { const values = { ...row }; values.vehicleType = values.vehicleType || '车辆'; - values.processStatus = values.processStatus || '已处理'; + values.processStatus = values.processStatus || '未处理'; if (values.vehicleNo) { values.vehicleNo = values.vehicleNo.trim().toUpperCase(); } @@ -343,7 +349,7 @@ export default { ); }, rowDel(row) { - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除该条数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', @@ -359,7 +365,7 @@ export default { this.$message.warning('请选择至少一条数据'); return; } - this.$confirm('确定将选择数据删除?', { + this.$confirm('确定删除所选数据,删除后不可恢复?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', @@ -375,10 +381,10 @@ export default { if (type === 'add') { this.form = { vehicleType: '车辆', - processStatus: '已处理', + processStatus: '未处理', }; this.updateVehicleTypeDisplays('车辆'); - this.updateProcessResultDisplay('已处理'); + this.updateProcessResultDisplay('未处理'); } if (['edit', 'view'].includes(type)) { getDetail(this.form.id).then(res => {