Files
tms-erp-web/src/views/vehicle/credit-score-quantification.vue
赵忠林 7fb8113c8e feat(core): 统一全站时间列加排序并优化司机管理显示
- 所有含时间的列表列(创建时间、更新时间、起止日期等)均增加排序功能,兼顾Avue option和手写el-table-column
- 司机管理「姓名身份证号」列拆分为独立的「姓名」「身份证号」两列,分别使用插槽渲染
- 修复车辆/船舶/司机管理「所属组织」列前导空格问题,value 使用无缩进原始标签
- 调整司机管理页到期筛选标签位置,移至搜索菜单行并优化样式,删除过渡占位筛选字段
- 增加组织名称列自定义插槽,去除已入库旧数据的前导空白
- 更新相关样式,删除无用样式代码,增加表格列排序样式支持
2026-08-07 15:11:08 +08:00

1233 lines
37 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<basic-container>
<avue-crud
:option="option"
:table-loading="loading"
:data="data"
v-model:page="page"
v-model="form"
ref="crud"
:permission="permissionList"
@search-change="searchChange"
@search-reset="searchReset"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@refresh-change="refreshChange"
@on-load="onLoad"
>
<template #menu-left>
<el-button
type="primary"
icon="el-icon-plus"
v-if="hasPermission('credit_score_quantification_add')"
@click="openConfig()"
>新增
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
plain
v-if="hasPermission('credit_score_quantification_delete')"
@click="handleDelete"
>批量删除
</el-button>
</template>
<template #name="{ row }">
<el-button type="primary" link @click="openConfig(row, true)">
{{ row.name }}
</el-button>
</template>
<template #status="{ row }">
<el-tag :type="statusMap[row.status].type">
{{ statusMap[row.status].label }}
</el-tag>
</template>
<template #menu="{ row }">
<el-link
type="primary"
v-if="hasPermission('credit_score_quantification_status') && row.status === 1"
@click="handleStatus(row)"
>
停用
</el-link>
<el-link
type="primary"
v-if="hasPermission('credit_score_quantification_publish') && row.status !== 1"
@click="handlePublish(row)"
>
发布
</el-link>
<el-link
type="primary"
v-if="hasPermission('credit_score_quantification_edit') && row.status !== 1"
@click="openConfig(row)"
>
编辑
</el-link>
<el-link
type="danger"
v-if="hasPermission('credit_score_quantification_delete')"
@click="rowDel(row)"
>
删除
</el-link>
</template>
</avue-crud>
<el-dialog
:title="readonly ? '查看评分量化表' : configForm.id ? '编辑评分量化表' : '新增评分量化表'"
append-to-body
v-model="configBox"
width="88%"
class="score-dialog"
@closed="resetConfig"
>
<div class="score-config">
<div class="section-title dialog-section-title">评分量化表</div>
<el-form
:model="configForm"
label-position="right"
label-width="auto"
class="base-form"
:disabled="readonly"
>
<el-form-item label="评定表名称" required>
<el-input
v-model="configForm.name"
class="score-config__name-input"
maxlength="30"
show-word-limit
/>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="configForm.remark"
type="textarea"
:rows="1"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
<div class="section-head">
<div class="section-title dialog-section-title">评分表</div>
</div>
<el-table :data="configForm.categories" border class="config-table">
<el-table-column label="序号" type="index" width="90" align="center" />
<el-table-column label="评分类目" prop="categoryName" min-width="180" />
<el-table-column label="评分项目数" width="140" align="center">
<template #default="{ row }">{{ row.items.length }}</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center">
<template #default="{ row }">
<el-link type="primary" @click="openCategory(row)">编辑</el-link>
</template>
</el-table-column>
</el-table>
<div class="section-head standard-head">
<div class="section-title dialog-section-title">评估标准</div>
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="openStandard()">
添加
</el-button>
</div>
<el-table :data="configForm.standards" border class="config-table">
<el-table-column label="序号" type="index" width="90" align="center" />
<el-table-column label="信用等级" prop="creditLevel" width="120" align="center" />
<el-table-column label="标准说明" prop="standardDescription" min-width="320" />
<el-table-column label="操作" width="160" align="center">
<template #default="{ row, $index }">
<el-link type="primary" @click="openStandard(row, $index)">编辑</el-link>
<el-link type="danger" v-if="!readonly" @click="removeStandard($index)"
>删除</el-link
>
</template>
</el-table-column>
</el-table>
</div>
<template #footer>
<el-button @click="configBox = false">{{ readonly ? '关闭' : '取消' }}</el-button>
<el-button type="primary" v-if="!readonly" @click="saveConfig">保存</el-button>
</template>
</el-dialog>
<el-dialog
title="评分类目详情"
append-to-body
v-model="categoryBox"
width="82%"
class="score-dialog category-dialog"
:close-on-press-escape="false"
>
<div class="category-detail">
<div class="category-line">评分类目{{ currentCategory.categoryName }}</div>
<div class="section-head">
<span></span>
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="openItem()">
添加
</el-button>
</div>
<el-table :data="currentCategory.items" border class="config-table">
<el-table-column label="序号" type="index" width="90" align="center" />
<el-table-column label="评分项目" prop="itemName" width="180" />
<el-table-column label="得分说明" prop="scoreDescription" min-width="260" />
<el-table-column label="选项数" width="100" align="center">
<template #default="{ row }">{{ row.options.length }}</template>
</el-table-column>
<el-table-column label="操作" width="160" align="center">
<template #default="{ row, $index }">
<el-link type="primary" @click="openItem(row, $index)">编辑</el-link>
<el-link
type="danger"
v-if="!readonly"
:disabled="currentCategory.items.length <= 1"
@click="removeItem($index)"
>
删除
</el-link>
</template>
</el-table-column>
</el-table>
</div>
<template #footer>
<el-button type="primary" @click="categoryBox = false">保存</el-button>
</template>
</el-dialog>
<el-dialog
title="评分项目详情"
append-to-body
v-model="itemBox"
width="86%"
class="score-item-dialog"
>
<el-form
:model="itemForm"
label-position="right"
label-width="112px"
:disabled="readonly"
class="score-item-form"
>
<div class="score-item-form__top">
<el-form-item label="评分项目" required>
<el-input v-model="itemForm.itemName" maxlength="20" />
</el-form-item>
<el-form-item label="分值" required>
<el-input-number v-model="itemForm.score" :min="0" :precision="2" :controls="false" />
</el-form-item>
</div>
<el-form-item label="得分说明">
<el-input v-model="itemForm.scoreDescription" maxlength="300" />
</el-form-item>
<el-form-item label="选项描述">
<el-input v-model="itemForm.optionDescription" maxlength="100" />
</el-form-item>
<el-form-item label="选项" required>
<div class="option-editor">
<div class="option-row" v-for="(option, index) in itemForm.options" :key="index">
<el-input v-model="option.optionName" maxlength="100" />
<div class="option-score">
<el-input-number v-model="option.score" :min="0" :precision="2" :controls="false" />
<span class="score-unit"></span>
</div>
<el-link
type="primary"
class="option-delete"
v-if="!readonly && itemForm.options.length > 1"
@click="removeOption(index)"
>
删除
</el-link>
</div>
<div class="option-add">
<el-link type="primary" v-if="!readonly" @click="addOption">
+添加选项
</el-link>
</div>
</div>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="itemBox = false">取消</el-button>
<el-button type="primary" v-if="!readonly" @click="saveItem">保存</el-button>
</template>
</el-dialog>
<el-dialog
title="评估标准设置"
append-to-body
v-model="standardBox"
width="76%"
:close-on-press-escape="false"
>
<el-form :model="standardForm" label-position="right" label-width="auto" :disabled="readonly">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="信用等级" required>
<el-select
v-model="standardForm.creditLevel"
placeholder="请选择信用等级"
clearable
@change="handleStandardCreditLevelChange"
>
<el-option
v-for="item in creditLevelOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="得分率(%)" required>
<div class="range-input standard-range-input">
<el-input
v-model="standardForm.scoreRateLower"
inputmode="numeric"
@input="value => handleStandardNumberInput('scoreRateLower', value, 0, 100)"
/>
<span></span>
<el-input
v-model="standardForm.scoreRateUpper"
inputmode="numeric"
@input="value => handleStandardNumberInput('scoreRateUpper', value, 0, 100)"
/>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="得分率每增加" required>
<el-input
v-model="standardForm.scoreRateIncrease"
inputmode="numeric"
@input="value => handleStandardNumberInput('scoreRateIncrease', value)"
>
<template #suffix>%</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="额度增加" required>
<el-input
v-model="standardForm.creditLimitIncrease"
inputmode="decimal"
@input="value => handleStandardNumberInput('creditLimitIncrease', value, 2)"
>
<template #suffix>万元</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="最大资金使用额度(万元)" required>
<div class="range-input standard-range-input">
<el-input
v-model="standardForm.creditLimitLower"
inputmode="decimal"
@input="value => handleStandardNumberInput('creditLimitLower', value, 2)"
/>
<span></span>
<el-input
v-model="standardForm.creditLimitUpper"
inputmode="decimal"
@input="value => handleStandardNumberInput('creditLimitUpper', value, 2)"
/>
</div>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="标准说明">
<el-input
v-model="standardForm.standardDescription"
type="textarea"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="standardBox = false">取消</el-button>
<el-button type="primary" v-if="!readonly" @click="saveStandard">保存</el-button>
</template>
</el-dialog>
</basic-container>
</template>
<script>
import {
getList,
getDetail,
submit,
remove,
publish,
changeStatus,
} from '@/api/vehicle/credit-score-quantification';
import { normalizeSearchRangeParams } from '@/utils/search-range';
import { mapGetters } from 'vuex';
const createTimeRangeMap = {
createTimeRange: ['createTimeStart', 'createTimeEnd'],
};
export default {
data() {
return {
form: {},
query: {},
loading: true,
data: [],
page: {
pageSize: 10,
currentPage: 1,
total: 0,
},
selectionList: [],
configBox: false,
categoryBox: false,
itemBox: false,
standardBox: false,
readonly: false,
configForm: this.emptyConfig(),
currentCategory: { items: [] },
currentItemIndex: -1,
currentStandardIndex: -1,
itemForm: this.emptyItem(),
standardForm: this.emptyStandard(),
statusMap: {
1: { label: '正常', type: 'success' },
2: { label: '停用', type: 'info' },
3: { label: '草稿', type: 'warning' },
},
creditLevelOptions: ['A', 'B', 'C', 'D', 'E', 'F'].map(item => ({
label: item,
value: item,
})),
option: {
height: 'auto',
calcHeight: 32,
dialogWidth: 760,
labelPosition: 'right',
tip: false,
searchShow: true,
searchMenuSpan: 24,
searchIcon: true,
searchIndex: 4,
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
indexWidth: 90,
addBtn: false,
viewBtn: false,
delBtn: false,
editBtn: false,
selection: true,
dialogClickModal: false,
menuWidth: 180,
column: [
{
label: '评分表名称',
prop: 'name',
slot: true,
minWidth: 180,
search: true,
maxlength: 30,
rules: [{ required: true, message: '请输入评定表名称', trigger: 'blur' }],
},
{
label: '创建人',
prop: 'createUserName',
minWidth: 120,
display: false,
},
{
label: '创建时间',
prop: 'createTime',
sortable: true,
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
minWidth: 160,
display: false,
},
{
label: '状态',
prop: 'status',
type: 'select',
search: true,
slot: true,
dataType: 'number',
searchValue: '',
dicData: [
{ label: '全部', value: '' },
{ label: '正常', value: 1 },
{ label: '停用', value: 2 },
],
clearable: true,
},
{
label: '创建时间',
prop: 'createTimeRange',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
searchRange: true,
hide: true,
display: false,
},
],
},
};
},
computed: {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
},
permissionList() {
return {
addBtn: false,
};
},
ids() {
let ids = [];
this.selectionList.forEach(ele => {
ids.push(ele.id);
});
return ids.join(',');
},
},
methods: {
hasPermission(code) {
return this.isAdmin || this.validData(this.permission[code], false);
},
emptyConfig() {
return {
name: '',
remark: '',
standardDescription: '',
status: 3,
categories: this.defaultCategories(),
standards: [],
};
},
defaultCategories() {
return [
{ categoryCode: 'basic', categoryName: '基础得分项', sort: 1, items: [] },
{ categoryCode: 'plus', categoryName: '加分项目', sort: 2, items: [] },
{ categoryCode: 'minus', categoryName: '减分项目', sort: 3, items: [] },
];
},
emptyItem() {
return {
itemName: '',
score: 0,
scoreDescription: '',
optionDescription: '',
options: [{ optionName: '', score: 0 }],
};
},
emptyStandard() {
return {
creditLevel: '',
scoreRateLower: undefined,
scoreRateUpper: undefined,
scoreRateIncrease: undefined,
creditLimitLower: undefined,
creditLimitUpper: undefined,
creditLimitIncrease: undefined,
standardDescription: '',
};
},
normalizeDetail(detail) {
const categories = this.defaultCategories();
const map = {};
(detail.categories || []).forEach(category => {
map[category.categoryCode] = {
...category,
items: (category.items || []).map(item => ({
...item,
options:
item.options && item.options.length ? item.options : [{ optionName: '', score: 0 }],
})),
};
});
return {
...detail,
categories: categories.map(category => map[category.categoryCode] || category),
standards: detail.standards || [],
};
},
openConfig(row, readonly = false) {
this.readonly = readonly;
if (row && row.id) {
getDetail(row.id).then(res => {
this.configForm = this.normalizeDetail(res.data.data);
this.configBox = true;
});
return;
}
this.configForm = this.emptyConfig();
if (row && row.name) {
this.configForm.name = row.name;
}
this.configBox = true;
},
resetConfig() {
this.readonly = false;
this.configForm = this.emptyConfig();
},
openCategory(category) {
this.currentCategory = category;
this.categoryBox = true;
},
openItem(row, index = -1) {
this.currentItemIndex = index;
this.itemForm = row
? { ...this.emptyItem(), ...JSON.parse(JSON.stringify(row)) }
: this.emptyItem();
this.itemBox = true;
},
hasDuplicateItemName(itemName, currentIndex = -1) {
const name = String(itemName || '').trim();
return this.currentCategory.items.some(
(item, index) => index !== currentIndex && String(item.itemName || '').trim() === name
);
},
saveItem() {
if (!this.itemForm.itemName || !this.itemForm.itemName.trim()) {
this.$message.warning('请输入评分项目');
return;
}
if (this.hasDuplicateItemName(this.itemForm.itemName, this.currentItemIndex)) {
this.$message.warning('评分项目名称不能重复');
return;
}
if (
this.itemForm.score === undefined ||
this.itemForm.score === null ||
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;
}
const item = JSON.parse(JSON.stringify(this.itemForm));
item.itemName = String(item.itemName || '').trim();
item.options = item.options.map(option => ({
...option,
optionName: String(option.optionName || '').trim(),
}));
if (this.currentItemIndex >= 0) {
this.currentCategory.items.splice(this.currentItemIndex, 1, item);
} else {
this.currentCategory.items.push(item);
}
this.itemBox = false;
},
removeItem(index) {
if (this.currentCategory.items.length <= 1) {
this.$message.warning('评分类目详情至少保留1条评分项目');
return;
}
this.currentCategory.items.splice(index, 1);
},
addOption() {
this.itemForm.options.push({ optionName: '', score: 0 });
},
removeOption(index) {
this.itemForm.options.splice(index, 1);
},
openStandard(row, index = -1) {
this.currentStandardIndex = index;
this.standardForm = row ? JSON.parse(JSON.stringify(row)) : this.emptyStandard();
this.standardBox = true;
},
hasDuplicateCreditLevel(creditLevel, currentIndex = -1) {
const level = String(creditLevel || '').trim();
return this.configForm.standards.some(
(item, index) => index !== currentIndex && String(item.creditLevel || '').trim() === level
);
},
handleStandardCreditLevelChange(value) {
if (!value) return;
if (this.hasDuplicateCreditLevel(value, this.currentStandardIndex)) {
this.$message.warning('信用等级不能重复');
this.standardForm.creditLevel = '';
}
},
handleStandardNumberInput(field, value, precision = 0, max) {
let normalized = String(value || '').replace(precision > 0 ? /[^\d.]/g : /\D/g, '');
if (precision > 0) {
const [integer = '', ...decimalParts] = normalized.split('.');
const decimal = decimalParts.join('').slice(0, precision);
normalized = decimalParts.length ? `${integer}.${decimal}` : integer;
}
if (max !== undefined && normalized !== '' && Number(normalized) > max) {
normalized = String(max);
}
this.standardForm[field] = normalized;
},
isValidCreditLevel(creditLevel) {
return this.creditLevelOptions.some(item => item.value === String(creditLevel || '').trim());
},
saveStandard() {
if (!this.standardForm.creditLevel) {
this.$message.warning('请选择信用等级');
return;
}
if (!this.isValidCreditLevel(this.standardForm.creditLevel)) {
this.$message.warning('信用等级只能选择A/B/C/D/E/F');
return;
}
if (this.hasDuplicateCreditLevel(this.standardForm.creditLevel, this.currentStandardIndex)) {
this.$message.warning('信用等级不能重复');
return;
}
if (
[
this.standardForm.scoreRateLower,
this.standardForm.scoreRateUpper,
this.standardForm.scoreRateIncrease,
this.standardForm.creditLimitLower,
this.standardForm.creditLimitUpper,
this.standardForm.creditLimitIncrease,
].some(value => value === undefined || value === null || value === '')
) {
this.$message.warning('请完整填写评估标准必填项');
return;
}
if (Number(this.standardForm.scoreRateLower) >= Number(this.standardForm.scoreRateUpper)) {
this.$message.warning('得分率下限必须小于上限');
return;
}
if (
Number(this.standardForm.creditLimitLower) >= Number(this.standardForm.creditLimitUpper)
) {
this.$message.warning('授信额度下限必须小于上限');
return;
}
const standard = JSON.parse(JSON.stringify(this.standardForm));
standard.creditLevel = String(standard.creditLevel || '').trim();
[
'scoreRateLower',
'scoreRateUpper',
'scoreRateIncrease',
'creditLimitLower',
'creditLimitUpper',
'creditLimitIncrease',
].forEach(field => {
standard[field] = Number(standard[field]);
});
const nextStandards = JSON.parse(JSON.stringify(this.configForm.standards));
if (this.currentStandardIndex >= 0) {
nextStandards.splice(this.currentStandardIndex, 1, standard);
} else {
nextStandards.push(standard);
}
if (this.hasRangeOverlap(nextStandards, 'scoreRateLower', 'scoreRateUpper')) {
this.$message.warning('评估标准中的得分率区间不能重叠');
return;
}
if (this.hasRangeOverlap(nextStandards, 'creditLimitLower', 'creditLimitUpper')) {
this.$message.warning('评估标准中的授信额度区间不能重叠');
return;
}
if (this.currentStandardIndex >= 0) {
this.configForm.standards.splice(this.currentStandardIndex, 1, standard);
} else {
this.configForm.standards.push(standard);
}
this.standardBox = false;
},
removeStandard(index) {
this.$confirm('确定删除该评估标准?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.configForm.standards.splice(index, 1);
});
},
isBlankValue(value) {
return value === undefined || value === null || value === '';
},
hasRangeOverlap(list, lowerProp, upperProp) {
const ranges = list
.map(item => ({
lower: Number(item[lowerProp]),
upper: Number(item[upperProp]),
}))
.sort((a, b) => a.lower - b.lower);
return ranges.some((range, index) => {
const next = ranges[index + 1];
return next && range.upper > next.lower;
});
},
validateConfig() {
if (!this.configForm.name || !this.configForm.name.trim()) {
this.$message.warning('请输入评定表名称');
return false;
}
const basic = this.configForm.categories.find(item => item.categoryCode === 'basic');
if (!basic || basic.items.length === 0) {
this.$message.warning('至少配置1个基础得分项目');
return false;
}
for (const category of this.configForm.categories) {
const itemNames = (category.items || [])
.map(item => String(item.itemName || '').trim())
.filter(Boolean);
if (new Set(itemNames).size !== itemNames.length) {
this.$message.warning(`${category.categoryName}的评分项目名称不能重复`);
return false;
}
for (const item of category.items) {
if (!item.options || item.options.length === 0) {
this.$message.warning(`${item.itemName}至少存在1条档位选项`);
return false;
}
}
}
const creditLevels = this.configForm.standards
.map(item => String(item.creditLevel || '').trim())
.filter(Boolean);
if (creditLevels.some(level => !this.isValidCreditLevel(level))) {
this.$message.warning('评估标准中的信用等级只能选择A/B/C/D/E/F');
return false;
}
if (new Set(creditLevels).size !== creditLevels.length) {
this.$message.warning('评估标准中的信用等级不能重复');
return false;
}
return true;
},
validatePublishConfig(config) {
const detail = this.normalizeDetail(config || {});
const basic = detail.categories.find(item => item.categoryCode === 'basic');
if (!basic || !basic.items || basic.items.length === 0) {
this.$message.warning('发布前至少配置1条基础得分项目');
return false;
}
for (const category of detail.categories) {
for (const item of category.items || []) {
if (!String(item.itemName || '').trim()) {
this.$message.warning('发布前请完整填写评分项目名称');
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;
}
}
}
const standards = detail.standards || [];
if (standards.length === 0) {
this.$message.warning('发布前至少配置1条信用等级评估标准');
return false;
}
const requiredFields = [
'scoreRateLower',
'scoreRateUpper',
'scoreRateIncrease',
'creditLimitLower',
'creditLimitUpper',
'creditLimitIncrease',
];
for (const standard of standards) {
if (!standard.creditLevel) {
this.$message.warning('评估标准中的信用等级不能为空');
return false;
}
if (!this.isValidCreditLevel(standard.creditLevel)) {
this.$message.warning('评估标准中的信用等级只能选择A/B/C/D/E/F');
return false;
}
if (requiredFields.some(field => this.isBlankValue(standard[field]))) {
this.$message.warning(`${standard.creditLevel}信用等级评估标准必填项未填写完整`);
return false;
}
if (Number(standard.scoreRateLower) >= Number(standard.scoreRateUpper)) {
this.$message.warning(`${standard.creditLevel}信用等级得分率下限必须小于上限`);
return false;
}
if (Number(standard.creditLimitLower) >= Number(standard.creditLimitUpper)) {
this.$message.warning(`${standard.creditLevel}信用等级授信额度下限必须小于上限`);
return false;
}
}
const creditLevels = standards.map(item => String(item.creditLevel || '').trim());
if (new Set(creditLevels).size !== creditLevels.length) {
this.$message.warning('评估标准中的信用等级不能重复');
return false;
}
if (this.hasRangeOverlap(standards, 'scoreRateLower', 'scoreRateUpper')) {
this.$message.warning('评估标准中的得分率区间不能重叠');
return false;
}
if (this.hasRangeOverlap(standards, 'creditLimitLower', 'creditLimitUpper')) {
this.$message.warning('评估标准中的授信额度区间不能重叠');
return false;
}
return true;
},
saveConfig() {
if (!this.validateConfig()) return;
submit({
...this.configForm,
status: 3,
}).then(() => {
this.configBox = false;
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
});
},
rowDel(row) {
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => remove(row.id))
.then(() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
});
},
handleDelete() {
if (this.selectionList.length === 0) {
this.$message.warning('请选择至少一条数据');
return;
}
this.$confirm('确定删除所选评分量化表?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => remove(this.ids))
.then(() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
this.$refs.crud.toggleSelection();
});
},
handlePublish(row) {
this.$confirm('发布前将校验所有评分项目、档位选项和评估标准,是否继续?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => getDetail(row.id))
.then(res => {
const detail = this.normalizeDetail(res.data.data || {});
if (!this.validatePublishConfig(detail)) {
return false;
}
return publish(row.id).then(() => true);
})
.then(published => {
if (!published) return;
this.onLoad(this.page);
this.$message({ type: 'success', message: '发布成功!' });
});
},
handleStatus(row) {
const nextStatus = row.status === 1 ? 2 : 1;
const actionName = nextStatus === 1 ? '启用' : '停用';
this.$confirm(`是否确认${actionName}该评分量化表?`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => changeStatus(row.id, nextStatus))
.then(() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
});
},
searchReset() {
this.query = {};
this.onLoad(this.page);
},
searchChange(params, done) {
this.query = this.buildQuery(params);
this.page.currentPage = 1;
this.onLoad(this.page);
done();
},
selectionChange(list) {
this.selectionList = list;
},
currentChange(currentPage) {
this.page.currentPage = currentPage;
},
sizeChange(pageSize) {
this.page.pageSize = pageSize;
},
refreshChange() {
this.onLoad(this.page, this.query);
},
onLoad(page, params = {}) {
this.loading = true;
getList(page.currentPage, page.pageSize, this.buildQuery(params)).then(res => {
const data = res.data.data;
this.page.total = data.total;
this.data = data.records;
this.loading = false;
});
},
buildQuery(params = {}) {
return normalizeSearchRangeParams({ ...params, ...this.query }, createTimeRangeMap);
},
},
};
</script>
<style lang="scss" scoped>
.score-config {
padding: 4px 12px 12px;
&__name-input {
width: min(420px, 100%);
}
}
.base-form {
max-width: 60%;
padding: 24px;
}
.section-head {
display: flex;
align-items: center;
justify-content: space-between;
margin: 24px 0 12px;
.section-title {
margin: 0;
}
}
.section-title {
margin: 18px 0 12px;
font-size: 20px;
font-weight: 700;
line-height: 20px;
color: #303133;
}
.standard-head {
margin-top: 36px;
}
.config-table {
width: 100%;
}
.category-line {
margin: 0 0 24px;
font-size: 16px;
font-weight: 400;
color: #606266;
}
:deep(.category-dialog .el-dialog__title) {
font-weight: 700;
}
.score-item-form {
width: 100%;
max-width: 1400px;
margin: 0 auto;
padding: 4px 0 6px;
:deep(.el-form-item) {
margin-bottom: 18px;
}
:deep(.el-form-item__label) {
padding-right: 10px;
color: #606266;
font-size: 14px;
line-height: 32px;
}
:deep(.el-input),
:deep(.el-input-number) {
width: 100%;
}
:deep(.el-input__wrapper),
:deep(.el-input-number .el-input__wrapper) {
min-height: 32px;
border-radius: 0;
box-shadow: 0 0 0 1px #dcdfe6 inset;
}
:deep(.el-input__inner) {
height: 32px;
color: #303133;
font-size: 14px;
font-weight: 400;
text-align: center;
}
&__top {
display: grid;
grid-template-columns: 360px 360px;
column-gap: 112px;
:deep(.el-form-item) {
margin-bottom: 18px;
}
}
}
.option-editor {
width: 100%;
}
.option-row {
display: grid;
grid-template-columns: minmax(420px, 1fr) 315px 74px;
gap: 54px;
align-items: center;
margin-bottom: 14px;
}
.option-score {
position: relative;
.score-unit {
position: absolute;
top: 50%;
right: 12px;
z-index: 1;
margin: 0;
color: #303133;
font-size: 14px;
line-height: 1;
transform: translateY(-50%);
}
:deep(.el-input__inner) {
padding-right: 42px;
}
}
.option-delete {
justify-self: start;
min-height: 32px;
padding: 0;
font-size: 14px;
}
.option-add {
display: flex;
justify-content: center;
margin-top: 6px;
:deep(.el-button) {
min-height: 32px;
font-size: 16px;
font-weight: 600;
}
}
.range-input {
display: flex;
align-items: center;
gap: 10px;
}
.standard-range-input {
width: 100%;
min-width: 0;
:deep(.el-input) {
flex: 1;
width: 0;
min-width: 0;
}
}
.score-unit {
margin-left: 8px;
color: #606266;
}
:deep(.score-dialog .el-dialog__body) {
max-height: 70vh;
overflow: auto;
}
:deep(.score-item-dialog) {
.el-dialog__header {
padding: 20px 24px 10px;
}
.el-dialog__title {
color: #303133;
font-size: 20px;
font-weight: 700;
line-height: 1;
}
.el-dialog__body {
padding: 12px 24px 14px;
}
.el-dialog__footer {
padding: 12px 24px 20px;
}
.dialog-footer,
.el-dialog__footer {
text-align: right;
}
.el-dialog__footer .el-button {
min-width: 88px;
min-height: 32px;
border-radius: 4px;
font-size: 14px;
font-weight: 400;
}
.el-dialog__footer .el-button + .el-button {
margin-left: 12px;
}
}
</style>