Files
X-Agents/web/src/views/Settings.vue
DESKTOP-72TV0V4\caoxiaozhu 03540fb9e9 feat: 更新前端页面
- Agents, Chat, Settings, Skill, Tools
- Account, Plan, Script
- useSkills composable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 14:26:25 +08:00

312 lines
12 KiB
Vue

<script setup lang="ts">
import { onMounted } from 'vue'
import { useModelSettings } from './settings/useModelSettings'
import FormDialog from '@/components/FormDialog.vue'
// 导入 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()
// 页面加载时获取模型列表
onMounted(() => {
fetchModels()
})
</script>
<template>
<div class="p-6 min-h-screen">
<!-- 页面标题 -->
<div class="flex items-center gap-2 mb-6">
<i class="fa-solid fa-brain text-orange-500"></i>
<span class="font-medium">Models</span>
</div>
<!-- 内容区域 -->
<div class="space-y-4">
<!-- Models 内容 -->
<div class="flex items-center justify-between mb-6">
<div>
<p class="text-sm text-gray-400 mt-1">Configure AI models</p>
</div>
<button class="bg-gradient-to-r from-primary-orange to-red-500 hover:from-orange-500 hover:to-red-600 text-white px-4 py-2 rounded-lg font-medium flex items-center gap-2 transition-all" @click="showAddModelForm = true">
<i class="fa-solid fa-plus"></i>
Add New Model
</button>
</div>
<!-- 模型列表 -->
<div v-if="modelsLoading" class="py-12 text-center text-gray-500">
<i class="fa-solid fa-spinner fa-spin text-2xl"></i>
</div>
<div v-else class="bg-dark-700 rounded-xl overflow-hidden">
<table class="w-full">
<thead class="bg-dark-600">
<tr>
<th class="text-left px-5 py-3 text-sm font-medium text-gray-400">Model Name</th>
<th class="text-left px-5 py-3 text-sm font-medium text-gray-400">Provider</th>
<th class="text-left px-5 py-3 text-sm font-medium text-gray-400">Model</th>
<th class="text-left px-5 py-3 text-sm font-medium text-gray-400">Model Type</th>
<th class="text-left px-5 py-3 text-sm font-medium text-gray-400">Base URL</th>
<th class="text-left px-5 py-3 text-sm font-medium text-gray-400">Status</th>
<th class="text-right px-5 py-3 text-sm font-medium text-gray-400">Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="model in models" :key="model.id" class="border-t border-dark-600 hover:bg-dark-600/50">
<td class="px-5 py-4">
<span class="font-medium">{{ model.name }}</span>
</td>
<td class="px-5 py-4 text-sm text-gray-300">
{{ model.provider }}
</td>
<td class="px-5 py-4 text-sm text-gray-300">
{{ model.model }}
</td>
<td class="px-5 py-4">
<span class="px-2 py-1 rounded text-xs" :class="model.model_type === 'chat' ? 'bg-primary-cyan/20 text-primary-cyan' : 'bg-purple-500/20 text-purple-400'">{{ model.model_type }}</span>
</td>
<td class="px-5 py-4 text-sm text-gray-300">
{{ model.base_url }}
</td>
<td class="px-5 py-4">
<span v-if="model.status === 'active'" class="px-2 py-1 rounded text-xs bg-primary-success/20 text-primary-success">Active</span>
<span v-else class="px-2 py-1 rounded text-xs bg-gray-500/20 text-gray-400">Inactive</span>
</td>
<td class="px-5 py-4">
<div class="flex items-center justify-end gap-2">
<button class="p-2 rounded-lg hover:bg-dark-500 transition-colors" title="Edit" @click="openEditDialog(model)">
<i class="fa-solid fa-pen text-gray-400 hover:text-white"></i>
</button>
<button class="p-2 rounded-lg hover:bg-dark-500 transition-colors" title="Delete" @click="deleteModel(model.id)">
<i class="fa-solid fa-trash text-gray-400 hover:text-red-400"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 新增模型弹窗 -->
<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>
</template>