Files
YG_FT/frontend/src/views/model/ModelCreateView.vue
caoxiaozhu 6f6609dae5 feat: 实现业务视图页面
登录、模型调优、评测、推理、对比、模型管理、数据集、数据处理、工具、系统(硬件/日志/训练日志)等全部业务页面视图。
2026-07-10 16:45:55 +08:00

232 lines
7.0 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.
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import PageCard from '@/components/PageCard.vue'
import {
getModel,
createModel,
updateModel,
getLocalModels,
} from '@/api/modules/model'
import { MODEL_TYPE_MAP } from '@/constants'
import type { ModelForm, ModelSource } from '@/types'
const route = useRoute()
const router = useRouter()
const formRef = ref<FormInstance>()
const loading = ref(false)
const submitting = ref(false)
const isEdit = computed(() => !!route.params.id)
const editId = computed(() => route.params.id as string | undefined)
const localModels = ref<{ path: string; name: string }[]>([])
const form = reactive<ModelForm>({
name: '',
type: 'LLM',
purpose: 'inference',
model_source: 'local',
description: '',
path: '',
api_url: '',
api_key: '',
online_model_name: '',
})
const rules: FormRules = {
name: [
{ required: true, message: '请输入模型名称', trigger: 'blur' },
{ max: 100, message: '不超过 100 字符', trigger: 'blur' },
],
type: [{ required: true, message: '请选择模型类型', trigger: 'change' }],
purpose: [{ required: true, message: '请选择模型用途', trigger: 'change' }],
}
/** 根据 model_source 动态校验 */
function getSourceRules(): FormRules {
if (form.model_source === 'local') {
return { path: [{ required: true, message: '请选择本地模型路径', trigger: 'change' }] }
}
return {
api_url: [{ required: true, message: '请输入 API 地址', trigger: 'blur' }],
api_key: [{ required: true, message: '请输入 API Key', trigger: 'blur' }],
online_model_name: [{ required: true, message: '请输入模型名称', trigger: 'blur' }],
}
}
const typeOptions = Object.entries(MODEL_TYPE_MAP).map(([value, label]) => ({ value, label }))
async function loadEditData() {
if (!editId.value) return
loading.value = true
try {
const model: any = await getModel(editId.value)
// 兼容旧数据online → api
const source: ModelSource = model.model_source === 'online' ? 'api' : model.model_source || 'local'
Object.assign(form, {
name: model.name || '',
type: model.type || 'LLM',
purpose: model.purpose || 'inference',
model_source: source,
description: model.description || '',
path: model.path || '',
api_url: model.api_url || '',
api_key: model.api_key || '',
online_model_name: model.model_name || '',
})
} catch {
// ignore
} finally {
loading.value = false
}
}
async function loadLocalModels() {
try {
const res = await getLocalModels()
localModels.value = res?.models || []
} catch {
localModels.value = []
}
}
async function handleSubmit() {
if (!formRef.value) return
await formRef.value.validate(async (valid) => {
if (!valid) return
// 来源相关字段校验
if (form.model_source === 'local' && !form.path) {
ElMessage.warning('请选择本地模型路径')
return
}
submitting.value = true
try {
const data: ModelForm = {
name: form.name,
type: form.type,
purpose: form.purpose,
model_source: form.model_source,
description: form.description,
}
if (form.model_source === 'local') {
data.path = form.path
} else {
data.api_url = form.api_url
data.api_key = form.api_key
data.online_model_name = form.online_model_name
}
if (isEdit.value && editId.value) {
await updateModel(editId.value, data)
ElMessage.success('更新成功')
} else {
await createModel(data)
ElMessage.success('创建成功')
}
router.push('/model-manage')
} catch {
// ignore
} finally {
submitting.value = false
}
})
}
function handleCancel() {
router.back()
}
onMounted(() => {
loadLocalModels()
loadEditData()
})
</script>
<template>
<PageCard :title="isEdit ? '编辑模型' : '添加模型'" v-loading="loading">
<el-form
ref="formRef"
:model="form"
:rules="{ ...rules, ...getSourceRules() }"
label-width="120px"
style="max-width: 640px"
>
<el-form-item label="模型名称" prop="name">
<el-input v-model="form.name" placeholder="请输入模型名称" maxlength="100" 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="模型用途" prop="purpose">
<el-radio-group v-model="form.purpose">
<el-radio value="training">训练基座</el-radio>
<el-radio value="inference">推理对比</el-radio>
<el-radio value="evaluation">评测</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="模型来源" prop="model_source">
<el-radio-group v-model="form.model_source">
<el-radio value="local">本地模型</el-radio>
<el-radio value="api">在线模型</el-radio>
</el-radio-group>
</el-form-item>
<!-- 本地模型 -->
<template v-if="form.model_source === 'local'">
<el-form-item label="本地模型路径" prop="path">
<el-select
v-model="form.path"
placeholder="请选择本地模型路径"
filterable
style="width: 100%"
>
<el-option
v-for="m in localModels"
:key="m.path"
:label="`${m.name} (${m.path})`"
:value="m.path"
/>
</el-select>
</el-form-item>
</template>
<!-- 在线 API 模型 -->
<template v-else>
<el-form-item label="API 地址" prop="api_url">
<el-input v-model="form.api_url" placeholder="如https://api.openai.com/v1" />
</el-form-item>
<el-form-item label="API Key" prop="api_key">
<el-input v-model="form.api_key" type="password" show-password placeholder="请输入 API Key" />
</el-form-item>
<el-form-item label="模型名称" prop="online_model_name">
<el-input v-model="form.online_model_name" placeholder="如gpt-4、qwen-turbo" />
</el-form-item>
</template>
<el-form-item label="模型描述">
<el-input
v-model="form.description"
type="textarea"
:rows="3"
maxlength="500"
show-word-limit
placeholder="选填"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="submitting" @click="handleSubmit">
{{ isEdit ? '保存' : '创建' }}
</el-button>
<el-button @click="handleCancel">取消</el-button>
</el-form-item>
</el-form>
</PageCard>
</template>