🔀 合并主干 master 到 dev
This commit is contained in:
@@ -24,11 +24,16 @@ export const getDetail = id =>
|
||||
},
|
||||
});
|
||||
|
||||
export const getCarrierContracts = projectIds =>
|
||||
export const getCarrierContracts = (projectIds, waybillIds = []) =>
|
||||
request({
|
||||
url: `${baseUrl}/carrier-contracts`,
|
||||
method: 'get',
|
||||
params: { projectIds: (projectIds || []).join(',') },
|
||||
params: {
|
||||
projectIds: (projectIds || []).join(','),
|
||||
...(waybillIds || []).filter(Boolean).length
|
||||
? { waybillIds: (waybillIds || []).filter(Boolean).join(',') }
|
||||
: {},
|
||||
},
|
||||
});
|
||||
|
||||
export const saveDraft = row =>
|
||||
|
||||
@@ -48,6 +48,7 @@ export const option = {
|
||||
searchOrder: 1,
|
||||
searchslot: true,
|
||||
slot: true,
|
||||
overHidden: true,
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
@@ -55,6 +56,8 @@ export const option = {
|
||||
prop: 'useDepartment',
|
||||
search: true,
|
||||
searchOrder: 3,
|
||||
slot: true,
|
||||
overHidden: true,
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -27,8 +27,14 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="合同编号">
|
||||
<el-input v-model="form.contractNo" disabled />
|
||||
<el-form-item label="合同编号" prop="contractNo">
|
||||
<el-input
|
||||
v-model="form.contractNo"
|
||||
maxlength="100"
|
||||
clearable
|
||||
placeholder="请输入合同编号"
|
||||
:disabled="isSettlementRuleChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="合同名称" prop="contractName">
|
||||
<el-input
|
||||
@@ -662,6 +668,24 @@ export default {
|
||||
'按数量',
|
||||
],
|
||||
rules: {
|
||||
contractNo: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (this.isSettlementRuleChange) {
|
||||
return callback();
|
||||
}
|
||||
const contractNo = String(value || '').trim();
|
||||
if (!contractNo) {
|
||||
return callback(new Error('请输入合同编号'));
|
||||
}
|
||||
if (contractNo.length > 100) {
|
||||
return callback(new Error('合同编号不能超过100个字符'));
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
contractName: [{ required: true, message: '请输入合同名称', trigger: 'blur' }],
|
||||
contractAmount: [
|
||||
{ required: true, message: '请输入合同金额', trigger: 'blur' },
|
||||
@@ -956,6 +980,16 @@ export default {
|
||||
}
|
||||
await this.$refs.formRef.validate();
|
||||
if (!this.isSettlementRuleChange) {
|
||||
const contractNo = String(this.form.contractNo || '').trim();
|
||||
if (!contractNo) {
|
||||
this.$message.warning('请输入合同编号');
|
||||
return;
|
||||
}
|
||||
if (contractNo.length > 100) {
|
||||
this.$message.warning('合同编号不能超过100个字符');
|
||||
return;
|
||||
}
|
||||
this.form.contractNo = contractNo;
|
||||
const total = this.paymentRatioRows.reduce((sum, row) => sum + Number(row.ratioLimit || 0), 0);
|
||||
if (this.paymentRatioRows.length && Math.abs(total - 100) > 0.0001) {
|
||||
this.$message.warning('付款比例上限合计必须等于100%');
|
||||
|
||||
@@ -901,7 +901,7 @@
|
||||
<div class="loading-manage-dialog__admission-tip">
|
||||
<div class="loading-manage-dialog__admission-title">可配载条件</div>
|
||||
<div class="loading-manage-dialog__admission-text">
|
||||
同时满足以下条件的运单可被配载:运输方式为「公路」;运单状态为「待执行」或「进行中」;未关联其他有效配载单。
|
||||
同时满足以下条件的运单可被配载:运输方式为「公路」;运单状态为「待执行」或「进行中」;未关联其他有效配载单;同一配载单的子运单须属同一项目,且承运商合同乙方须存在共同可选值(按各子单客户合同甲方匹配后取交集)。
|
||||
</div>
|
||||
<div class="loading-manage-dialog__admission-title">配载后状态说明</div>
|
||||
<div class="loading-manage-dialog__admission-text">
|
||||
@@ -1193,6 +1193,16 @@
|
||||
min-width="160"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="carrierContractName"
|
||||
label="承运商合同"
|
||||
min-width="160"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ row.carrierContractName || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="planName"
|
||||
label="计划名称"
|
||||
@@ -3896,6 +3906,7 @@ export default {
|
||||
async loadCandidateWaybills() {
|
||||
this.candidateLoading = true;
|
||||
try {
|
||||
const selectedProjectId = this.selectedWaybillRows.map(row => row.projectId).find(Boolean);
|
||||
const query = {
|
||||
...this.buildQueryParams(this.candidateQuery),
|
||||
// 公路、待执行、未配载的判定都在后端可配载开关内
|
||||
@@ -3905,6 +3916,10 @@ export default {
|
||||
// 其余账户限定本组织(不展开下级)
|
||||
allDept: 0,
|
||||
};
|
||||
// 已选运单后,候选列表优先限定同一项目,便于再按承运商合同乙方交集筛选
|
||||
if (selectedProjectId && !query.projectId && !query.projectName) {
|
||||
query.projectId = selectedProjectId;
|
||||
}
|
||||
const res = await getWaybillList(
|
||||
this.candidatePage.currentPage,
|
||||
this.candidatePage.pageSize,
|
||||
@@ -3966,7 +3981,10 @@ export default {
|
||||
];
|
||||
if (projectIds.length) {
|
||||
try {
|
||||
const response = await loadingApi.getCarrierContracts(projectIds);
|
||||
const waybillIds = [...this.selectedWaybillRows, ...selectedRows]
|
||||
.map(row => row.id)
|
||||
.filter(Boolean);
|
||||
const response = await loadingApi.getCarrierContracts(projectIds, waybillIds);
|
||||
const contracts = response?.data?.data || response?.data || [];
|
||||
const stillAllowed = (Array.isArray(contracts) ? contracts : []).some(
|
||||
item => String(item.id) === String(contractId)
|
||||
@@ -4015,6 +4033,19 @@ export default {
|
||||
exists.add(key);
|
||||
}
|
||||
});
|
||||
// 承运商类型:新增后须仍存在共同的承运商合同乙方(按客户合同甲方匹配后取交集)
|
||||
if (
|
||||
String(this.dialogForm.carrierType || '').trim() === '承运商' &&
|
||||
nextRows.length > this.selectedWaybillRows.length
|
||||
) {
|
||||
const compatible = await this.hasCompatibleCarrierContracts(nextRows);
|
||||
if (!compatible) {
|
||||
this.$message.warning(
|
||||
'所选运单在同一项目下没有共同的承运商合同乙方,不能加入同一配载单'
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const prevRouteNodes = [...this.routeNodes];
|
||||
this.selectedWaybillRows = nextRows;
|
||||
if (this.isRunningEdit) {
|
||||
@@ -4028,6 +4059,18 @@ export default {
|
||||
this.candidateAdding = false;
|
||||
}
|
||||
},
|
||||
async hasCompatibleCarrierContracts(rows = []) {
|
||||
const projectId = (rows || []).map(row => row.projectId).find(Boolean);
|
||||
const waybillIds = (rows || []).map(row => row.id).filter(Boolean);
|
||||
if (!projectId || waybillIds.length < 2) return true;
|
||||
try {
|
||||
const response = await loadingApi.getCarrierContracts([projectId], waybillIds);
|
||||
const contracts = response?.data?.data || response?.data || [];
|
||||
return (Array.isArray(contracts) ? contracts : []).length > 0;
|
||||
} catch (error) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
async addSelectedWaybills() {
|
||||
await this.addWaybillRows(this.candidateSelection);
|
||||
},
|
||||
@@ -4711,9 +4754,13 @@ export default {
|
||||
}
|
||||
this.taskCarrierLoading = true;
|
||||
try {
|
||||
// 配载单只有一个项目,承运商合同也必须属于该项目
|
||||
// 配载单只有一个项目;传入子运单后按客户合同甲方匹配并取承运商合同乙方交集
|
||||
const projectId = this.selectedWaybillRows.map(row => row.projectId).find(Boolean);
|
||||
const response = await loadingApi.getCarrierContracts(projectId ? [projectId] : []);
|
||||
const waybillIds = this.selectedWaybillRows.map(row => row.id).filter(Boolean);
|
||||
const response = await loadingApi.getCarrierContracts(
|
||||
projectId ? [projectId] : [],
|
||||
waybillIds
|
||||
);
|
||||
if (requestId !== this.taskCarrierRequestId) return [];
|
||||
const contracts = response.data?.data || response.data || [];
|
||||
if (this.dialogForm.carrierType !== '自运') {
|
||||
|
||||
@@ -251,10 +251,10 @@
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="不含税单价" min-width="130">
|
||||
<el-table-column label="合计金额" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.unitPriceNoTax"
|
||||
v-model="row.totalAmount"
|
||||
:disabled="readonly"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
@@ -263,9 +263,6 @@
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="不含税金额" min-width="130" align="right">
|
||||
<template #default="{ row }">{{ formatMoney(row.amountNoTax) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="税率" min-width="110">
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
@@ -282,11 +279,14 @@
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="税额" min-width="120">
|
||||
<template #default="{ row }">{{ formatMoney(row.taxAmount) }}</template>
|
||||
<el-table-column label="不含税单价" min-width="140" align="right">
|
||||
<template #default="{ row }">{{ formatUnitPrice(row.unitPriceNoTax) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="合计" min-width="130" align="right">
|
||||
<template #default="{ row }">{{ formatMoney(row.totalAmount) }}</template>
|
||||
<el-table-column label="不含税金额" min-width="130" align="right">
|
||||
<template #default="{ row }">{{ formatMoney(row.amountNoTax) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="税额" min-width="120" align="right">
|
||||
<template #default="{ row }">{{ formatMoney(row.taxAmount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="170">
|
||||
<template #default="{ row }">
|
||||
@@ -1108,15 +1108,30 @@ export default {
|
||||
addLine(sheet) {
|
||||
sheet.lines.push(emptyLine());
|
||||
},
|
||||
/**
|
||||
* 填写合计金额 + 税率后反算:
|
||||
* 合计金额 = 数量 × 不含税单价 × (1 + 税率)
|
||||
* 不含税单价 = 合计金额 / 数量 / (1 + 税率),保留 6 位小数
|
||||
* 不含税金额 = 数量 × 不含税单价,保留 2 位小数
|
||||
*/
|
||||
recalculateLine(row) {
|
||||
const quantity = Number(row.quantity || 0);
|
||||
const unitPrice = Number(row.unitPriceNoTax || 0);
|
||||
const amountNoTax = Number((quantity * unitPrice).toFixed(2));
|
||||
const totalAmount = Number(Number(row.totalAmount || 0).toFixed(2));
|
||||
const rate = Number(row.taxRate || 0) / 100;
|
||||
row.totalAmount = totalAmount;
|
||||
row.amountWithTax = totalAmount;
|
||||
if (quantity <= 0 || totalAmount <= 0) {
|
||||
row.unitPriceNoTax = 0;
|
||||
row.amountNoTax = 0;
|
||||
row.taxAmount = 0;
|
||||
return;
|
||||
}
|
||||
const factor = 1 + rate;
|
||||
const unitPriceNoTax = Number((totalAmount / quantity / factor).toFixed(6));
|
||||
const amountNoTax = Number((quantity * unitPriceNoTax).toFixed(2));
|
||||
row.unitPriceNoTax = unitPriceNoTax;
|
||||
row.amountNoTax = amountNoTax;
|
||||
row.taxAmount = rate ? Number((amountNoTax * rate).toFixed(2)) : 0;
|
||||
row.totalAmount = Number((amountNoTax + row.taxAmount).toFixed(2));
|
||||
row.amountWithTax = row.totalAmount;
|
||||
row.taxAmount = Number((totalAmount - amountNoTax).toFixed(2));
|
||||
},
|
||||
validateBusiness() {
|
||||
if (!this.selectedDetailIds.length) throw new Error('请至少选择一条开票明细');
|
||||
@@ -1129,12 +1144,14 @@ export default {
|
||||
for (const line of sheet.lines) {
|
||||
if (!line.goodsCategory || !line.goodsName) throw new Error('请完整填写商品和服务信息');
|
||||
if (
|
||||
[line.quantity, line.unitPriceNoTax, line.amountNoTax, line.taxRate].some(
|
||||
[line.quantity, line.totalAmount, line.unitPriceNoTax, line.amountNoTax, line.taxRate].some(
|
||||
Number.isNaN
|
||||
)
|
||||
) {
|
||||
throw new Error('开票商品行金额格式不正确');
|
||||
}
|
||||
if (Number(line.quantity || 0) <= 0) throw new Error('开票商品行数量必须大于0');
|
||||
if (Number(line.totalAmount || 0) <= 0) throw new Error('开票商品行合计金额必须大于0');
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1234,6 +1251,9 @@ export default {
|
||||
formatMoney(value) {
|
||||
return Number(value || 0).toFixed(2);
|
||||
},
|
||||
formatUnitPrice(value) {
|
||||
return Number(value || 0).toFixed(6);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1388,16 +1388,12 @@ export default {
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
summaryTotal(value) {
|
||||
this.form.settlementAmount = Number(value || 0).toFixed(2);
|
||||
this.form.localSettlementAmount = (
|
||||
Number(value || 0) * Number(this.form.exchangeRate || 1)
|
||||
).toFixed(2);
|
||||
summaryTotal() {
|
||||
this.syncSettlementAmountsFromSummary();
|
||||
},
|
||||
'form.exchangeRate'(value) {
|
||||
this.form.localSettlementAmount = (
|
||||
Number(this.summaryTotal || 0) * Number(value || 1)
|
||||
).toFixed(2);
|
||||
'form.exchangeRate'() {
|
||||
if (this._skipExchangeRateRebuild) return;
|
||||
this.buildSummaryFeesFromDetails();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@@ -1683,8 +1679,9 @@ export default {
|
||||
current.settlementAmount = current.originalAmount;
|
||||
summaryMap.set(key, current);
|
||||
});
|
||||
// 先保留预结算手工费用行,再按明细本位币 + 结算汇率重算系统归集行
|
||||
this.summaryFees = [...summaryMap.values()];
|
||||
if (!this.summaryFees.length) await this.refreshSummaryFees();
|
||||
await this.refreshSummaryFees();
|
||||
},
|
||||
async loadAllContracts() {
|
||||
const response = await getContractOptions('');
|
||||
@@ -1786,13 +1783,19 @@ export default {
|
||||
},
|
||||
async fetchBfiExchangeRate() {
|
||||
const currency = this.form.currency;
|
||||
if (!currency || currency === 'RMB') return;
|
||||
if (!currency || currency === 'RMB') {
|
||||
this.buildSummaryFeesFromDetails();
|
||||
return;
|
||||
}
|
||||
if (!this.form.exchangeRateDate) return;
|
||||
try {
|
||||
const { data } = await getBfiExchangeRate(currency, this.form.exchangeRateDate);
|
||||
const rateData = data?.data;
|
||||
if (rateData?.excval != null) {
|
||||
this._skipExchangeRateRebuild = true;
|
||||
this.form.exchangeRate = Number(rateData.excval);
|
||||
this._skipExchangeRateRebuild = false;
|
||||
this.buildSummaryFeesFromDetails();
|
||||
} else {
|
||||
this.$message.warning(`未查询到 ${currency} 的BFI汇率数据`);
|
||||
}
|
||||
@@ -1800,6 +1803,37 @@ export default {
|
||||
this.$message.warning('查询BFI汇率失败,请手动输入结算汇率');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 明细金额按本位币理解;外币结算时结算合计原金额/结算金额 = 本位币金额 / 结算汇率。
|
||||
*/
|
||||
convertLocalToSettlementAmount(localAmount) {
|
||||
const amount = Number(localAmount || 0);
|
||||
if (!Number.isFinite(amount)) return 0;
|
||||
if (this.isLocalCurrencySettlement) {
|
||||
return Number(amount.toFixed(2));
|
||||
}
|
||||
const rate = Number(this.form.exchangeRate || 0);
|
||||
if (rate <= 0) return 0;
|
||||
return Number((amount / rate).toFixed(2));
|
||||
},
|
||||
/**
|
||||
* 结算金额取结算合计原币汇总值;本位币合计 = 结算金额 × 结算汇率。
|
||||
*/
|
||||
syncSettlementAmountsFromSummary() {
|
||||
const settlementTotal = this.summaryFees.reduce(
|
||||
(total, row) => total + Number(row.settlementAmount || 0),
|
||||
0
|
||||
);
|
||||
this.form.settlementAmount = Number(settlementTotal || 0).toFixed(2);
|
||||
if (this.isLocalCurrencySettlement) {
|
||||
this.form.localSettlementAmount = this.form.settlementAmount;
|
||||
this.form.exchangeRate = 1;
|
||||
return;
|
||||
}
|
||||
const rate = Number(this.form.exchangeRate || 0);
|
||||
this.form.localSettlementAmount =
|
||||
rate > 0 ? (Number(this.form.settlementAmount || 0) * rate).toFixed(2) : '0.00';
|
||||
},
|
||||
openCandidateDialog() {
|
||||
this.candidate.visible = true;
|
||||
this.candidate.page.current = 1;
|
||||
@@ -2021,8 +2055,9 @@ export default {
|
||||
.map(row => [`${row.feeType}\u0000${row.feeItem}`, row])
|
||||
);
|
||||
const append = (feeItem, originalValue, settlementValue, fallbackFeeType = '') => {
|
||||
const originalAmount = Number(originalValue || 0);
|
||||
const settlementAmount = Number(settlementValue || 0);
|
||||
// 明细费用按本位币归集,外币结算时再按汇率换算成结算原币金额
|
||||
const originalAmount = this.convertLocalToSettlementAmount(originalValue);
|
||||
const settlementAmount = this.convertLocalToSettlementAmount(settlementValue);
|
||||
if (!feeItem || !Number.isFinite(originalAmount) || !Number.isFinite(settlementAmount)) {
|
||||
return;
|
||||
}
|
||||
@@ -2060,6 +2095,7 @@ export default {
|
||||
});
|
||||
const manualRows = this.summaryFees.filter(row => Number(row.manualFlag) === 1);
|
||||
this.summaryFees = [...generatedMap.values(), ...manualRows];
|
||||
this.syncSettlementAmountsFromSummary();
|
||||
},
|
||||
// 每条明细对应的费用行:[调整前基准行, 调整后当前行],缺少费用快照时退化为明细自身的汇总行。
|
||||
resolveDetailFeePairs(detail) {
|
||||
|
||||
@@ -1332,9 +1332,12 @@ export default {
|
||||
if (value) this.initialize();
|
||||
},
|
||||
},
|
||||
summaryTotal(value) {
|
||||
this.form.settlementAmount = Number(value || 0).toFixed(2);
|
||||
this.recalculateLocalAmount();
|
||||
summaryTotal() {
|
||||
this.syncSettlementAmountsFromSummary();
|
||||
},
|
||||
'form.exchangeRate'() {
|
||||
if (this._skipExchangeRateRebuild) return;
|
||||
this.buildSummaryFeesFromDetails();
|
||||
},
|
||||
filteredDetails: {
|
||||
handler(list) {
|
||||
@@ -1890,8 +1893,9 @@ export default {
|
||||
.map(row => [`${row.feeType}\u0000${row.feeItem}`, row])
|
||||
);
|
||||
const appendSummary = (feeItem, originalValue, settlementValue, fallbackFeeType = '') => {
|
||||
const originalAmount = Number(originalValue || 0);
|
||||
const settlementAmount = Number(settlementValue || 0);
|
||||
// 明细费用按本位币归集,外币结算时再按汇率换算成结算原币金额
|
||||
const originalAmount = this.convertLocalToSettlementAmount(originalValue);
|
||||
const settlementAmount = this.convertLocalToSettlementAmount(settlementValue);
|
||||
if (!feeItem || !Number.isFinite(originalAmount) || !Number.isFinite(settlementAmount)) {
|
||||
return;
|
||||
}
|
||||
@@ -1946,7 +1950,7 @@ export default {
|
||||
});
|
||||
nextFeeMap.forEach(row => orderedFees.push(row));
|
||||
this.summaryFees = orderedFees;
|
||||
this.recalculateLocalAmount();
|
||||
this.syncSettlementAmountsFromSummary();
|
||||
},
|
||||
// 每条明细对应的费用行:[调整前基准行, 调整后当前行],缺少来源费用快照时退化为明细自身的汇总行。
|
||||
resolveDetailFeePairs(detail) {
|
||||
@@ -2034,17 +2038,21 @@ export default {
|
||||
},
|
||||
handleExchangeRateDateChange() {
|
||||
this.fetchBfiExchangeRate();
|
||||
this.recalculateLocalAmount();
|
||||
},
|
||||
async fetchBfiExchangeRate() {
|
||||
if (!this.form.currency || this.form.currency === 'RMB') return;
|
||||
if (!this.form.currency || this.form.currency === 'RMB') {
|
||||
this.buildSummaryFeesFromDetails();
|
||||
return;
|
||||
}
|
||||
if (!this.form.exchangeRateDate) return;
|
||||
try {
|
||||
const { data } = await getBfiExchangeRate(this.form.currency, this.form.exchangeRateDate);
|
||||
const rateData = data?.data;
|
||||
if (rateData?.excval != null) {
|
||||
this._skipExchangeRateRebuild = true;
|
||||
this.form.exchangeRate = Number(rateData.excval);
|
||||
this.recalculateLocalAmount();
|
||||
this._skipExchangeRateRebuild = false;
|
||||
this.buildSummaryFeesFromDetails();
|
||||
} else {
|
||||
this.$message.warning(`未查询到 ${this.form.currency} 的BFI汇率数据`);
|
||||
}
|
||||
@@ -2052,9 +2060,39 @@ export default {
|
||||
this.$message.warning('查询BFI汇率失败,请手动输入结算汇率');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 明细金额按本位币理解;外币结算时结算合计原金额/结算金额 = 本位币金额 / 结算汇率。
|
||||
*/
|
||||
convertLocalToSettlementAmount(localAmount) {
|
||||
const amount = Number(localAmount || 0);
|
||||
if (!Number.isFinite(amount)) return 0;
|
||||
if (this.isLocalCurrencySettlement) {
|
||||
return Number(amount.toFixed(2));
|
||||
}
|
||||
const rate = Number(this.form.exchangeRate || 0);
|
||||
if (rate <= 0) return 0;
|
||||
return Number((amount / rate).toFixed(2));
|
||||
},
|
||||
/**
|
||||
* 结算金额取结算合计原币汇总值;本位币合计 = 结算金额 × 结算汇率。
|
||||
*/
|
||||
syncSettlementAmountsFromSummary() {
|
||||
const settlementTotal = this.summaryFees.reduce(
|
||||
(total, row) => total + Number(row.settlementAmount || 0),
|
||||
0
|
||||
);
|
||||
this.form.settlementAmount = Number(settlementTotal || 0).toFixed(2);
|
||||
if (this.isLocalCurrencySettlement) {
|
||||
this.form.localSettlementAmount = this.form.settlementAmount;
|
||||
this.form.exchangeRate = 1;
|
||||
return;
|
||||
}
|
||||
const rate = Number(this.form.exchangeRate || 0);
|
||||
this.form.localSettlementAmount =
|
||||
rate > 0 ? (Number(this.form.settlementAmount || 0) * rate).toFixed(2) : '0.00';
|
||||
},
|
||||
recalculateLocalAmount() {
|
||||
const rate = this.form.currency === 'RMB' ? 1 : Number(this.form.exchangeRate || 0);
|
||||
this.form.localSettlementAmount = (Number(this.form.settlementAmount || 0) * rate).toFixed(2);
|
||||
this.buildSummaryFeesFromDetails();
|
||||
},
|
||||
openCandidateDialog() {
|
||||
if (!this.form.contractId) {
|
||||
|
||||
@@ -59,11 +59,22 @@
|
||||
</el-button>
|
||||
</template>
|
||||
<template #organizationName="{ row }">
|
||||
<span
|
||||
v-if="isBlankOrganizationName(row.organizationName)"
|
||||
class="organization-name--empty"
|
||||
>未分配组织</span>
|
||||
<span v-else>{{ String(row.organizationName || '').replace(/^[\s\u3000]+/, '') }}</span>
|
||||
<el-tooltip
|
||||
:content="formatOrgDisplayName(row.organizationName)"
|
||||
placement="top"
|
||||
:disabled="!formatOrgDisplayName(row.organizationName)"
|
||||
>
|
||||
<span class="org-cell-text">{{ formatOrgDisplayName(row.organizationName) }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template #useDepartment="{ row }">
|
||||
<el-tooltip
|
||||
:content="formatOrgDisplayName(row.useDepartment)"
|
||||
placement="top"
|
||||
:disabled="!formatOrgDisplayName(row.useDepartment)"
|
||||
>
|
||||
<span class="org-cell-text">{{ formatOrgDisplayName(row.useDepartment) }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'primary' : 'info'" class="status-text">
|
||||
@@ -319,26 +330,58 @@
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="所有组织" prop="organizationName">
|
||||
<el-cascader
|
||||
v-model="organizationCascaderValue"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<el-tooltip
|
||||
:content="
|
||||
formatOrgCascaderPath(organizationCascaderPath) ||
|
||||
formatOrgDisplayName(vehicleForm.organizationName)
|
||||
"
|
||||
placement="top"
|
||||
:disabled="
|
||||
!(
|
||||
formatOrgCascaderPath(organizationCascaderPath) ||
|
||||
formatOrgDisplayName(vehicleForm.organizationName)
|
||||
)
|
||||
"
|
||||
>
|
||||
<div class="org-cascader-tooltip">
|
||||
<el-cascader
|
||||
v-model="organizationCascaderValue"
|
||||
:options="organizationFormTreeOptions"
|
||||
:props="formOrgCascaderProps"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
/>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="使用部门" prop="useDepartment">
|
||||
<el-cascader
|
||||
v-model="useDepartmentCascaderValue"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<el-tooltip
|
||||
:content="
|
||||
formatOrgCascaderPath(useDepartmentCascaderPath) ||
|
||||
formatOrgDisplayName(vehicleForm.useDepartment)
|
||||
"
|
||||
placement="top"
|
||||
:disabled="
|
||||
!(
|
||||
formatOrgCascaderPath(useDepartmentCascaderPath) ||
|
||||
formatOrgDisplayName(vehicleForm.useDepartment)
|
||||
)
|
||||
"
|
||||
>
|
||||
<div class="org-cascader-tooltip">
|
||||
<el-cascader
|
||||
v-model="useDepartmentCascaderValue"
|
||||
:options="useDepartmentFormTreeOptions"
|
||||
:props="formOrgCascaderProps"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
/>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -656,8 +699,17 @@ export default {
|
||||
vehicleCertificateUploads: {},
|
||||
uploadHeaders: getUploadHeaders(),
|
||||
organizationOptions: [],
|
||||
// 搜索栏组织级联追加「未分配组织」一项(哨兵值见 organization-search mixin)
|
||||
enableUnassignedOrgFilter: true,
|
||||
// 表单两个级联各自维护路径,避免名称回写互相覆盖;不要与搜索栏共用同一棵树节点
|
||||
organizationCascaderPath: [],
|
||||
useDepartmentCascaderPath: [],
|
||||
formOrgCascaderProps: {
|
||||
value: 'id',
|
||||
label: 'label',
|
||||
children: 'children',
|
||||
emitPath: true,
|
||||
checkStrictly: true,
|
||||
expandTrigger: 'hover',
|
||||
},
|
||||
vehicleTypeOptions: [
|
||||
{ label: '高栏车', value: '高栏' },
|
||||
{ label: '低栏车', value: '低栏' },
|
||||
@@ -669,7 +721,7 @@ export default {
|
||||
businessRelationOptions: ['自有', '管理挂靠', '业务合作'],
|
||||
energyTypeOptions: ['柴油', '汽油', '新能源', '天然气'],
|
||||
baseRules: {
|
||||
// 所属组织可为空:为空表示未分配组织,数据权限为空集
|
||||
organizationName: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
|
||||
useDepartment: [{ required: true, message: '请选择使用部门', trigger: 'change' }],
|
||||
plateNo: [
|
||||
{ required: true, message: '请输入车牌号', trigger: 'change' },
|
||||
@@ -703,22 +755,31 @@ export default {
|
||||
: [{ required: true, message: '请选择行驶证有效期', trigger: 'change' }],
|
||||
};
|
||||
},
|
||||
// 所属组织级联:表单存组织名称,级联控件用 id 路径,两者在此双向转换
|
||||
// 两个级联必须使用克隆后的独立树,否则 checkStrictly 会在节点上互相抢选中态
|
||||
organizationFormTreeOptions() {
|
||||
return this.cloneOrgTree(this.organizationTreeOptions);
|
||||
},
|
||||
useDepartmentFormTreeOptions() {
|
||||
return this.cloneOrgTree(this.organizationTreeOptions);
|
||||
},
|
||||
// 所属组织级联:本地保存 id 路径,名称同步到表单字段供后端使用
|
||||
organizationCascaderValue: {
|
||||
get() {
|
||||
return this.resolveOrgPath(this.vehicleForm.organizationName);
|
||||
return this.organizationCascaderPath;
|
||||
},
|
||||
set(path) {
|
||||
this.vehicleForm.organizationName = this.resolveOrgName(path);
|
||||
this.organizationCascaderPath = Array.isArray(path) ? [...path] : [];
|
||||
this.vehicleForm.organizationName = this.resolveOrgName(this.organizationCascaderPath);
|
||||
},
|
||||
},
|
||||
// 使用部门级联:与所属组织同一套部门树
|
||||
// 使用部门级联:独立路径状态,避免与所属组织共用计算回写导致选不中
|
||||
useDepartmentCascaderValue: {
|
||||
get() {
|
||||
return this.resolveOrgPath(this.vehicleForm.useDepartment);
|
||||
return this.useDepartmentCascaderPath;
|
||||
},
|
||||
set(path) {
|
||||
this.vehicleForm.useDepartment = this.resolveOrgName(path);
|
||||
this.useDepartmentCascaderPath = Array.isArray(path) ? [...path] : [];
|
||||
this.vehicleForm.useDepartment = this.resolveOrgName(this.useDepartmentCascaderPath);
|
||||
},
|
||||
},
|
||||
isAdmin() {
|
||||
@@ -751,10 +812,49 @@ export default {
|
||||
this.initDeptTree();
|
||||
this.loadOrganizationOptions();
|
||||
},
|
||||
watch: {
|
||||
organizationTreeOptions() {
|
||||
if (this.vehicleBox) {
|
||||
this.syncOrgCascaderPaths();
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.permission?.[code] === true;
|
||||
},
|
||||
/** 去掉组织名称前导空白,供列表与提示展示 */
|
||||
formatOrgDisplayName(name) {
|
||||
return String(name || '').replace(/^[\s\u3000]+/, '');
|
||||
},
|
||||
/** 级联路径还原完整组织层级文案,悬停展示用 */
|
||||
formatOrgCascaderPath(path) {
|
||||
const ids = Array.isArray(path)
|
||||
? path.filter(item => item !== undefined && item !== null && item !== '')
|
||||
: [];
|
||||
if (!ids.length) return '';
|
||||
const labels = [];
|
||||
let nodes = this.organizationTreeOptions || [];
|
||||
for (const id of ids) {
|
||||
const node = nodes.find(item => String(item.id) === String(id));
|
||||
if (!node) break;
|
||||
labels.push(node.rawLabel || node.label || '');
|
||||
nodes = node.children || [];
|
||||
}
|
||||
return labels.filter(Boolean).join(' / ');
|
||||
},
|
||||
cloneOrgTree(tree = []) {
|
||||
return (tree || []).map(item => ({
|
||||
id: String(item.id),
|
||||
label: item.label,
|
||||
rawLabel: item.rawLabel,
|
||||
children: item.children?.length ? this.cloneOrgTree(item.children) : undefined,
|
||||
}));
|
||||
},
|
||||
syncOrgCascaderPaths() {
|
||||
this.organizationCascaderPath = this.resolveOrgPath(this.vehicleForm.organizationName);
|
||||
this.useDepartmentCascaderPath = this.resolveOrgPath(this.vehicleForm.useDepartment);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
this.organizationOptions = this.flattenDeptOptions(res.data.data || []);
|
||||
@@ -766,6 +866,9 @@ export default {
|
||||
if (this.vehicleBox && !this.vehicleForm.useDepartment) {
|
||||
this.vehicleForm.useDepartment = this.getCurrentOrganizationName();
|
||||
}
|
||||
if (this.vehicleBox) {
|
||||
this.syncOrgCascaderPaths();
|
||||
}
|
||||
});
|
||||
},
|
||||
flattenDeptOptions(tree = [], level = 0) {
|
||||
@@ -860,6 +963,7 @@ export default {
|
||||
this.vehicleForm = emptyForm();
|
||||
this.vehicleForm.organizationName = this.getCurrentOrganizationName();
|
||||
this.vehicleForm.useDepartment = this.getCurrentOrganizationName();
|
||||
this.syncOrgCascaderPaths();
|
||||
this.syncPlateNo(false);
|
||||
this.vehicleBox = true;
|
||||
return;
|
||||
@@ -873,6 +977,7 @@ export default {
|
||||
if (!this.vehicleForm.useDepartment) {
|
||||
this.vehicleForm.useDepartment = this.getCurrentOrganizationName();
|
||||
}
|
||||
this.syncOrgCascaderPaths();
|
||||
this.splitPlateNo();
|
||||
this.syncPlateNo(false);
|
||||
this.vehicleBox = true;
|
||||
@@ -884,6 +989,8 @@ export default {
|
||||
},
|
||||
resetVehicle() {
|
||||
this.vehicleForm = emptyForm();
|
||||
this.organizationCascaderPath = [];
|
||||
this.useDepartmentCascaderPath = [];
|
||||
this.vehicleCertificateUploads = {};
|
||||
this.readonly = false;
|
||||
this.submitLoading = false;
|
||||
@@ -1503,11 +1610,6 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
// 空组织记录在列表里的占位文案
|
||||
.organization-name--empty {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
// 编辑弹窗:灰底 + section-card 白卡分组
|
||||
.vehicle-form {
|
||||
:deep(.el-form-item__label) {
|
||||
@@ -1541,7 +1643,20 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.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%;
|
||||
}
|
||||
|
||||
:deep(.el-date-editor.el-input),
|
||||
:deep(.el-date-editor.el-input__wrapper),
|
||||
:deep(.el-select),
|
||||
|
||||
@@ -102,15 +102,10 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="减分最高扣分">
|
||||
<el-input
|
||||
:model-value="configForm.maxMinusScore"
|
||||
placeholder="所有减分项合计不得超过该分值"
|
||||
clearable
|
||||
style="width: 500px"
|
||||
@input="handleMaxMinusScoreInput"
|
||||
>
|
||||
<el-input :model-value="HARDCODED_MAX_MINUS_SCORE" disabled style="width: 500px">
|
||||
<template #append>分</template>
|
||||
</el-input>
|
||||
<div class="score-config__hint">暂定最高扣 15 分,加分合计同样最高按 15 分计</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
@@ -500,9 +495,15 @@ const standardNumberFields = [
|
||||
'creditLimitIncrease',
|
||||
];
|
||||
|
||||
/** 暂定:加分/减分合计上限均为 15 分,超出按 15 分计 */
|
||||
const HARDCODED_MAX_PLUS_SCORE = 15;
|
||||
const HARDCODED_MAX_MINUS_SCORE = 15;
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
HARDCODED_MAX_PLUS_SCORE,
|
||||
HARDCODED_MAX_MINUS_SCORE,
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
@@ -639,7 +640,7 @@ export default {
|
||||
name: '',
|
||||
remark: '',
|
||||
standardDescription: '',
|
||||
maxMinusScore: '',
|
||||
maxMinusScore: HARDCODED_MAX_MINUS_SCORE,
|
||||
status: 3,
|
||||
categories: this.defaultCategories(),
|
||||
standards: [],
|
||||
@@ -696,7 +697,8 @@ export default {
|
||||
});
|
||||
return {
|
||||
...detail,
|
||||
maxMinusScore: this.normalizePlainDecimal(detail.maxMinusScore),
|
||||
// 暂定写死最高扣分,不读取历史配置值
|
||||
maxMinusScore: HARDCODED_MAX_MINUS_SCORE,
|
||||
categories: categories.map(category => ({
|
||||
...(map[category.categoryCode] || category),
|
||||
categoryName: category.categoryName,
|
||||
@@ -704,14 +706,6 @@ export default {
|
||||
standards: (detail.standards || []).map(standard => this.normalizeStandard(standard)),
|
||||
};
|
||||
},
|
||||
handleMaxMinusScoreInput(value) {
|
||||
const raw = String(value ?? '')
|
||||
.replace(/[^\d.]/g, '')
|
||||
.replace(/^\./, '')
|
||||
.replace(/(\..*)\./g, '$1')
|
||||
.replace(/^(\d+)(\.\d{0,2})?.*$/, '$1$2');
|
||||
this.configForm.maxMinusScore = raw;
|
||||
},
|
||||
openConfig(row, readonly = false) {
|
||||
this.readonly = readonly;
|
||||
if (row && row.id) {
|
||||
@@ -837,9 +831,7 @@ export default {
|
||||
},
|
||||
prepareSubmitConfig(config) {
|
||||
const payload = JSON.parse(JSON.stringify(config || {}));
|
||||
const maxMinus = this.normalizePlainDecimal(payload.maxMinusScore);
|
||||
payload.maxMinusScore =
|
||||
maxMinus === '' || maxMinus == null ? null : this.toScoreValue(maxMinus);
|
||||
payload.maxMinusScore = this.toScoreValue(HARDCODED_MAX_MINUS_SCORE);
|
||||
(payload.categories || []).forEach(category => {
|
||||
(category.items || []).forEach(item => {
|
||||
item.score = this.toScoreValue(item.score);
|
||||
@@ -1096,14 +1088,8 @@ export default {
|
||||
this.$message.warning('请输入评定表名称');
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
this.configForm.maxMinusScore !== '' &&
|
||||
this.configForm.maxMinusScore != null &&
|
||||
!this.isNonNegativeDecimal(this.configForm.maxMinusScore)
|
||||
) {
|
||||
this.$message.warning('减分最高扣分须为不小于0的数字,最多两位小数');
|
||||
return false;
|
||||
}
|
||||
// 减分最高扣分暂定写死为 15,提交前强制回填,避免历史脏数据触发校验
|
||||
this.configForm.maxMinusScore = HARDCODED_MAX_MINUS_SCORE;
|
||||
const basic = this.configForm.categories.find(item => item.categoryCode === 'basic');
|
||||
if (!basic || basic.items.length === 0) {
|
||||
this.$message.warning('至少配置1个基础得分项目');
|
||||
@@ -1586,6 +1572,13 @@ export default {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.score-config__hint {
|
||||
margin-top: 6px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
:deep(.score-config__name-input) {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
@@ -1588,6 +1588,9 @@ const customerArchiveFormRoute = '/vehicle/customer-archive/form';
|
||||
const customerArchiveListRefreshKey = 'customer-archive-list-refresh';
|
||||
/** 客商所属组织固定为顶层组织 */
|
||||
const FIXED_OWNER_DEPT_NAME = '广西现代物流集团有限公司';
|
||||
/** 暂定:加分/减分合计上限均为 15 分,超出按 15 分计 */
|
||||
const HARDCODED_MAX_PLUS_SCORE = 15;
|
||||
const HARDCODED_MAX_MINUS_SCORE = 15;
|
||||
|
||||
const createTimeRangeMap = {
|
||||
createTimeRange: ['createTimeStart', 'createTimeEnd'],
|
||||
@@ -2291,16 +2294,18 @@ export default {
|
||||
{ categoryCode: 'plus', categoryName: '加分项目' },
|
||||
{ categoryCode: 'minus', categoryName: '减分项目' },
|
||||
];
|
||||
const maxMinus = Number(this.currentScore?.maxMinusScore);
|
||||
const hasMaxMinus = Number.isFinite(maxMinus) && maxMinus >= 0;
|
||||
const rows = categoryMap.map((category, index) => {
|
||||
const details = this.getScoreCategoryDetails(category.categoryCode);
|
||||
let selfScore = details.reduce((sum, item) => sum + Number(item.selfScore || 0), 0);
|
||||
let reviewScore = details.reduce((sum, item) => sum + Number(item.reviewScore || 0), 0);
|
||||
// 减分项目合计受量化表「最高扣分」限制
|
||||
if (category.categoryCode === 'minus' && hasMaxMinus) {
|
||||
selfScore = Math.min(selfScore, maxMinus);
|
||||
reviewScore = Math.min(reviewScore, maxMinus);
|
||||
// 暂定:加分/减分合计最高按 15 分计
|
||||
if (category.categoryCode === 'plus') {
|
||||
selfScore = Math.min(selfScore, HARDCODED_MAX_PLUS_SCORE);
|
||||
reviewScore = Math.min(reviewScore, HARDCODED_MAX_PLUS_SCORE);
|
||||
}
|
||||
if (category.categoryCode === 'minus') {
|
||||
selfScore = Math.min(selfScore, HARDCODED_MAX_MINUS_SCORE);
|
||||
reviewScore = Math.min(reviewScore, HARDCODED_MAX_MINUS_SCORE);
|
||||
}
|
||||
return {
|
||||
...category,
|
||||
@@ -4216,7 +4221,8 @@ export default {
|
||||
scoreDate: this.$dayjs().format('YYYY-MM-DD'),
|
||||
applyCreditLimit: this.archiveForm.applyCreditLimit || '',
|
||||
maxCreditLimit: '',
|
||||
maxMinusScore: '',
|
||||
maxMinusScore: HARDCODED_MAX_MINUS_SCORE,
|
||||
maxPlusScore: HARDCODED_MAX_PLUS_SCORE,
|
||||
creditLevel: '',
|
||||
standards: [],
|
||||
tempApplyCreditLimit: '',
|
||||
@@ -4236,10 +4242,8 @@ export default {
|
||||
: score.applyCreditLimit
|
||||
),
|
||||
maxCreditLimit: this.normalizeOptionalAmount(score.maxCreditLimit),
|
||||
maxMinusScore:
|
||||
score.maxMinusScore === undefined || score.maxMinusScore === null
|
||||
? ''
|
||||
: score.maxMinusScore,
|
||||
maxMinusScore: HARDCODED_MAX_MINUS_SCORE,
|
||||
maxPlusScore: HARDCODED_MAX_PLUS_SCORE,
|
||||
tempApplyCreditLimit: this.normalizeOptionalAmount(score.tempApplyCreditLimit),
|
||||
attachments: score.attachments || this.parseAttachments(score.proofAttachments),
|
||||
details: (score.details || []).map(detail => ({
|
||||
@@ -4699,11 +4703,8 @@ export default {
|
||||
applyCreditLimit: this.currentScore.applyCreditLimit,
|
||||
maxCreditLimit: '',
|
||||
creditLevel: '',
|
||||
maxMinusScore:
|
||||
quantificationData.maxMinusScore ??
|
||||
scoreTemplate.maxMinusScore ??
|
||||
this.currentScore.maxMinusScore ??
|
||||
'',
|
||||
maxMinusScore: HARDCODED_MAX_MINUS_SCORE,
|
||||
maxPlusScore: HARDCODED_MAX_PLUS_SCORE,
|
||||
tempApplyCreditLimit: this.currentScore.tempApplyCreditLimit,
|
||||
tempCreditStartDate: this.currentScore.tempCreditStartDate,
|
||||
tempCreditEndDate: this.currentScore.tempCreditEndDate,
|
||||
@@ -4722,14 +4723,11 @@ export default {
|
||||
},
|
||||
applyMaxMinusScoreFromQuantification(quantificationId) {
|
||||
if (!quantificationId) return Promise.resolve();
|
||||
return getCreditScoreQuantificationDetail(quantificationId).then(res => {
|
||||
const data = res?.data?.data || {};
|
||||
this.currentScore.maxMinusScore =
|
||||
data.maxMinusScore === undefined || data.maxMinusScore === null
|
||||
? ''
|
||||
: data.maxMinusScore;
|
||||
this.calculateScore(this.currentScore);
|
||||
});
|
||||
// 暂定写死加分/减分上限,不再依赖量化表配置值
|
||||
this.currentScore.maxMinusScore = HARDCODED_MAX_MINUS_SCORE;
|
||||
this.currentScore.maxPlusScore = HARDCODED_MAX_PLUS_SCORE;
|
||||
this.calculateScore(this.currentScore);
|
||||
return Promise.resolve();
|
||||
},
|
||||
normalizeScoreTemplateDetails(template = {}) {
|
||||
const categoryDetails = (template.categories || []).flatMap(category =>
|
||||
@@ -5073,17 +5071,17 @@ export default {
|
||||
const plusDetails = this.getScoreCategoryDetailsByList(details, 'plus');
|
||||
const minusDetails = this.getScoreCategoryDetailsByList(details, 'minus');
|
||||
const sumScore = (list, prop) => list.reduce((sum, item) => sum + Number(item[prop] || 0), 0);
|
||||
const rawPlusSelf = sumScore(plusDetails, 'selfScore');
|
||||
const rawPlusReview = sumScore(plusDetails, 'reviewScore');
|
||||
const rawMinusSelf = sumScore(minusDetails, 'selfScore');
|
||||
const rawMinusReview = sumScore(minusDetails, 'reviewScore');
|
||||
// 量化表「减分最高扣分」:所有减分项合计不得超过该上限
|
||||
const maxMinus = Number(score.maxMinusScore);
|
||||
const hasMaxMinus = Number.isFinite(maxMinus) && maxMinus >= 0;
|
||||
const minusSelf = hasMaxMinus ? Math.min(rawMinusSelf, maxMinus) : rawMinusSelf;
|
||||
const minusReview = hasMaxMinus ? Math.min(rawMinusReview, maxMinus) : rawMinusReview;
|
||||
const selfScore =
|
||||
sumScore(basicDetails, 'selfScore') + sumScore(plusDetails, 'selfScore') - minusSelf;
|
||||
const reviewScore =
|
||||
sumScore(basicDetails, 'reviewScore') + sumScore(plusDetails, 'reviewScore') - minusReview;
|
||||
// 暂定:加分/减分合计最高按 15 分计
|
||||
const plusSelf = Math.min(rawPlusSelf, HARDCODED_MAX_PLUS_SCORE);
|
||||
const plusReview = Math.min(rawPlusReview, HARDCODED_MAX_PLUS_SCORE);
|
||||
const minusSelf = Math.min(rawMinusSelf, HARDCODED_MAX_MINUS_SCORE);
|
||||
const minusReview = Math.min(rawMinusReview, HARDCODED_MAX_MINUS_SCORE);
|
||||
const selfScore = sumScore(basicDetails, 'selfScore') + plusSelf - minusSelf;
|
||||
const reviewScore = sumScore(basicDetails, 'reviewScore') + plusReview - minusReview;
|
||||
const hasReviewScore = details.some(
|
||||
item => !this.isBlankScore(item.reviewScore)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user