diff --git a/src/views/vehicle/credit-score-quantification.vue b/src/views/vehicle/credit-score-quantification.vue
index b2eb1f9..f148242 100644
--- a/src/views/vehicle/credit-score-quantification.vue
+++ b/src/views/vehicle/credit-score-quantification.vue
@@ -85,67 +85,73 @@
>
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
- {{ row.items.length }}
-
-
-
- 编辑
-
-
-
+
+
+
+
+ {{ row.items.length }}
+
+
+
+ 编辑
+
+
+
-
+
添加评估标准
-
-
-
-
-
-
- 编辑
- 删除
-
-
-
+
+
+
+
+
+
+ 编辑
+ 删除
+
+
+
@@ -166,31 +172,37 @@
评分类目:{{ currentCategory.categoryName }}
-
+
添加评分项目
-
-
-
-
-
- {{ row.options.length }}
-
-
-
- 编辑
-
- 删除
-
-
-
-
+
+
+
+
+
+ {{ row.options.length }}
+
+
+
+ 编辑
+
+ 删除
+
+
+
+
@@ -206,51 +218,119 @@
class="score-item-dialog"
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +添加选项
-
-
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+ 选项
+ 分值
+
+
+
+
+
+
+
+
+
+
+
+
+
+ handleOptionDecimalInput(option, 'changeValue', value)"
+ >
+
+
+
+
+
+
+
+
+
+
+
+ handleOptionDecimalInput(option, 'score', value)"
+ >
+ 分
+
+
+
+
+
+
+ handleOptionDecimalInput(option, 'score', value)"
+ >
+ 分
+
+
+
+
+ 删除
+
+
+
+ +添加选项
+
+
+
@@ -267,93 +347,99 @@
:close-on-press-escape="false"
>
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- handleStandardNumberInput('scoreRateIncrease', value)"
- >
- %
-
-
-
-
-
- handleStandardNumberInput('creditLimitIncrease', value, 2)"
- class="credit-increase-input"
- />
-
-
-
-
-
-
-
- handleStandardNumberInput('creditLimitLower', value, 2)"
+ @input="value => handleStandardNumberInput('creditLimitIncrease', value, 2)"
+ class="credit-increase-input"
/>
- 至
- handleStandardNumberInput('creditLimitUpper', value, 2)"
- />
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ handleStandardNumberInput('creditLimitLower', value, 2)"
+ />
+ 至
+ handleStandardNumberInput('creditLimitUpper', value, 2)"
+ />
+
+
+
+
+
+
+
@@ -413,6 +499,7 @@ export default {
label: item,
value: item,
})),
+ scoreUnitOptions: ['%', '件', '次', '项', '天'],
option: {
height: 'auto',
calcHeight: 32,
@@ -536,7 +623,19 @@ export default {
score: 1,
scoreDescription: '',
optionDescription: '',
- options: [{ optionName: '', score: 1 }],
+ optionType: 'option',
+ baseValue: '',
+ options: [this.emptyOption()],
+ };
+ },
+ emptyOption() {
+ return {
+ optionName: '',
+ score: 1,
+ changeType: 'increase',
+ changeValue: '',
+ changeUnit: '%',
+ scoreType: 'add',
};
},
emptyStandard() {
@@ -559,14 +658,20 @@ export default {
...category,
items: (category.items || []).map(item => ({
...item,
+ optionType: this.normalizeOptionType(item.optionType),
options:
- item.options && item.options.length ? item.options : [{ optionName: '', score: 1 }],
+ item.options && item.options.length
+ ? item.options.map(option => this.normalizeOption(option))
+ : [this.emptyOption()],
})),
};
});
return {
...detail,
- categories: categories.map(category => map[category.categoryCode] || category),
+ categories: categories.map(category => ({
+ ...(map[category.categoryCode] || category),
+ categoryName: category.categoryName,
+ })),
standards: detail.standards || [],
};
},
@@ -596,10 +701,84 @@ export default {
openItem(row, index = -1) {
this.currentItemIndex = index;
this.itemForm = row
- ? { ...this.emptyItem(), ...JSON.parse(JSON.stringify(row)) }
+ ? this.normalizeItem({ ...this.emptyItem(), ...JSON.parse(JSON.stringify(row)) })
: this.emptyItem();
this.itemBox = true;
},
+ normalizeOptionType(value) {
+ return value === 'score' || value === 2 || String(value) === 'score' ? 'score' : 'option';
+ },
+ normalizeItem(item = {}) {
+ return {
+ ...this.emptyItem(),
+ ...item,
+ optionType: this.normalizeOptionType(item.optionType),
+ options:
+ item.options && item.options.length
+ ? item.options.map(option => this.normalizeOption(option))
+ : [this.emptyOption()],
+ };
+ },
+ normalizeOption(option = {}) {
+ const normalized = { ...this.emptyOption(), ...option };
+ return {
+ ...normalized,
+ changeType:
+ normalized.changeType === 'decrease' ||
+ normalized.changeType === '每减少' ||
+ normalized.changeType === 2
+ ? 'decrease'
+ : 'increase',
+ scoreType:
+ normalized.scoreType === 'subtract' ||
+ normalized.scoreType === '减' ||
+ normalized.scoreType === 2
+ ? 'subtract'
+ : 'add',
+ };
+ },
+ handleOptionTypeChange(value) {
+ this.itemForm.optionType = this.normalizeOptionType(value);
+ this.itemForm.options = (this.itemForm.options || []).map(option =>
+ this.normalizeOption(option)
+ );
+ },
+ handleBaseValueInput(value) {
+ const source = String(value ?? '').replace(/[^\d.-]/g, '');
+ const negative = source.startsWith('-');
+ const unsigned = source.replace(/-/g, '');
+ const [integer = '', ...decimalParts] = unsigned.split('.');
+ const decimal = decimalParts.join('').slice(0, 10);
+ this.itemForm.baseValue = `${negative ? '-' : ''}${integer}${decimalParts.length ? `.${decimal}` : ''}`;
+ },
+ handleOptionDecimalInput(option, field, value) {
+ option[field] = this.normalizeDecimalInput(value);
+ },
+ normalizeDecimalInput(value) {
+ const source = String(value ?? '').replace(/[^\d.]/g, '');
+ const [integer = '', ...decimalParts] = source.split('.');
+ const decimal = decimalParts.join('').slice(0, 2);
+ return decimalParts.length ? `${integer}.${decimal}` : integer;
+ },
+ toScoreValue(value) {
+ const number = Number(value);
+ return Number.isFinite(number) ? number.toFixed(2) : value;
+ },
+ prepareSubmitConfig(config) {
+ const payload = JSON.parse(JSON.stringify(config || {}));
+ (payload.categories || []).forEach(category => {
+ (category.items || []).forEach(item => {
+ item.score = this.toScoreValue(item.score);
+ (item.options || []).forEach(option => {
+ option.score = this.toScoreValue(option.score);
+ if (this.normalizeOptionType(item.optionType) === 'score') {
+ option.changeValue = this.toScoreValue(option.changeValue);
+ }
+ });
+ });
+ });
+ return payload;
+ },
hasDuplicateItemName(itemName, currentIndex = -1) {
const name = String(itemName || '').trim();
return this.currentCategory.items.some(
@@ -623,37 +802,55 @@ export default {
this.$message.warning('请输入分值');
return;
}
- if (!this.isPositiveInteger(this.itemForm.score)) {
- this.$message.warning('分值只能输入正整数');
- return;
- }
if (!this.itemForm.options.length) {
this.$message.warning('至少添加1条档位选项');
return;
}
- const invalidOption = this.itemForm.options.some(
- option =>
- !String(option.optionName || '').trim() ||
- option.score === undefined ||
- option.score === null ||
- option.score === ''
- );
- if (invalidOption) {
- this.$message.warning('请完整填写档位选项');
- return;
- }
- if (this.itemForm.options.some(option => !this.isPositiveInteger(option.score))) {
- this.$message.warning('选项分数只能输入正整数');
- return;
+ if (this.itemForm.optionType === 'score') {
+ if (this.itemForm.options.length !== 1) {
+ this.$message.warning('分值类型只能配置1条阶梯配置');
+ return;
+ }
+ if (!this.isAnyNumber(this.itemForm.baseValue)) {
+ this.$message.warning('请输入基准数值');
+ return;
+ }
+ const invalidScoreOption = this.itemForm.options.some(
+ option =>
+ !option.changeType ||
+ !this.isPositiveDecimal(option.changeValue) ||
+ !option.changeUnit ||
+ !option.scoreType ||
+ !this.isPositiveDecimal(option.score)
+ );
+ if (invalidScoreOption) {
+ this.$message.warning('请完整填写基础分值,并确保数值为正数且最多两位小数');
+ return;
+ }
+ } else {
+ const invalidOption = this.itemForm.options.some(
+ option =>
+ !String(option.optionName || '').trim() ||
+ option.score === undefined ||
+ option.score === null ||
+ option.score === ''
+ );
+ if (invalidOption) {
+ this.$message.warning('请完整填写档位选项');
+ return;
+ }
}
const item = JSON.parse(JSON.stringify(this.itemForm));
item.itemName = String(item.itemName || '').trim();
+ item.optionType = this.normalizeOptionType(item.optionType);
+ item.baseValue = item.optionType === 'score' ? String(item.baseValue).trim() : null;
item.options = item.options.map(option => ({
...option,
optionName: String(option.optionName || '').trim(),
- score: Number(option.score),
+ score: this.toScoreValue(option.score),
+ ...(item.optionType === 'score' ? { changeValue: this.toScoreValue(option.changeValue) } : {}),
}));
- item.score = Number(item.score);
+ item.score = this.toScoreValue(item.score);
if (this.currentItemIndex >= 0) {
this.currentCategory.items.splice(this.currentItemIndex, 1, item);
} else {
@@ -669,7 +866,7 @@ export default {
this.currentCategory.items.splice(index, 1);
},
addOption() {
- this.itemForm.options.push({ optionName: '', score: 1 });
+ this.itemForm.options.push(this.emptyOption());
},
removeOption(index) {
this.itemForm.options.splice(index, 1);
@@ -788,8 +985,20 @@ export default {
isBlankValue(value) {
return value === undefined || value === null || value === '';
},
- isPositiveInteger(value) {
- return /^\d+$/.test(String(value)) && Number(value) > 0;
+ isPositiveDecimal(value) {
+ const number = Number(value);
+ console.log(
+ value,
+ Number.isFinite(number),
+ number,
+ Math.abs(number * 100 - Math.round(number * 100))
+ );
+ if (!Number.isFinite(number) || number <= 0) return false;
+ return Math.abs(number * 100 - Math.round(number * 100)) < 1e-8;
+ },
+ isAnyNumber(value) {
+ const text = String(value ?? '').trim();
+ return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(text) && Number.isFinite(Number(text));
},
hasRangeOverlap(list, lowerProp, upperProp) {
const ranges = list
@@ -822,17 +1031,32 @@ export default {
return false;
}
for (const item of category.items) {
- if (!this.isPositiveInteger(item.score)) {
- this.$message.warning(`${item.itemName || '评分项目'}的分值只能为正整数`);
- return false;
- }
if (!item.options || item.options.length === 0) {
this.$message.warning(`${item.itemName}至少存在1条档位选项`);
return false;
}
- if (item.options.some(option => !this.isPositiveInteger(option.score))) {
- this.$message.warning(`${item.itemName || '评分项目'}的选项分数只能为正整数`);
- return false;
+ if (this.normalizeOptionType(item.optionType) === 'score') {
+ if (item.options.length !== 1) {
+ this.$message.warning(`${item.itemName || '评分项目'}的分值类型只能配置1条阶梯配置`);
+ return false;
+ }
+ if (!this.isAnyNumber(item.baseValue)) {
+ this.$message.warning(`${item.itemName || '评分项目'}的基准数值不能为空且必须为数字`);
+ return false;
+ }
+ if (
+ item.options.some(
+ option =>
+ !option.changeType ||
+ !this.isPositiveDecimal(option.changeValue) ||
+ !this.scoreUnitOptions.includes(option.changeUnit) ||
+ !option.scoreType ||
+ !this.isPositiveDecimal(option.score)
+ )
+ ) {
+ this.$message.warning(`${item.itemName || '评分项目'}的基础分值配置不完整`);
+ return false;
+ }
}
}
}
@@ -862,24 +1086,40 @@ export default {
this.$message.warning('发布前请完整填写评分项目名称');
return false;
}
- if (!this.isPositiveInteger(item.score)) {
- this.$message.warning(`${item.itemName || '评分项目'}的分值只能为正整数`);
- return false;
- }
if (!item.options || item.options.length === 0) {
this.$message.warning(`${item.itemName || '评分项目'}至少配置1个档位选项`);
return false;
}
- const invalidOption = item.options.some(
- option => !String(option.optionName || '').trim() || this.isBlankValue(option.score)
- );
- if (invalidOption) {
- this.$message.warning(`${item.itemName || '评分项目'}的档位选项未填写完整`);
- return false;
- }
- if (item.options.some(option => !this.isPositiveInteger(option.score))) {
- this.$message.warning(`${item.itemName || '评分项目'}的选项分数只能为正整数`);
- return false;
+ if (this.normalizeOptionType(item.optionType) === 'score') {
+ if (item.options.length !== 1) {
+ this.$message.warning(`${item.itemName || '评分项目'}的分值类型只能配置1条阶梯配置`);
+ return false;
+ }
+ if (!this.isAnyNumber(item.baseValue)) {
+ this.$message.warning(`${item.itemName || '评分项目'}的基准数值不能为空且必须为数字`);
+ return false;
+ }
+ if (
+ item.options.some(
+ option =>
+ !option.changeType ||
+ !this.isPositiveDecimal(option.changeValue) ||
+ !this.scoreUnitOptions.includes(option.changeUnit) ||
+ !option.scoreType ||
+ !this.isPositiveDecimal(option.score)
+ )
+ ) {
+ this.$message.warning(`${item.itemName || '评分项目'}的基础分值配置不完整`);
+ return false;
+ }
+ } else {
+ const invalidOption = item.options.some(
+ option => !String(option.optionName || '').trim() || this.isBlankValue(option.score)
+ );
+ if (invalidOption) {
+ this.$message.warning(`${item.itemName || '评分项目'}的档位选项未填写完整`);
+ return false;
+ }
}
}
}
@@ -938,7 +1178,7 @@ export default {
saveConfig() {
if (!this.validateConfig()) return;
submit({
- ...this.configForm,
+ ...this.prepareSubmitConfig(this.configForm),
status: 3,
}).then(() => {
this.configBox = false;
@@ -1157,6 +1397,31 @@ export default {
margin-bottom: 14px;
}
+.option-row--score {
+ grid-template-columns: 150px minmax(220px, 1fr) 110px 180px 74px;
+ gap: 16px;
+
+ .option-score {
+ min-width: 0;
+ }
+}
+
+.option-change-type,
+.option-score-type {
+ width: 100%;
+}
+
+.option-unit-select {
+ width: 78px;
+
+ :deep(.el-input__wrapper) {
+ border: 0;
+ border-left: 1px solid #dcdfe6;
+ border-radius: 0;
+ box-shadow: none;
+ }
+}
+
.option-score {
position: relative;
@@ -1278,5 +1543,4 @@ export default {
margin-left: 12px;
}
}
-
diff --git a/src/views/vehicle/customer-archive.vue b/src/views/vehicle/customer-archive.vue
index dda6a42..32f69ae 100644
--- a/src/views/vehicle/customer-archive.vue
+++ b/src/views/vehicle/customer-archive.vue
@@ -1210,20 +1210,32 @@
-
请选择:
-
-
-
+
+ handleScoreValueInput(detail, value)"
+ >
+ {{ getScoreRule(detail)?.changeUnit || '' }}
+
+
+
+ 请选择:
+
+
+
+
@@ -2154,8 +2166,8 @@ export default {
const scoreDetails = this.normalizeChangeFieldValue(score.details);
const details = (Array.isArray(scoreDetails) ? scoreDetails : [])
.map(detail => {
- const category =
- detail.categoryName || categoryNames[this.getScoreDetailCategoryCode(detail)] || '';
+ const categoryCode = this.getScoreDetailCategoryCode(detail);
+ const category = categoryNames[categoryCode] || detail.categoryName || '';
const itemName = detail.itemName || '评分项目';
const option = detail.selectedOption || detail.scoreDescription || '';
const points = [
@@ -3537,7 +3549,10 @@ export default {
quantificationId: score.quantificationId ? String(score.quantificationId) : '',
tempApplyCreditLimit: this.normalizeOptionalAmount(score.tempApplyCreditLimit),
attachments: score.attachments || this.parseAttachments(score.proofAttachments),
- details: score.details || [],
+ details: (score.details || []).map(detail => ({
+ ...detail,
+ scoreInput: this.normalizeScoreInputValue(detail.scoreInput),
+ })),
};
},
normalizeOptionalAmount(value) {
@@ -3836,6 +3851,7 @@ export default {
...item,
categoryCode: category.categoryCode,
categoryName: category.categoryName,
+ scoreInput: this.normalizeScoreInputValue(item.scoreInput),
optionsJson:
item.optionsJson ||
JSON.stringify(
@@ -3843,6 +3859,10 @@ export default {
label: option.label || option.optionName || option.value,
value: option.value || option.optionName || option.label,
score: option.score,
+ changeType: option.changeType,
+ changeValue: option.changeValue,
+ changeUnit: option.changeUnit,
+ scoreType: option.scoreType,
}))
),
}))
@@ -3862,9 +3882,16 @@ export default {
...detail,
categoryCode: detail.categoryCode || category?.categoryCode || '',
categoryName: detail.categoryName || category?.categoryName || '',
+ scoreInput: this.normalizeScoreInputValue(detail.scoreInput),
};
});
},
+ normalizeScoreInputValue(value) {
+ if (value === undefined || value === null || value === '' || value === -1 || value === '-1') {
+ return '';
+ }
+ return String(value);
+ },
getScoreDetailCategoryCode(detail = {}) {
const rawCode = String(
detail.categoryCode ||
@@ -3916,12 +3943,24 @@ export default {
parseScoreOptions(value) {
if (!value) return [];
const normalize = list =>
- list.map(item => ({
- ...item,
- label: item.label || item.optionName || item.value,
- value: item.value || item.optionName || item.label,
- score: item.score,
- }));
+ list.map(item => {
+ const isScoreOption = item.changeType || item.changeValue || item.changeUnit;
+ const changeLabel = item.changeType === 'decrease' ? '每减少' : '每增加';
+ const scoreLabel = item.scoreType === 'subtract' ? '减' : '加';
+ const generatedLabel = isScoreOption
+ ? `${changeLabel}${item.changeValue || ''}${item.changeUnit || ''}${scoreLabel}${item.score || ''}分`
+ : '';
+ const label = item.label || item.optionName || item.value || generatedLabel;
+ return {
+ ...item,
+ label,
+ value: item.value || item.optionName || item.label || generatedLabel,
+ score:
+ item.scoreType === 'subtract' && item.score !== undefined
+ ? -Math.abs(Number(item.score))
+ : item.score,
+ };
+ });
if (Array.isArray(value)) return normalize(value);
try {
const options = JSON.parse(value);
@@ -3930,6 +3969,50 @@ export default {
return [];
}
},
+ isScoreTypeDetail(detail = {}) {
+ return String(detail.optionType || '').toLowerCase() === 'score';
+ },
+ getScoreRule(detail = {}) {
+ return this.parseScoreOptions(detail.optionsJson)[0] || null;
+ },
+ handleScoreValueInput(detail, value) {
+ const raw = String(value ?? '')
+ .replace(/[^\d.-]/g, '')
+ .replace(/(?!^)-/g, '')
+ .replace(/(\..*)\./g, '$1');
+ const sign = raw.startsWith('-') ? '-' : '';
+ const unsigned = raw.replace(/^-/, '');
+ const hasDecimal = unsigned.includes('.');
+ const [integerPart, decimalPart = ''] = unsigned.split('.');
+ const normalized = `${sign}${integerPart}${hasDecimal ? `.${decimalPart.slice(0, 2)}` : ''}`;
+ detail.scoreInput = normalized;
+ const rule = this.getScoreRule(detail);
+ if (!normalized || normalized === '-' || normalized === '.') {
+ detail.selfScore = '';
+ this.calculateScore(this.currentScore);
+ return;
+ }
+ const base = Number(detail.baseValue);
+ const input = Number(normalized);
+ if (!rule || !Number.isFinite(base) || !Number.isFinite(input)) {
+ detail.selfScore = '';
+ this.calculateScore(this.currentScore);
+ return;
+ }
+ const increase = rule.changeType !== 'decrease';
+ const valid = increase ? input >= base : input <= base;
+ if (!valid) {
+ detail.selfScore = '';
+ this.$message.warning(`输入值${increase ? '不能小于' : '不能大于'}基准数值${base}`);
+ this.calculateScore(this.currentScore);
+ return;
+ }
+ const distance = increase ? input - base : base - input;
+ const stepScore = Math.abs(Number(rule.score || 0));
+ const signedStep = rule.scoreType === 'subtract' ? -stepScore : stepScore;
+ detail.selfScore = Number((Number(detail.score || 0) + distance * signedStep).toFixed(2));
+ this.calculateScore(this.currentScore);
+ },
scoreOptionChange(detail, optionValue) {
const option = this.parseScoreOptions(detail.optionsJson).find(
item => item.value === optionValue
@@ -4076,9 +4159,13 @@ export default {
return score;
},
hasIncompleteBasicScoreDetail(details = []) {
- return this.getScoreCategoryDetailsByList(details, 'basic').some(
- item => !item.selectedOption
- );
+ return this.getScoreCategoryDetailsByList(details, 'basic').some(item => {
+ if (this.isScoreTypeDetail(item)) {
+ const input = String(item.scoreInput ?? '').trim();
+ return !input || !Number.isFinite(Number(input)) || item.selfScore === '';
+ }
+ return !item.selectedOption;
+ });
},
finishScore(score) {
if (!score.details || !score.details.length) {
@@ -4086,7 +4173,7 @@ export default {
return;
}
if (this.hasIncompleteBasicScoreDetail(score.details)) {
- this.$message.warning('请完整选择基础得分项评分明细选项');
+ this.$message.warning('请完整填写基础得分项评分明细');
return;
}
this.calculateScore(score);
@@ -4148,6 +4235,17 @@ export default {
this.$message.warning('当前评分量化表未配置评分项目');
return false;
}
+ const invalidScoreInput = this.currentScore.details.find(item => {
+ if (!this.isScoreTypeDetail(item)) return false;
+ const input = String(item.scoreInput ?? '').trim();
+ return input !== '' && item.selfScore === '';
+ });
+ if (invalidScoreInput) {
+ this.$message.warning(
+ `${invalidScoreInput.itemName || '评分项目'}的输入值不符合基准数值限制,请重新填写`
+ );
+ return false;
+ }
const overMaxReviewScore = this.currentScore.details.find(item => {
if (
item.reviewScore === undefined ||
@@ -4169,7 +4267,7 @@ export default {
return false;
}
if (requireComplete && this.hasIncompleteBasicScoreDetail(this.currentScore.details)) {
- this.$message.warning('请完整选择基础得分项评分明细选项');
+ this.$message.warning('请完整填写基础得分项评分明细');
return false;
}
return true;