feat(aihr): align figma pages for release
This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<div class="rubric-page">
|
||||
<div class="page-heading">
|
||||
<div>
|
||||
<h1>Rubric 评分规则</h1>
|
||||
<p>维护评分维度、权重、版本和生效状态</p>
|
||||
</div>
|
||||
<el-button type="danger" :icon="Plus" @click="createVersion">新建版本</el-button>
|
||||
</div>
|
||||
|
||||
<div class="rubric-layout">
|
||||
<section class="version-card">
|
||||
<h2>版本列表</h2>
|
||||
<button
|
||||
v-for="item in rubrics"
|
||||
:key="item.code"
|
||||
class="version-item"
|
||||
:class="{ active: item.code === selectedCode }"
|
||||
@click="selectRubric(item)"
|
||||
>
|
||||
<strong>{{ item.name }} {{ item.version }}</strong>
|
||||
<span>{{ item.enabled ? '当前生效' : '历史版本' }} · {{ item.dimensions.length }} 维度</span>
|
||||
</button>
|
||||
<el-empty v-if="!loading && rubrics.length === 0" description="暂无评分规则" />
|
||||
</section>
|
||||
|
||||
<section v-if="form" class="editor-card">
|
||||
<div class="editor-heading">
|
||||
<div>
|
||||
<h2>{{ form.name }} {{ form.version }}</h2>
|
||||
<p>{{ selectedRubric?.scenarioName || form.scenarioCode }} · {{ form.description || '维护评分维度与权重' }}</p>
|
||||
</div>
|
||||
<el-tag :type="form.enabled ? 'success' : 'info'">{{ form.enabled ? '已启用' : '已停用' }}</el-tag>
|
||||
</div>
|
||||
|
||||
<h3>评分维度与权重</h3>
|
||||
<div v-for="dimension in form.dimensions" :key="dimension.code" class="dimension-row">
|
||||
<div class="dimension-label">
|
||||
<strong>{{ dimension.name }}</strong>
|
||||
<span>{{ dimension.description || '请补充该维度的评分说明' }}</span>
|
||||
</div>
|
||||
<div class="dimension-control">
|
||||
<span>{{ Math.round(dimension.weight * 100) }}% 权重</span>
|
||||
<el-slider v-model="dimension.weight" :min="0" :max="1" :step="0.05" @change="normalizeWeights" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="band-card">
|
||||
<strong>评分档位</strong>
|
||||
<span>90–100 优秀 · 75–89 良好 · 60–74 待提升 · <60 不合格</span>
|
||||
</div>
|
||||
|
||||
<div class="editor-actions">
|
||||
<el-button @click="copyVersion">复制版本</el-button>
|
||||
<el-button @click="selectedRubric && toggleEnabled(selectedRubric)">{{ form.enabled ? '停用版本' : '启用版本' }}</el-button>
|
||||
<el-button type="danger" :loading="saving" @click="saveVersion">保存并发布</el-button>
|
||||
</div>
|
||||
</section>
|
||||
<el-empty v-else-if="!loading" description="请选择一个评分规则" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Plus } from '@element-plus/icons-vue';
|
||||
import {
|
||||
listPracticeRubrics,
|
||||
savePracticeRubric,
|
||||
updatePracticeRubricEnabled,
|
||||
type PracticeRubricAdmin,
|
||||
type PracticeRubricSaveRequest
|
||||
} from '@/api/aihr/practice';
|
||||
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const rubrics = ref<PracticeRubricAdmin[]>([]);
|
||||
const selectedCode = ref('');
|
||||
const form = ref<PracticeRubricSaveRequest>();
|
||||
|
||||
const selectedRubric = computed(() => rubrics.value.find((item) => item.code === selectedCode.value));
|
||||
|
||||
const cloneForm = (item: PracticeRubricAdmin): PracticeRubricSaveRequest => ({
|
||||
code: item.code,
|
||||
scenarioCode: item.scenarioCode,
|
||||
name: item.name,
|
||||
version: item.version,
|
||||
enabled: item.enabled,
|
||||
description: item.description,
|
||||
dimensions: item.dimensions.map((dimension) => ({ ...dimension }))
|
||||
});
|
||||
|
||||
const loadRubrics = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await listPracticeRubrics();
|
||||
rubrics.value = result.data ?? [];
|
||||
if (!selectedCode.value && rubrics.value.length > 0) {
|
||||
selectRubric(rubrics.value[0]);
|
||||
} else if (selectedCode.value) {
|
||||
const current = rubrics.value.find((item) => item.code === selectedCode.value);
|
||||
if (current) selectRubric(current);
|
||||
}
|
||||
} catch {
|
||||
rubrics.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const selectRubric = (item: PracticeRubricAdmin) => {
|
||||
selectedCode.value = item.code;
|
||||
form.value = reactive(cloneForm(item));
|
||||
};
|
||||
|
||||
const normalizeWeights = () => {
|
||||
if (!form.value) return;
|
||||
const total = form.value.dimensions.reduce((sum, item) => sum + Number(item.weight || 0), 0);
|
||||
if (total > 0 && Math.abs(total - 1) > 0.001) {
|
||||
form.value.dimensions.forEach((item) => {
|
||||
item.weight = Number((item.weight / total).toFixed(2));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const createVersion = () => {
|
||||
const source = rubrics.value[0];
|
||||
if (!source) return;
|
||||
const suffix = Date.now().toString().slice(-4);
|
||||
form.value = reactive({
|
||||
...cloneForm(source),
|
||||
code: `${source.code}-${suffix}`,
|
||||
version: `v${Number.parseInt(source.version.replace(/\D/g, ''), 10) + 1}`,
|
||||
enabled: false
|
||||
});
|
||||
selectedCode.value = '';
|
||||
};
|
||||
|
||||
const copyVersion = () => createVersion();
|
||||
|
||||
const saveVersion = async () => {
|
||||
if (!form.value) return;
|
||||
normalizeWeights();
|
||||
const total = form.value.dimensions.reduce((sum, item) => sum + Number(item.weight || 0), 0);
|
||||
if (Math.abs(total - 1) > 0.011) {
|
||||
ElMessage.warning('各维度权重合计必须为 100%');
|
||||
return;
|
||||
}
|
||||
saving.value = true;
|
||||
try {
|
||||
const result = await savePracticeRubric(form.value);
|
||||
ElMessage.success('评分规则已保存并发布');
|
||||
await loadRubrics();
|
||||
if (result.data) selectRubric(result.data);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const toggleEnabled = async (item: PracticeRubricAdmin) => {
|
||||
await ElMessageBox.confirm(`确定${item.enabled ? '停用' : '启用'}“${item.name} ${item.version}”吗?`, '状态确认');
|
||||
await updatePracticeRubricEnabled(item.code, !item.enabled);
|
||||
item.enabled = !item.enabled;
|
||||
if (item.code === selectedCode.value && form.value) form.value.enabled = item.enabled;
|
||||
ElMessage.success(item.enabled ? '评分规则已启用' : '评分规则已停用');
|
||||
};
|
||||
|
||||
onMounted(loadRubrics);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.rubric-page {
|
||||
min-height: calc(100vh - 84px);
|
||||
padding: 28px 32px 40px;
|
||||
background: #f4f6fa;
|
||||
color: #1d2635;
|
||||
}
|
||||
.page-heading,
|
||||
.editor-heading,
|
||||
.editor-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.page-heading {
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
}
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
h3 {
|
||||
margin: 24px 0 18px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.page-heading p,
|
||||
.editor-heading p {
|
||||
margin-top: 8px;
|
||||
color: #858e9e;
|
||||
font-size: 13px;
|
||||
}
|
||||
.rubric-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 360px minmax(0, 1fr);
|
||||
gap: 18px;
|
||||
align-items: start;
|
||||
}
|
||||
.version-card,
|
||||
.editor-card {
|
||||
min-height: 520px;
|
||||
padding: 18px 16px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
box-shadow: 0 8px 24px rgb(33 43 54 / 4%);
|
||||
}
|
||||
.version-card h2 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.version-item {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 9px;
|
||||
padding: 16px 14px;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
background: #fafbfc;
|
||||
color: #253044;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
.version-item.active {
|
||||
background: #fff0f1;
|
||||
color: #e60012;
|
||||
}
|
||||
.version-item strong,
|
||||
.version-item span {
|
||||
display: block;
|
||||
}
|
||||
.version-item span {
|
||||
margin-top: 7px;
|
||||
color: #858e9e;
|
||||
font-size: 12px;
|
||||
}
|
||||
.editor-card {
|
||||
padding: 20px;
|
||||
}
|
||||
.editor-heading {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.dimension-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 24px;
|
||||
align-items: center;
|
||||
margin: 18px 0;
|
||||
}
|
||||
.dimension-label strong,
|
||||
.dimension-label span,
|
||||
.dimension-control > span {
|
||||
display: block;
|
||||
}
|
||||
.dimension-label span {
|
||||
margin-top: 7px;
|
||||
color: #858e9e;
|
||||
font-size: 12px;
|
||||
}
|
||||
.dimension-control > span {
|
||||
margin-bottom: 8px;
|
||||
color: #e60012;
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
}
|
||||
.band-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-top: 24px;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
background: #f7f9fb;
|
||||
color: #758196;
|
||||
font-size: 13px;
|
||||
}
|
||||
.band-card strong {
|
||||
color: #253044;
|
||||
font-size: 15px;
|
||||
}
|
||||
.editor-actions {
|
||||
margin-top: 16px;
|
||||
gap: 10px;
|
||||
}
|
||||
.editor-actions .el-button:last-child {
|
||||
flex: 1;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.rubric-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.dimension-row {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user