Files
schoolNews/schoolNewsWeb/src/views/admin/manage/ai/AIConfigView.vue

430 lines
9.8 KiB
Vue
Raw Normal View History

2025-10-16 18:03:46 +08:00
<template>
2025-11-04 18:49:37 +08:00
<AdminLayout title="AI配置" subtitle="AI助手配置管理">
<div class="ai-config-container">
<!-- 智能体信息卡片 -->
<div class="agent-info-card">
<div class="agent-header">
<div class="agent-icon">
<img v-if="configForm.avatar" :src="FILE_DOWNLOAD_URL + configForm.avatar" alt="助手头像" />
<div v-else class="default-icon">
<img src="@/assets/imgs/assisstent.svg" alt="助手头像" />
</div>
</div>
<div class="agent-info">
<h2 class="agent-name">{{ configForm.name || '未配置助手' }}</h2>
</div>
<div class="agent-status" :class="statusClass">
{{ statusText }}
</div>
</div>
</div>
<!-- 配置表单 -->
<div class="config-form-container">
<el-form :model="configForm" label-position="top" class="config-form">
<!-- 基本信息 -->
<div class="form-section">
<el-form-item label="助手名称" required>
<el-input
v-model="configForm.name"
placeholder="请输入助手名称"
maxlength="50"
show-word-limit
/>
</el-form-item>
<el-form-item label="助手头像">
<FileUpload
v-model:cover-url="configForm.avatar"
:as-dialog="false"
list-type="cover"
accept="image/*"
:max-size="2"
module="ai-agent"
tip="点击上传助手头像"
/>
</el-form-item>
<el-form-item label="模式">
<el-select
v-model="configForm.modelProvider"
placeholder="选择模式"
style="width: 100%"
>
<el-option label="OpenAI" value="openai" />
<el-option label="Anthropic" value="anthropic" />
<el-option label="Azure OpenAI" value="azure" />
<el-option label="通义千问" value="qwen" />
<el-option label="文心一言" value="wenxin" />
<el-option label="Dify" value="dify" />
</el-select>
</el-form-item>
<el-form-item label="模型">
<el-input
v-model="configForm.modelName"
placeholder="例如: gpt-4, claude-3-opus"
/>
</el-form-item>
<el-form-item label="系统提示词">
<el-input
v-model="configForm.systemPrompt"
type="textarea"
:rows="8"
placeholder="请输入系统提示词定义AI助手的角色、行为和回答风格..."
maxlength="2000"
show-word-limit
/>
</el-form-item>
</div>
<!-- 操作按钮 -->
<div class="form-actions">
<el-button type="primary" size="large" @click="handleSave" :loading="saving">
保存配置
</el-button>
<el-button size="large" @click="handleReset">
重置
</el-button>
</div>
</el-form>
</div>
2025-10-28 19:04:35 +08:00
</div>
</AdminLayout>
2025-10-16 18:03:46 +08:00
</template>
<script setup lang="ts">
2025-11-04 18:49:37 +08:00
import { ref, computed, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
2025-10-28 19:04:35 +08:00
import { AdminLayout } from '@/views/admin';
2025-11-04 18:49:37 +08:00
import { FileUpload } from '@/components/file';
import { aiAgentConfigApi } from '@/apis/ai';
import { FILE_DOWNLOAD_URL } from '@/config';
import type { AiAgentConfig } from '@/types';
2025-10-28 19:04:35 +08:00
defineOptions({
name: 'AIConfigView'
});
2025-11-04 18:49:37 +08:00
// 表单数据
const configForm = ref<AiAgentConfig>({
name: '',
avatar: '',
systemPrompt: '',
modelName: '',
modelProvider: 'dify',
status: 1
2025-10-16 18:03:46 +08:00
});
2025-11-04 18:49:37 +08:00
// 状态
const saving = ref(false);
const loading = ref(false);
// 状态文本
const statusText = computed(() => {
return configForm.value.status === 1 ? '运行中' : '已停用';
2025-10-16 18:03:46 +08:00
});
2025-11-04 18:49:37 +08:00
// 状态样式类
const statusClass = computed(() => {
return configForm.value.status === 1 ? 'status-active' : 'status-inactive';
});
2025-10-16 18:03:46 +08:00
2025-11-04 18:49:37 +08:00
// 加载配置
onMounted(async () => {
await loadConfig();
});
async function loadConfig() {
try {
loading.value = true;
// 获取启用的智能体列表
const result = await aiAgentConfigApi.listEnabledAgents();
if (result.success && result.data && result.data.length > 0) {
// 使用第一个启用的智能体
Object.assign(configForm.value, result.data[0]);
}
} catch (error) {
console.error('加载配置失败:', error);
ElMessage.warning('暂无配置信息,请填写配置');
} finally {
loading.value = false;
}
2025-10-16 18:03:46 +08:00
}
2025-11-04 18:49:37 +08:00
// 保存配置
async function handleSave() {
// 验证必填项
if (!configForm.value.name) {
ElMessage.warning('请输入助手名称');
return;
}
if (!configForm.value.modelProvider) {
ElMessage.warning('请选择模式');
return;
}
try {
saving.value = true;
// 判断是更新还是创建
if (configForm.value.id) {
await aiAgentConfigApi.updateAgent(configForm.value);
ElMessage.success('配置更新成功');
} else {
const result = await aiAgentConfigApi.createAgent(configForm.value);
if (result.success && result.data) {
configForm.value.id = result.data.id;
}
ElMessage.success('配置创建成功');
}
// 重新加载配置
await loadConfig();
} catch (error: any) {
console.error('保存配置失败:', error);
ElMessage.error(error.message || '保存配置失败');
} finally {
saving.value = false;
}
2025-10-16 18:03:46 +08:00
}
2025-11-04 18:49:37 +08:00
// 重置配置
async function handleReset() {
try {
await ElMessageBox.confirm(
'确定要重置配置吗?此操作将清空当前未保存的修改。',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
);
await loadConfig();
ElMessage.success('配置已重置');
} catch {
// 用户取消
}
2025-10-16 18:03:46 +08:00
}
</script>
<style lang="scss" scoped>
2025-11-04 18:49:37 +08:00
.ai-config-container {
padding: 24px;
max-width: 1600px;
margin: 0 auto;
}
.agent-info-card {
background: #FFFFFF;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 14px;
padding: 24px;
margin-bottom: 24px;
.agent-header {
display: flex;
align-items: center;
gap: 12px;
.agent-icon {
width: 48px;
height: 48px;
border-radius: 10px;
background: #E7000B;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
flex-shrink: 0;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
.default-icon {
display: flex;
align-items: center;
justify-content: center;
}
}
.agent-info {
flex: 1;
min-width: 0;
.agent-name {
font-size: 16px;
font-weight: 400;
line-height: 1.5;
color: #101828;
margin: 0;
letter-spacing: -0.02em;
}
}
.agent-status {
padding: 2px 8px;
border-radius: 8px;
font-size: 12px;
font-weight: 500;
line-height: 1.33;
white-space: nowrap;
&.status-active {
background: #DCFCE7;
color: #008236;
border: 1px solid transparent;
}
&.status-inactive {
background: #FEF2F2;
color: #DC2626;
border: 1px solid transparent;
}
}
}
}
.config-form-container {
background: #FFFFFF;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 14px;
padding: 24px;
2025-10-16 18:03:46 +08:00
}
.config-form {
2025-11-04 18:49:37 +08:00
max-width: 672px;
.form-section {
margin-bottom: 32px;
&:last-child {
margin-bottom: 0;
}
}
.section-title {
font-size: 16px;
font-weight: 500;
color: #101828;
margin: 0 0 16px 0;
padding-bottom: 8px;
border-bottom: 1px solid #F3F3F5;
}
:deep(.el-form-item) {
margin-bottom: 16px;
.el-form-item__label {
font-size: 14px;
font-weight: 500;
color: #0A0A0A;
line-height: 1;
margin-bottom: 8px;
padding: 0;
letter-spacing: -0.01em;
}
.el-input__wrapper {
background: #F3F3F5;
border: 1px solid transparent;
border-radius: 8px;
padding: 4px 12px;
box-shadow: none;
transition: all 0.2s;
&:hover {
border-color: rgba(231, 0, 11, 0.2);
}
&.is-focus {
border-color: #E7000B;
background: #FFFFFF;
}
}
.el-textarea__inner {
background: #F3F3F5;
border: 1px solid transparent;
border-radius: 8px;
padding: 8px 12px;
box-shadow: none;
transition: all 0.2s;
&:hover {
border-color: rgba(231, 0, 11, 0.2);
}
&:focus {
border-color: #E7000B;
background: #FFFFFF;
}
}
.el-input-number {
width: 100%;
.el-input__wrapper {
width: 100%;
}
}
}
.form-actions {
margin-top: 32px;
padding-top: 24px;
border-top: 1px solid #F3F3F5;
display: flex;
gap: 12px;
.el-button {
border-radius: 8px;
font-weight: 500;
letter-spacing: -0.01em;
&.el-button--primary {
background: #E7000B;
border-color: #E7000B;
color: #FFFFFF;
&:hover {
background: #C90009;
border-color: #C90009;
}
&:active {
background: #A30008;
border-color: #A30008;
}
}
&.el-button--default {
background: #F3F3F5;
border-color: transparent;
color: #0A0A0A;
&:hover {
background: #E5E5E7;
}
}
}
}
}
:deep(.el-switch) {
--el-switch-on-color: #E7000B;
.el-switch__label {
font-size: 14px;
color: #0A0A0A;
2025-10-16 18:03:46 +08:00
}
}
</style>