feat: 评测模块新增详情页与创建向导

新增 EvalTaskDetail 与逐样本结果类型,Mock 与适配器补充评测详情接口,路由注册详情页并将维度创建并入向导;EvalCreateView 改为分步向导(任务配置、规则设置、维度表单),新增 EvalDetailView 展示综合得分与样本判定,列表补充详情入口,配套两份回归脚本。
This commit is contained in:
caoxiaozhu
2026-07-12 15:39:59 +08:00
parent c0f5f4a30a
commit 71ce26fff6
15 changed files with 1912 additions and 303 deletions

View File

@@ -1,19 +1,17 @@
<script setup lang="ts">
import { ref, reactive, computed, watch, onMounted } from 'vue'
import { ref, reactive, computed, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { MdEditor } from 'md-editor-v3'
import 'md-editor-v3/lib/style.css'
import PageCard from '@/components/PageCard.vue'
import DimensionFormFields, { type DimensionFormDraft } from './create/DimensionFormFields.vue'
import {
createDimension,
updateDimension,
getDimension,
} from '@/api/modules/eval'
import { getModelList } from '@/api/modules/model'
import { EVAL_METHODS, EVAL_METHOD_PROMPTS } from '@/constants/dimension'
import { DIMENSION_TYPE_MAP } from '@/constants'
import type { DimensionType, ModelItem } from '@/types'
import { EVAL_METHODS } from '@/constants/dimension'
import type { ModelItem } from '@/types'
const route = useRoute()
const router = useRouter()
@@ -24,9 +22,9 @@ const editId = computed(() => route.params.id as string | undefined)
const evalModels = ref<ModelItem[]>([])
const form = reactive({
const form = reactive<DimensionFormDraft>({
name: '',
type: '' as DimensionType | '',
type: '',
description: '',
eval_model: '',
eval_method: '' as string | string[],
@@ -49,44 +47,10 @@ const rules: FormRules = {
],
type: [{ required: true, message: '请选择指标类型', trigger: 'change' }],
eval_model: [{ required: true, message: '请选择大模型', trigger: 'change' }],
eval_method: [{ required: true, message: '请选择评估方式', trigger: 'change' }],
eval_prompt: [{ required: true, message: '请填写评估 Prompt', trigger: 'blur' }],
}
const typeOptions = Object.entries(DIMENSION_TYPE_MAP).map(([value, label]) => ({ value, label }))
/** 当前类型下的评估方法选项 */
const currentMethods = computed(() => (form.type ? EVAL_METHODS[form.type] || [] : []))
/** 是否文本相似度类型(多选评估方法) */
const isTextSimilarity = computed(() => form.type === 'text_similarity')
/** 切换指标类型时重置相关字段 */
watch(
() => form.type,
(t) => {
if (!t) return
const methods = EVAL_METHODS[t] || []
const first = methods[0]?.value || ''
form.eval_method = isTextSimilarity.value ? [first] : first
// 切换 Prompt
if (form.type !== 'text_similarity' && EVAL_METHOD_PROMPTS[first]) {
form.eval_prompt = EVAL_METHOD_PROMPTS[first]
}
},
)
/** 评估方法变化时更新 Prompt */
watch(
() => form.eval_method,
(m) => {
if (form.type === 'text_similarity' || !m) return
const method = Array.isArray(m) ? m[0] : m
if (method && EVAL_METHOD_PROMPTS[method]) {
form.eval_prompt = EVAL_METHOD_PROMPTS[method]
}
},
)
async function loadEditData() {
if (!editId.value) return
try {
@@ -176,100 +140,7 @@ onMounted(() => {
<template>
<PageCard :title="isEdit ? '编辑评测维度' : '添加评测维度'">
<el-form ref="formRef" :model="form" :rules="rules" label-width="130px" style="max-width: 760px">
<el-form-item label="维度名称" prop="name">
<el-input v-model="form.name" placeholder="请输入维度名称" maxlength="50" show-word-limit />
</el-form-item>
<el-form-item label="指标类型" prop="type">
<el-select v-model="form.type" placeholder="请选择指标类型" style="width: 100%">
<el-option v-for="opt in typeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="描述">
<el-input v-model="form.description" type="textarea" :rows="2" maxlength="200" show-word-limit />
</el-form-item>
<!-- 分类型 / 指标型需要选择大模型和评估方法 -->
<template v-if="form.type === 'classification' || form.type === 'metric'">
<el-form-item label="选择大模型" prop="eval_model">
<el-select v-model="form.eval_model" placeholder="请选择评测模型" filterable style="width: 100%">
<el-option v-for="m in evalModels" :key="m.id" :label="m.name" :value="m.name" />
</el-select>
</el-form-item>
<el-form-item label="评估方式" prop="eval_method">
<el-radio-group v-model="(form.eval_method as string)">
<el-radio v-for="m in currentMethods" :key="m.value" :value="m.value">
{{ m.name }}
<span class="method-desc">{{ m.desc }}</span>
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="评估 Prompt" prop="eval_prompt">
<MdEditor v-model="form.eval_prompt" language="zh-CN" :toolbars-exclude="['github', 'save']" style="height: 320px" />
</el-form-item>
</template>
<!-- 文本相似度多选评估方法 + BLEU/精度配置 -->
<template v-if="form.type === 'text_similarity'">
<el-form-item label="评估方式">
<el-checkbox-group v-model="(form.eval_method as string[])">
<el-checkbox v-for="m in currentMethods" :key="m.value" :value="m.value">
{{ m.name }}
<span class="method-desc">{{ m.desc }}</span>
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="BLEU n-gram">
<el-select v-model="form.bleu_n" style="width: 160px">
<el-option :value="1" label="1-gram" />
<el-option :value="2" label="2-gram" />
<el-option :value="3" label="3-gram" />
<el-option :value="4" label="4-gram" />
</el-select>
</el-form-item>
<el-form-item label="输出精度">
<el-select v-model="form.output_precision" style="width: 160px">
<el-option :value="1" label="1 位小数" />
<el-option :value="2" label="2 位小数" />
<el-option :value="3" label="3 位小数" />
<el-option :value="4" label="4 位小数" />
</el-select>
</el-form-item>
</template>
<!-- 指标型评分范围 + 通过阈值 -->
<template v-if="form.type === 'metric'">
<el-form-item label="评分最小值">
<el-input-number v-model="form.score_min" :min="0" :step="1" style="width: 160px" />
</el-form-item>
<el-form-item label="评分最大值">
<el-input-number v-model="form.score_max" :min="0" :step="1" style="width: 160px" />
</el-form-item>
<el-form-item label="通过阈值">
<div style="width: 100%">
<el-slider
v-model="form.pass_threshold"
:min="form.score_min"
:max="form.score_max"
:step="0.5"
show-input
:show-input-controls="false"
/>
</div>
</el-form-item>
</template>
<el-form-item label="启用该维度">
<el-switch v-model="form.is_active" />
</el-form-item>
<el-form-item label="设为默认">
<el-switch v-model="form.is_default" />
</el-form-item>
<DimensionFormFields v-model="form" :eval-models="evalModels" />
<el-form-item>
<el-button type="primary" :loading="submitting" @click="handleSubmit">
@@ -280,11 +151,3 @@ onMounted(() => {
</el-form>
</PageCard>
</template>
<style scoped>
.method-desc {
color: #909399;
font-size: 12px;
margin-left: 6px;
}
</style>