Files
Jog/jog-frontend/src/views/admin/AiModels.vue
T
nojava 6c8b31e38a feat: AI 助手模型管理(CRUD、会话绑定、自动化验收)
- Flyway V6:模型表、默认种子、ai_chat_session.model_id 回填
- 管理端 /api/admin/ai/models;用户端选模与新会话绑定
- 禁用模型后会话只读(5009);chat 按会话 modelCode 调 DashScope
- 前端 AiModels 页、助手模型选择器与 localStorage
- 单测、AiModelManagementApiIT、Playwright E2E;e2e Profile 关闭注解限流
- 技术实施文档与验收报告;移除过期冲突分析文档

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 03:36:31 +08:00

188 lines
6.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.
<template>
<div>
<h2 class="page-title">AI 模型管理</h2>
<div class="filter-bar">
<el-button type="primary" @click="showAddDialog">新增模型</el-button>
</div>
<el-card class="table-card">
<el-table :data="models" v-loading="loading">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="displayName" label="展示名称" min-width="140" />
<el-table-column prop="modelCode" label="模型编码" min-width="140" />
<el-table-column prop="description" label="描述" min-width="160" show-overflow-tooltip />
<el-table-column label="状态" width="80">
<template #default="{ row }">
<el-tag :type="row.enabled ? 'success' : 'danger'">{{ row.enabled ? '启用' : '禁用' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="图片" width="70">
<template #default="{ row }">
<span>{{ row.allowImageUpload ? '是' : '否' }}</span>
</template>
</el-table-column>
<el-table-column label="语音" width="70">
<template #default="{ row }">
<span>{{ row.allowVoiceUpload ? '是' : '否' }}</span>
</template>
</el-table-column>
<el-table-column label="默认" width="70">
<template #default="{ row }">
<el-tag v-if="row.isDefault" type="warning" size="small">默认</el-tag>
<span v-else></span>
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" width="70" />
<el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }">
<el-button text type="primary" @click="showEditDialog(row)">编辑</el-button>
<el-button text type="danger" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="dialogVisible" :title="isEdit ? '编辑模型' : '新增模型'" width="520px">
<el-form :model="form" label-width="120px">
<el-form-item label="展示名称" required>
<el-input v-model="form.displayName" maxlength="32" placeholder="如:通义 Turbo · 快速" />
</el-form-item>
<el-form-item label="模型编码" required>
<el-input v-model="form.modelCode" maxlength="64" placeholder="如:qwen-turbo" />
</el-form-item>
<el-form-item label="描述">
<el-input v-model="form.description" type="textarea" :rows="2" maxlength="200" placeholder="可选,如:更强推理,适合复杂问题" />
</el-form-item>
<el-form-item label="启用">
<el-switch v-model="form.enabled" />
</el-form-item>
<el-form-item label="允许上传图片">
<el-switch v-model="form.allowImageUpload" />
</el-form-item>
<el-form-item label="允许上传语音">
<el-switch v-model="form.allowVoiceUpload" />
</el-form-item>
<el-form-item label="默认模型">
<el-switch v-model="form.isDefault" />
</el-form-item>
<el-form-item label="排序">
<el-input-number v-model="form.sort" :min="0" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { listAdminAiModels, createAdminAiModel, updateAdminAiModel, deleteAdminAiModel } from '@/api/aiModel'
import { ElMessage, ElMessageBox } from 'element-plus'
const models = ref([])
const loading = ref(false)
const dialogVisible = ref(false)
const isEdit = ref(false)
const editingId = ref(null)
const form = ref({
modelCode: '',
displayName: '',
description: '',
enabled: true,
allowImageUpload: false,
allowVoiceUpload: false,
isDefault: false,
sort: 0,
})
async function loadModels() {
loading.value = true
try {
const res = await listAdminAiModels()
models.value = res.data || []
} finally {
loading.value = false
}
}
function showAddDialog() {
isEdit.value = false
editingId.value = null
form.value = { modelCode: '', displayName: '', description: '', enabled: true, allowImageUpload: false, allowVoiceUpload: false, isDefault: false, sort: 0 }
dialogVisible.value = true
}
function showEditDialog(row) {
isEdit.value = true
editingId.value = row.id
form.value = {
modelCode: row.modelCode,
displayName: row.displayName,
description: row.description || '',
enabled: row.enabled,
allowImageUpload: row.allowImageUpload,
allowVoiceUpload: row.allowVoiceUpload,
isDefault: row.isDefault,
sort: row.sort,
}
dialogVisible.value = true
}
async function handleSubmit() {
if (!form.value.displayName.trim()) {
ElMessage.warning('请输入展示名称')
return
}
if (!form.value.modelCode.trim()) {
ElMessage.warning('请输入模型编码')
return
}
try {
if (isEdit.value) {
await updateAdminAiModel(editingId.value, form.value)
} else {
await createAdminAiModel(form.value)
}
ElMessage.success('操作成功')
dialogVisible.value = false
loadModels()
} catch {
// error handled by interceptor
}
}
async function handleDelete(row) {
await ElMessageBox.confirm(`确定删除模型"${row.displayName}"`, '提示')
try {
await deleteAdminAiModel(row.id)
ElMessage.success('已删除')
loadModels()
} catch {
// error handled by interceptor
}
}
onMounted(loadModels)
</script>
<style scoped>
.page-title {
color: var(--text-primary);
font-size: var(--font-size-xl);
margin-bottom: var(--spacing-lg);
}
.filter-bar {
margin-bottom: var(--spacing-lg);
display: flex;
gap: var(--spacing-md);
align-items: center;
}
.table-card {
border-radius: var(--radius-md);
overflow: hidden;
}
.table-card :deep(.el-card__body) { padding: 0; }
</style>