1、新增结算模块

2、调整业务模块
3、调整客商模块
This commit is contained in:
2026-08-20 06:33:01 +08:00
parent 4199255186
commit fd5b1484e6
59 changed files with 11745 additions and 1104 deletions
@@ -0,0 +1,102 @@
<template>
<el-dialog v-model="visible" :title="readonly ? '计费方案详情' : index < 0 ? '添加计费方案' : '编辑计费方案'" width="92%" append-to-body destroy-on-close>
<el-form ref="formRef" :model="draft" :rules="formRules" label-position="right" label-width="auto">
<el-row :gutter="20">
<el-col :span="8"><el-form-item label="方案名称" prop="planName"><el-input v-model="draft.planName" maxlength="100" :disabled="readonly" /></el-form-item></el-col>
<el-col :span="8"><el-form-item label="默认方案"><el-checkbox v-model="draft.defaultPlan" :disabled="readonly">默认方案</el-checkbox></el-form-item></el-col>
<el-col :span="24"><el-form-item label="备注"><el-input v-model="draft.remark" maxlength="100" show-word-limit :disabled="readonly" /></el-form-item></el-col>
</el-row>
</el-form>
<div class="rule-head"><el-link v-if="!readonly" type="primary" @click="addRule">+添加规则</el-link></div>
<el-table :data="draft.rules" border>
<el-table-column type="index" label="序号" width="65" />
<el-table-column label="费用类型" width="180"><template #default="{ row }"><el-select v-model="row.feeType" clearable filterable :disabled="readonly" :loading="feeCategoryLoading" @change="value => handleFeeTypeChange(row, value)"><el-option v-for="item in feeCategories" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /></el-select></template></el-table-column>
<el-table-column label="费用项" width="210"><template #default="{ row }"><el-select v-model="row.feeItem" clearable filterable :disabled="readonly || !row.feeType" :loading="feeItemLoadingMap[feeTypeKey(row)]"><el-option v-for="item in feeItemOptions(row)" :key="item.id || item.name || item.englishName" :label="item.name || item.englishName" :value="item.name || item.englishName" /></el-select></template></el-table-column>
<el-table-column label="计费要素" width="170"><template #default="{ row }"><el-select v-model="row.billingElement" :disabled="readonly" placeholder="请选择" @change="handleElementChange(row)"><el-option v-for="item in billingElements" :key="item" :label="item" :value="item" /></el-select></template></el-table-column>
<el-table-column label="计费类型" width="180"><template #default="{ row }"><el-select v-model="row.billingType" :disabled="readonly || !row.billingElement" placeholder="请选择" @change="handleTypeChange(row)"><el-option v-for="item in billingTypes(row)" :key="item" :label="item" :value="item" /></el-select></template></el-table-column>
<el-table-column label="计费单位" width="150"><template #default="{ row }"><el-select v-model="row.billingUnit" clearable filterable :disabled="readonly" :loading="unitLoading"><el-option v-for="item in unitOptions" :key="item.id || item.dictKey || item.dictValue" :label="item.dictValue" :value="item.dictValue" /></el-select></template></el-table-column>
<el-table-column label="单价(元)" width="180"><template #default="{ row }"><div v-if="usesRangeUnitPrice(row)" class="limit-list"><el-input v-for="(item, rangeIndex) in getRanges(row)" :key="rangeIndex" v-model="item.unitPrice" :disabled="readonly" placeholder="请输入单价" @input="value => rangeUnitPriceInput(row, rangeIndex, value)" /></div><el-input v-else v-model="row.unitPrice" :disabled="readonly" @input="value => decimalInput(row, 'unitPrice', value)" /></template></el-table-column>
<el-table-column label="计费要素下限" width="250"><template #default="{ row }"><div class="limit-list"><el-input v-for="(item, rangeIndex) in getRanges(row)" :key="rangeIndex" v-model="item.lowerLimit" :disabled="!canEditLimit(row)" :placeholder="canEditLimit(row) ? '请输入下限' : '无需配置'" @input="value => limitInput(row, rangeIndex, 'lowerLimit', value)" /></div></template></el-table-column>
<el-table-column label="计费要素上限" width="270"><template #default="{ row }"><div class="limit-list"><div v-for="(item, rangeIndex) in getRanges(row)" :key="rangeIndex" class="limit-row"><el-input v-model="item.upperLimit" :disabled="!canEditLimit(row)" :placeholder="canEditLimit(row) ? '请输入上限' : '无需配置'" @input="value => limitInput(row, rangeIndex, 'upperLimit', value)" /><el-link v-if="canEditLimit(row)" type="primary" @click="addRange(row, rangeIndex)">添加</el-link><el-link v-if="canEditLimit(row) && getRanges(row).length > 1" type="danger" @click="removeRange(row, rangeIndex)">删除</el-link></div></div></template></el-table-column>
<el-table-column label="保底计费重量" width="240"><template #default="{ row }"><el-input v-model="row.minimumBillingWeight" :disabled="!canEditMinimum(row)" :placeholder="canEditMinimum(row) ? '实际计量值小于保底数值时按保底计费' : '仅按重量、按吨·公里可填写'" @input="value => decimalInput(row, 'minimumBillingWeight', value)" /></template></el-table-column>
<el-table-column label="备注" width="180"><template #default="{ row }"><el-input v-model="row.remark" maxlength="100" show-word-limit :disabled="readonly" /></template></el-table-column>
<el-table-column label="操作" width="250" fixed="right"><template #default="{ row, $index }"><el-link type="primary" @click="openMatch(row, $index)">{{ readonly ? '查看匹配条件' : '设置匹配条件' }}</el-link><el-link v-if="!readonly" type="primary" @click="copyRule(row)">复制</el-link><el-link v-if="!readonly" type="danger" @click="removeRule($index)">删除</el-link></template></el-table-column>
</el-table>
<template #footer><el-button v-if="readonly" type="primary" @click="visible = false">关闭</el-button><template v-else><el-button @click="visible = false">取消</el-button><el-button type="primary" @click="save">提交</el-button></template></template>
</el-dialog>
<el-dialog v-model="matchVisible" title="设置匹配条件" append-to-body width="720px">
<el-form :model="matchForm" label-position="right" label-width="90px">
<el-row :gutter="48">
<el-col :span="12"><el-form-item label="起运地"><el-cascader v-model="matchForm.originPath" :options="regionOptions" :props="regionProps" :placeholder="matchForm.origin || '请选择起运地'" :loading="regionLoading" :disabled="readonly" clearable filterable @visible-change="v => v && ensureRegions()" @change="v => matchRegionChange('origin', v)" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="目的地"><el-cascader v-model="matchForm.destinationPath" :options="regionOptions" :props="regionProps" :placeholder="matchForm.destination || '请选择目的地'" :loading="regionLoading" :disabled="readonly" clearable filterable @visible-change="v => v && ensureRegions()" @change="v => matchRegionChange('destination', v)" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="运输方式"><el-select v-model="matchForm.transportMode" :disabled="readonly" clearable><el-option label="公路" value="公路" /><el-option label="铁路" value="铁路" /><el-option label="水路" value="水路" /></el-select></el-form-item></el-col>
<el-col :span="12"><el-form-item label="货物类型"><el-cascader v-model="matchForm.cargoTypePath" :options="cargoOptions" :props="cargoProps" :placeholder="matchForm.cargoType || '请选择货物类型'" :loading="cargoLoading" :disabled="readonly" clearable filterable :filter-method="filterCargo" @visible-change="v => v && ensureCargo()" @change="matchCargoChange" /></el-form-item></el-col>
</el-row>
</el-form>
<template #footer><el-button v-if="readonly" type="primary" @click="matchVisible = false">关闭</el-button><template v-else><el-button @click="matchVisible = false">取消</el-button><el-button type="primary" @click="saveMatch">保存</el-button></template></template>
</el-dialog>
</template>
<script>
import { getList as getCargoTypeList } from '@/api/base/cargo-type';
import { getList as getFeeItemList } from '@/api/base/fee-item';
import { getLazyTree as getRegionLazyTree } from '@/api/base/region';
import { getDictionary } from '@/api/system/dictbiz';
const clone = value => JSON.parse(JSON.stringify(value));
const defaultRule = () => ({ feeType: '', feeItem: '', billingElement: '', billingType: '', billingUnit: '', unitPrice: '', lowerLimit: '', upperLimit: '', limitRanges: [{ lowerLimit: '', upperLimit: '', unitPrice: '' }], minimumBillingWeight: '', remark: '', matchCondition: { origin: '', originCode: '', originPath: [], destination: '', destinationCode: '', destinationPath: [], transportMode: '', cargoType: '', cargoTypeCode: '', cargoTypePath: [] } });
export default {
props: { modelValue: Boolean, value: { type: Object, default: () => ({}) }, index: { type: Number, default: -1 }, readonly: Boolean },
emits: ['update:modelValue', 'save'],
data() { return { draft: this.normalizePlan(this.value), feeCategories: [], feeCategoryLoading: false, feeItems: {}, feeItemLoadingMap: {}, unitOptions: [], unitLoading: false, billingElements: ['按重量', '按体积', '按车辆', '按里程', '按吨·公里', '固定金额(整单一口价)', '按数量'], typeMap: { 按重量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'], 按体积: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'], 按车辆: ['固定单价'], 按里程: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'], '按吨·公里': ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'], '固定金额(整单一口价)': ['固定一口价'], 按数量: ['固定单价', '区间单价', '阶梯单价', '区间阶梯一口价'] }, matchVisible: false, matchIndex: -1, matchForm: defaultRule().matchCondition, regionOptions: [], regionLoading: false, regionRequest: null, cargoOptions: [], cargoFlatOptions: [], cargoLoading: false, cargoRequest: null, formRules: { planName: [{ required: true, message: '请输入方案名称', trigger: 'blur' }] } }; },
computed: { visible: { get() { return this.modelValue; }, set(value) { this.$emit('update:modelValue', value); } }, regionProps() { return { label: 'title', value: 'id', children: 'children', leaf: 'leaf', emitPath: true }; }, cargoProps() { return { label: 'cargoName', value: 'id', children: 'children', disabled: (data, node) => node.level === 1, leaf: 'leaf', checkStrictly: true, emitPath: true }; } },
mounted() { this.loadDictionaries(); },
watch: { value: { deep: true, handler(value) { if (this.modelValue) { this.draft = this.normalizePlan(value); this.loadRuleItems(); } } } },
methods: {
normalizePlan(value = {}) { const plan = { planName: '', defaultPlan: false, remark: '', ...clone(value || {}) }; plan.rules = (value?.rules?.length ? value.rules : [defaultRule()]).map(rule => this.normalizeRule(rule)); return plan; },
normalizeRule(rule = {}) { const next = { ...defaultRule(), ...clone(rule) }; next.limitRanges = this.normalizeRanges(next); this.syncLegacyLimit(next); next.matchCondition = { ...defaultRule().matchCondition, ...(rule.matchCondition || {}) }; return next; },
normalizeRanges(row) { const ranges = Array.isArray(row.limitRanges) ? row.limitRanges.map(item => ({ lowerLimit: item?.lowerLimit ?? '', upperLimit: item?.upperLimit ?? '', unitPrice: item?.unitPrice === undefined || item?.unitPrice === '' ? row.unitPrice ?? '' : item.unitPrice })).filter(item => item.lowerLimit !== '' || item.upperLimit !== '' || item.unitPrice !== '') : []; if (!ranges.length && (row.lowerLimit !== '' || row.upperLimit !== '' || row.unitPrice !== '')) ranges.push({ lowerLimit: row.lowerLimit ?? '', upperLimit: row.upperLimit ?? '', unitPrice: row.unitPrice ?? '' }); return ranges.length ? ranges : [{ lowerLimit: '', upperLimit: '', unitPrice: '' }]; },
syncLegacyLimit(row) { const first = row.limitRanges?.[0] || {}; row.lowerLimit = first.lowerLimit || ''; row.upperLimit = first.upperLimit || ''; if (this.usesRangeUnitPrice(row)) row.unitPrice = first.unitPrice || ''; },
loadDictionaries() { this.feeCategoryLoading = true; getDictionary({ code: 'fee_category' }).then(res => { this.feeCategories = res.data?.data || []; this.loadRuleItems(); }).finally(() => { this.feeCategoryLoading = false; }); this.unitLoading = true; getDictionary({ code: 'unit_fee' }).then(res => { this.unitOptions = res.data?.data || []; }).finally(() => { this.unitLoading = false; }); },
feeTypeKey(row) { const option = this.feeCategories.find(item => String(item.dictKey) === String(row.feeType) || String(item.dictValue) === String(row.feeType)); return option?.dictKey || row.feeType || ''; },
feeItemOptions(row) { return this.feeItems[this.feeTypeKey(row)] || []; },
loadRuleItems() { (this.draft.rules || []).forEach(row => this.loadFeeItems(row.feeType)); },
loadFeeItems(value) { const key = this.feeTypeKey({ feeType: value }); if (!key || this.feeItems[key]) return; this.feeItemLoadingMap[key] = true; getFeeItemList(1, 9999, { feeCategory: key }).then(res => { const data = res?.data?.data || res?.data || {}; this.feeItems[key] = Array.isArray(data) ? data : (data.records || []); }).finally(() => { this.feeItemLoadingMap[key] = false; }); },
handleFeeTypeChange(row, value) { row.feeType = this.feeTypeKey({ feeType: value }); row.feeItem = ''; this.loadFeeItems(row.feeType); },
billingTypes(row) { return this.typeMap[row.billingElement] || []; },
handleElementChange(row) { if (!this.billingTypes(row).includes(row.billingType)) row.billingType = ''; if (!this.canEditMinimum(row)) row.minimumBillingWeight = ''; this.handleTypeChange(row); },
handleTypeChange(row) { if (!this.canEditLimit(row)) { row.limitRanges = [{ lowerLimit: '', upperLimit: '', unitPrice: '' }]; row.lowerLimit = ''; row.upperLimit = ''; } else { row.limitRanges = this.normalizeRanges(row); this.syncLegacyLimit(row); } },
canEditLimit(row) { return !this.readonly && Boolean(row.billingElement) && Boolean(row.billingType) && (row.billingType.includes('区间') || row.billingType.includes('阶梯')); },
usesRangeUnitPrice(row) { return ['区间单价', '阶梯单价'].includes(row.billingType); },
canEditMinimum(row) { return !this.readonly && ['按重量', '按吨·公里'].includes(row.billingElement); },
getRanges(row) { return Array.isArray(row.limitRanges) && row.limitRanges.length ? row.limitRanges : [{ lowerLimit: '', upperLimit: '', unitPrice: '' }]; },
addRange(row, index) { row.limitRanges = this.getRanges(row); row.limitRanges.splice(index + 1, 0, { lowerLimit: '', upperLimit: '', unitPrice: '' }); },
removeRange(row, index) { row.limitRanges = this.getRanges(row); row.limitRanges.splice(index, 1); if (!row.limitRanges.length) row.limitRanges.push({ lowerLimit: '', upperLimit: '', unitPrice: '' }); this.syncLegacyLimit(row); },
limitInput(row, index, prop, value) { row.limitRanges = this.getRanges(row); const text = String(value || '').replace(/[^\d.]/g, ''); const parts = text.split('.'); row.limitRanges[index][prop] = parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 2)}` : parts[0]; this.syncLegacyLimit(row); },
decimalInput(row, prop, value) { const text = String(value || '').replace(/[^\d.]/g, ''); const parts = text.split('.'); row[prop] = parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 2)}` : parts[0]; },
rangeUnitPriceInput(row, index, value) { row.limitRanges = this.getRanges(row); const target = { value }; this.decimalInput(target, 'value', value); row.limitRanges[index].unitPrice = target.value; if (index === 0) row.unitPrice = target.value; },
addRule() { this.draft.rules.push(defaultRule()); },
copyRule(row) { const next = clone(row); this.draft.rules.push(this.normalizeRule(next)); this.loadFeeItems(next.feeType); },
removeRule(index) { this.draft.rules.splice(index, 1); if (!this.draft.rules.length) this.addRule(); },
validateRules() { const groups = {}; const feeItemSet = new Set(); const required = [['feeType', '费用类型'], ['feeItem', '费用项'], ['billingElement', '计费要素'], ['billingType', '计费类型'], ['billingUnit', '计费单位']]; for (const [i, row] of this.draft.rules.entries()) { const empty = required.find(([key]) => row[key] === undefined || row[key] === null || String(row[key]).trim() === ''); if (empty) { this.$message.warning(`${i + 1}${empty[1]}不能为空`); return false; } const feeItem = String(row.feeItem).trim(); if (feeItemSet.has(feeItem)) { this.$message.warning(`费用项“${feeItem}”不能重复`); return false; } feeItemSet.add(feeItem); if (!this.usesRangeUnitPrice(row) && (row.unitPrice === undefined || row.unitPrice === null || String(row.unitPrice).trim() === '')) { this.$message.warning(`${i + 1}行单价不能为空`); return false; } if (!this.billingTypes(row).includes(row.billingType)) { this.$message.warning('请选择计费要素对应的计费类型'); return false; } if (!this.canEditLimit(row)) continue; const key = row.billingElement; groups[key] = groups[key] || []; for (const range of this.getRanges(row)) { const lower = Number(range.lowerLimit); const upper = Number(range.upperLimit); if (range.lowerLimit === '' || range.upperLimit === '' || Number.isNaN(lower) || Number.isNaN(upper)) { this.$message.warning('请完整填写计费要素上下限'); return false; } if (this.usesRangeUnitPrice(row) && (range.unitPrice === undefined || range.unitPrice === null || String(range.unitPrice).trim() === '')) { this.$message.warning('请为每组计费要素区间填写单价'); return false; } if (lower > upper) { this.$message.warning('计费要素下限不能大于上限'); return false; } groups[key].push({ lower, upper }); } } return this.validateRangeGroups(groups); },
validateRangeGroups(groups) { const precision = 0.000001; for (const [element, ranges] of Object.entries(groups)) { const sorted = [...ranges].sort((a, b) => a.lower - b.lower || a.upper - b.upper); for (let i = 1; i < sorted.length; i += 1) { if (sorted[i].lower < sorted[i - 1].upper - precision) { this.$message.warning(`${element}的计费要素区间不能重叠`); return false; } if (sorted[i].lower > sorted[i - 1].upper + precision) { this.$message.warning(`${element}的计费要素区间必须连续,不能存在间隙`); return false; } } } return true; },
save() { this.$refs.formRef.validate(valid => { if (!valid || !this.validateRules()) return; const plan = clone(this.draft); plan.rules = plan.rules.map(rule => { const ranges = this.canEditLimit(rule) ? this.getRanges(rule) : []; return { ...rule, unitPrice: this.usesRangeUnitPrice(rule) ? ranges[0]?.unitPrice || '' : rule.unitPrice, limitRanges: ranges, lowerLimit: ranges[0]?.lowerLimit || '', upperLimit: ranges[0]?.upperLimit || '', minimumBillingWeight: this.canEditMinimum(rule) ? rule.minimumBillingWeight : '' }; }); this.$emit('save', plan, this.index); this.visible = false; }); },
openMatch(row, index) { this.matchIndex = index; this.matchForm = { ...defaultRule().matchCondition, ...(row.matchCondition || {}) }; this.matchVisible = true; this.ensureRegions(); this.ensureCargo().then(() => { const path = this.resolveCargoPath(this.matchForm); if (path.length) this.matchCargoChange(path); }); },
ensureRegions() { if (this.regionOptions.length) return Promise.resolve(this.regionOptions); if (this.regionRequest) return this.regionRequest; this.regionLoading = true; this.regionRequest = getRegionLazyTree().then(res => { const raw = res.data?.data || []; this.regionOptions = this.normalizeRegionTree(raw); return this.regionOptions; }).finally(() => { this.regionLoading = false; this.regionRequest = null; }); return this.regionRequest; },
normalizeRegionTree(list) { const map = new Map(); const flat = []; const walk = items => (items || []).forEach(item => { flat.push({ ...item, children: undefined }); if (item.children?.length) walk(item.children); }); walk(list); flat.forEach(item => map.set(String(item.id ?? item.code ?? item.value), { ...item, id: String(item.id ?? item.code ?? item.value), parentId: String(item.parentId ?? item.parentCode ?? ''), children: [] })); const roots = []; map.forEach(node => { const parent = map.get(node.parentId); if (parent && parent !== node) parent.children.push(node); else roots.push(node); }); const china = roots.find(item => ['中国', '中华人民共和国'].includes(item.title || item.name)); const source = china?.children?.length ? china.children : roots; const normalize = (node, level) => ({ ...node, title: node.title || node.name || '', leaf: level === 2, children: level === 1 ? (node.children || []).map(child => normalize(child, 2)) : undefined }); return source.map(node => normalize(node, 1)); },
regionLabels(path) { let options = this.regionOptions; const labels = []; (path || []).forEach(value => { const option = options.find(item => String(item.id) === String(value)); if (option) { labels.push(option.title || option.name || ''); options = option.children || []; } }); return labels; },
matchRegionChange(prop, value) { const path = Array.isArray(value) && value.length >= 2 ? value.slice(0, 2) : []; this.matchForm[`${prop}Path`] = path; const labels = this.regionLabels(path); this.matchForm[prop] = labels.join(''); this.matchForm[`${prop}Code`] = path.length ? String(path[path.length - 1]) : ''; },
ensureCargo() { if (this.cargoOptions.length) return Promise.resolve(this.cargoOptions); if (this.cargoRequest) return this.cargoRequest; this.cargoLoading = true; this.cargoRequest = getCargoTypeList(1, 9999).then(res => { const data = res?.data?.data || res?.data || {}; const records = Array.isArray(data) ? data : (data.records || []); this.cargoOptions = this.buildCargoTree(records); this.cargoFlatOptions = this.flatten(this.cargoOptions); return this.cargoOptions; }).finally(() => { this.cargoLoading = false; this.cargoRequest = null; }); return this.cargoRequest; },
buildCargoTree(records) { const flat = []; const walk = items => (items || []).forEach(item => { flat.push(item); if (item.children?.length) walk(item.children); }); walk(records); const map = new Map(); flat.forEach(item => { const id = String(item.id ?? item.cargoCode ?? item.code); map.set(id, { ...item, id, cargoName: item.cargoName || item.name || item.typeName || '', parentId: String(item.parentId ?? item.parentCode ?? ''), children: [] }); }); const roots = []; map.forEach(node => { const parent = map.get(node.parentId); if (parent && parent !== node && Number(node.typeLevel) !== 1) parent.children.push(node); else roots.push(node); }); const normalize = (node, level, parent = []) => { const path = [...parent, node.id]; const children = level === 1 ? node.children.map(item => normalize(item, 2, path)) : []; return { ...node, path, leaf: level === 2, children: children.length ? children : undefined }; }; return roots.map(node => normalize(node, 1)); },
flatten(list) { const result = []; const walk = items => (items || []).forEach(item => { result.push(item); if (item.children?.length) walk(item.children); }); walk(list); return result; },
filterCargo(node, keyword) { const text = String(keyword || '').trim(); return !text || String(node.label || node.text || node.cargoName || '').includes(text); },
cargoLabels(path) { let options = this.cargoOptions; const labels = []; (path || []).forEach(value => { const option = options.find(item => String(item.id) === String(value)); if (option) { labels.push(option.cargoName || ''); options = option.children || []; } }); return labels; },
matchCargoChange(value) { const path = Array.isArray(value) && value.length >= 2 ? value.slice(0, 2).map(item => String(item)) : []; const item = this.cargoFlatOptions.find(option => String(option.id) === String(path[1]) && option.path?.length >= 2); this.matchForm.cargoTypePath = path; this.matchForm.cargoType = item ? this.cargoLabels(path).join('/') : ''; this.matchForm.cargoTypeCode = item ? item.cargoCode || item.code || item.id : ''; },
resolveCargoPath(condition) { if (Array.isArray(condition.cargoTypePath) && condition.cargoTypePath.length >= 2) return condition.cargoTypePath; const item = this.cargoFlatOptions.find(option => option.path?.length >= 2 && (String(option.cargoCode || option.code || option.id) === String(condition.cargoTypeCode) || option.cargoName === condition.cargoType)); return item?.path || []; },
saveMatch() { const row = this.draft.rules[this.matchIndex]; if (row) { this.matchRegionChange('origin', this.matchForm.originPath); this.matchRegionChange('destination', this.matchForm.destinationPath); this.matchCargoChange(this.matchForm.cargoTypePath); row.matchCondition = clone(this.matchForm); } this.matchVisible = false; },
},
};
</script>
<style scoped>.rule-head { margin: 12px 0; }.limit-list { display: flex; flex-direction: column; gap: 8px; }.limit-row { display: flex; align-items: center; gap: 8px; }.limit-row .el-input { flex: 1; }</style>