Files
X-Agents/web/src/views/Settings.vue
DESKTOP-72TV0V4\caoxiaozhu 49433f1681 feat: 增强 Knowledge 页面功能
- 优化文档预览功能
- 添加 CSV 文件解析支持
- 增强知识库详情展示
- 优化样式和交互

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 15:42:55 +08:00

439 lines
15 KiB
Vue

<script setup lang="ts">
import { ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { useModelSettings } from './settings/useModelSettings'
import FormDialog from '@/components/FormDialog.vue'
import './settings/settings.css'
import './settings/modelSettings.css'
// 当前选中的设置菜单
const activeMenu = ref('general')
// 导入 Model Settings 逻辑
const {
models,
modelsLoading,
modelTypeOptions,
providerOptions,
showAddModelForm,
showEditModelForm,
newModelForm,
editForm,
testingConnection,
connectionStatus,
fetchModels,
testConnection,
addModel,
cancelAddModel,
deleteModel,
openEditDialog,
updateModel,
cancelEditModel,
testingEditConnection,
editConnectionStatus,
testConnectionEdit,
} = useModelSettings()
// 监听菜单切换,获取模型列表
watch(activeMenu, (newVal) => {
if (newVal === 'modelSettings') {
fetchModels()
}
})
// 设置菜单列表
const menuItems = [
{ key: 'general', label: 'General', icon: 'fa-gear' },
{ key: 'members', label: 'Members', icon: 'fa-users' },
{ key: 'notifications', label: 'Notifications', icon: 'fa-bell' },
{ key: 'modelSettings', label: 'Model Settings', icon: 'fa-brain' },
]
// General 设置表单
const generalForm = ref({
name: 'Alex Smith',
email: 'alex@gmail.com',
password: '********',
language: 'English',
timezone: 'UTC +08:00 Beijing',
})
// 语言选项
const languageOptions = [
{ value: 'English', label: 'English' },
{ value: 'Chinese', label: '中文' },
{ value: 'Japanese', label: '日本語' },
]
// 时区选项
const timezoneOptions = [
{ value: 'UTC +08:00 Beijing', label: 'UTC +08:00 Beijing' },
{ value: 'UTC +00:00 London', label: 'UTC +00:00 London' },
{ value: 'UTC -05:00 New York', label: 'UTC -05:00 New York' },
{ value: 'UTC -08:00 Los Angeles', label: 'UTC -08:00 Los Angeles' },
]
// 保存设置
const saveChanges = () => {
ElMessage.success('Settings saved successfully')
}
// 显示密码修改弹窗
const showChangePassword = () => {
ElMessage.info('Password change dialog would open here')
}
</script>
<template>
<div class="settings-page">
<!-- 页面标题 -->
<div class="page-header">
<h1 class="page-title">Settings</h1>
</div>
<div class="settings-container">
<!-- 左侧菜单 -->
<nav class="settings-nav">
<ul>
<li
v-for="item in menuItems"
:key="item.key"
:class="['nav-item', { active: activeMenu === item.key }]"
@click="activeMenu = item.key"
>
<i :class="['fa-solid', item.icon]"></i>
<span>{{ item.label }}</span>
</li>
</ul>
</nav>
<!-- 右侧内容 -->
<div class="settings-content">
<!-- General 设置 -->
<div v-if="activeMenu === 'general'" class="settings-section">
<h2 class="section-title">General Settings</h2>
<p class="section-desc">Manage your personal information and preferences</p>
<el-form :model="generalForm" label-position="top" class="settings-form">
<el-form-item label="Name">
<el-input v-model="generalForm.name" placeholder="Enter your name" />
</el-form-item>
<el-form-item label="Email">
<el-input v-model="generalForm.email" placeholder="Enter your email" />
</el-form-item>
<el-form-item label="Password">
<div class="password-field">
<el-input v-model="generalForm.password" type="password" disabled />
<el-button @click="showChangePassword">Change</el-button>
</div>
</el-form-item>
<el-form-item label="Language">
<el-select v-model="generalForm.language" placeholder="Select language">
<el-option
v-for="lang in languageOptions"
:key="lang.value"
:label="lang.label"
:value="lang.value"
/>
</el-select>
</el-form-item>
<el-form-item label="Timezone">
<el-select v-model="generalForm.timezone" placeholder="Select timezone">
<el-option
v-for="tz in timezoneOptions"
:key="tz.value"
:label="tz.label"
:value="tz.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveChanges">Save Changes</el-button>
</el-form-item>
</el-form>
</div>
<!-- Members 设置 -->
<div v-if="activeMenu === 'members'" class="settings-section">
<h2 class="section-title">Members</h2>
<p class="section-desc">Manage team members</p>
</div>
<!-- Notifications 设置 -->
<div v-if="activeMenu === 'notifications'" class="settings-section">
<h2 class="section-title">Notifications</h2>
<p class="section-desc">Configure notification preferences</p>
</div>
<!-- Model Settings 设置 -->
<div v-if="activeMenu === 'modelSettings'" class="settings-section">
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="section-title">Model Settings</h2>
<p class="section-desc">Configure AI model settings</p>
</div>
<el-button type="primary" class="add-model-btn" @click="showAddModelForm = true">
<i class="fa-solid fa-plus mr-2"></i>
Add New Model
</el-button>
</div>
<!-- 模型列表 -->
<div v-if="modelsLoading" class="loading-spinner">
<i class="fa-solid fa-spinner"></i>
</div>
<table v-else class="model-table">
<thead>
<tr>
<th>Model Name</th>
<th class="text-center">Provider</th>
<th class="text-center">Model</th>
<th class="text-center">Model Type</th>
<th class="text-center">Base URL</th>
<th class="text-center">Status</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="model in models" :key="model.id" class="table-row">
<td>
<span class="font-medium">{{ model.name }}</span>
</td>
<td class="text-center text-sm">
{{ model.provider }}
</td>
<td class="text-center text-sm">
{{ model.model }}
</td>
<td class="text-center">
<span class="model-type-tag" :class="model.model_type">{{ model.model_type }}</span>
</td>
<td class="text-center text-sm">
{{ model.base_url }}
</td>
<td class="text-center">
<span v-if="model.status === 'active'" class="status-active">Active</span>
<span v-else class="status-inactive">Inactive</span>
</td>
<td class="text-center">
<button class="btn-icon" title="Edit" @click="openEditDialog(model)">
<i class="fa-solid fa-pen"></i>
</button>
<button class="btn-icon" title="Delete" @click="deleteModel(model.id)">
<i class="fa-solid fa-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
<!-- 新增模型弹窗 -->
<FormDialog
:model-value="showAddModelForm"
@update:model-value="showAddModelForm = $event"
title="Add New Model"
description="Configure your model settings"
icon="fa-solid fa-brain"
icon-class="bg-gradient-to-br from-primary-orange to-red-500"
class="add-model-dialog"
>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Model Name</label>
<input
v-model="newModelForm.name"
type="text"
placeholder="e.g., My GPT-4 Model"
class="input-field"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Model Type</label>
<el-select v-model="newModelForm.modelType" placeholder="Select model type" class="w-full" size="large" popper-class="dark-select-dropdown">
<el-option
v-for="type in modelTypeOptions"
:key="type.value"
:label="type.label"
:value="type.value"
/>
</el-select>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Provider</label>
<el-select v-model="newModelForm.provider" placeholder="Select provider" class="w-full" size="large" popper-class="dark-select-dropdown">
<el-option
v-for="provider in providerOptions"
:key="provider.value"
:label="provider.label"
:value="provider.value"
/>
</el-select>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Model</label>
<input
v-model="newModelForm.model"
type="text"
placeholder="e.g., gpt-4o (OpenAI) or llama3 (Ollama)"
class="input-field"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">API Key</label>
<input
v-model="newModelForm.apiKey"
type="password"
placeholder="sk-xxxxx (required for OpenAI)"
class="input-field"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Base URL</label>
<input
v-model="newModelForm.baseUrl"
type="text"
placeholder="https://api.openai.com/v1 (OpenAI) or http://localhost:11434 (Ollama)"
class="input-field"
>
</div>
<!-- 连接状态提示 -->
<div v-if="connectionStatus === 'success'" class="connection-status success">
<i class="fa-solid fa-check-circle"></i> Connection successful
</div>
<div v-else-if="connectionStatus === 'error'" class="connection-status error">
<i class="fa-solid fa-xmark-circle"></i> Connection failed
</div>
</div>
<template #footer>
<button class="btn-secondary" @click="cancelAddModel">Cancel</button>
<button
:disabled="testingConnection"
class="btn-primary"
@click="testConnection"
>
<i class="fa-solid fa-plug"></i>
Test Connection
</button>
<button class="btn-primary" @click="addModel">
Confirm
</button>
</template>
</FormDialog>
<!-- 编辑模型弹窗 -->
<FormDialog
:model-value="showEditModelForm"
@update:model-value="showEditModelForm = $event"
title="Edit Model"
description="Update your model settings"
icon="fa-solid fa-pen-to-square"
icon-class="bg-gradient-to-br from-primary-cyan to-blue-500"
>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Model Name</label>
<input
v-model="editForm.name"
type="text"
placeholder="e.g., My GPT-4 Model"
class="input-field"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Provider</label>
<el-select v-model="editForm.provider" placeholder="Select provider" class="w-full" size="large" popper-class="dark-select-dropdown">
<el-option
v-for="option in providerOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Model</label>
<input
v-model="editForm.model"
type="text"
placeholder="e.g., gpt-4, llama2"
class="input-field"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Model Type</label>
<el-select v-model="editForm.modelType" placeholder="Select model type" class="w-full" size="large" popper-class="dark-select-dropdown">
<el-option
v-for="option in modelTypeOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">API Key</label>
<input
v-model="editForm.apiKey"
type="password"
placeholder="sk-..."
class="input-field"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Base URL</label>
<input
v-model="editForm.baseUrl"
type="text"
placeholder="e.g., https://api.openai.com/v1"
class="input-field"
>
</div>
<!-- 连接状态提示 -->
<div v-if="editConnectionStatus === 'success'" class="connection-status success">
<i class="fa-solid fa-check-circle"></i> Connection successful
</div>
<div v-if="editConnectionStatus === 'error'" class="connection-status error">
<i class="fa-solid fa-xmark-circle"></i> Connection failed
</div>
</div>
<template #footer>
<button class="btn-secondary" @click="cancelEditModel">Cancel</button>
<button
:disabled="testingEditConnection"
class="btn-primary"
@click="testConnectionEdit"
>
<i class="fa-solid fa-plug"></i>
Test Connection
</button>
<button class="btn-primary" @click="updateModel">
Confirm
</button>
</template>
</FormDialog>
</div>
</div>
</div>
</div>
</template>