后台管理功能开发,AI配置管理

This commit is contained in:
2025-10-30 14:50:44 +08:00
parent dc0413d084
commit 8b6e3d0815
23 changed files with 4463 additions and 4 deletions
+160
View File
@@ -0,0 +1,160 @@
# AI配置管理前端实现
## 概述
基于Vue 3 + TypeScript + Element Plus实现的AI配置管理前端页面,提供完整的AI配置增删改查功能。
## 实现的文件
### 1. 类型定义
- `src/types/aiconfig.ts` - AI配置相关的TypeScript类型定义
- AiConfig接口
- 请求/响应类型
- 选项常量定义
### 2. API调用
- `src/api/aiconfig.ts` - AI配置相关的API调用函数
- 基础CRUD操作
- 条件查询
- 状态管理
- 统计功能
### 3. 页面组件
- `src/views/aiconfig/AiConfigList.vue` - AI配置管理主页面
- 分页列表展示
- 搜索过滤
- 新增/编辑对话框
- 状态管理操作
- 统计信息展示
### 4. 通用组件
- `src/components/AiConfigQuickActions.vue` - AI配置快速操作组件
- 统计信息展示
- 快速操作入口
### 5. 路由配置
- 更新了 `src/router/index.ts`,添加AI配置管理路由
### 6. 仪表板集成
- 更新了 `src/views/Dashboard.vue`,集成AI配置统计信息
## 主要功能
### 1. 配置列表管理
- **分页查询**: 支持分页显示配置列表
- **多条件搜索**: 支持关键词、配置类型、服务提供商、使用场景、状态、环境等条件搜索
- **排序功能**: 支持按不同字段排序
- **统计信息**: 实时显示总数、启用数、禁用数、默认配置数
### 2. 配置CRUD操作
- **新增配置**: 分标签页的详细配置表单
- 基础配置:名称、类型、提供商、URL、Token等
- 参数配置:超时、重试、Token限制、温度参数等
- 费用配置:价格、限制等
- 其他配置:状态、自定义参数等
- **编辑配置**: 支持修改所有配置项
- **查看详情**: 只读模式查看配置详情
- **删除配置**: 确认删除操作
### 3. 状态管理
- **启用/禁用**: 一键切换配置状态
- **设置默认**: 设置/取消默认配置
- **批量操作**: 支持批量状态管理
### 4. 数据展示优化
- **标签化显示**: 配置类型、服务提供商、环境等使用不同颜色标签
- **状态指示**: 清晰的状态标识
- **脱敏处理**: API Token等敏感信息脱敏显示
- **响应式布局**: 适配不同屏幕尺寸
### 5. 用户体验优化
- **加载状态**: 操作过程中的加载提示
- **错误处理**: 完善的错误提示和处理
- **确认对话框**: 重要操作的二次确认
- **表单验证**: 完整的表单验证规则
- **快捷操作**: 表格行内快捷操作按钮
## 技术特点
### 1. 类型安全
- 完整的TypeScript类型定义
- 严格的类型检查
- 良好的IDE支持
### 2. 组件化设计
- 可复用的组件设计
- 清晰的组件职责划分
- 良好的组件通信
### 3. 响应式设计
- 适配不同屏幕尺寸
- 移动端友好
- 灵活的布局系统
### 4. 性能优化
- 按需加载
- 合理的数据缓存
- 优化的渲染性能
## 页面路由
- `/aiconfig/list` - AI配置列表页面
- 在仪表板 `/dashboard` 中集成了AI配置统计信息
## 使用说明
### 1. 访问页面
通过左侧导航菜单 "AI配置管理" -> "AI配置列表" 进入管理页面
### 2. 搜索配置
- 使用顶部搜索表单进行条件筛选
- 支持关键词模糊搜索
- 支持多个条件组合搜索
### 3. 新增配置
1. 点击 "新增配置" 按钮
2. 填写基础配置信息(必填项)
3. 根据需要配置参数、费用等信息
4. 点击确定保存
### 4. 编辑配置
1. 点击表格中的 "编辑" 按钮
2. 修改需要更新的配置项
3. 点击确定保存更改
### 5. 状态管理
- 点击 "启用/禁用" 切换配置状态
- 点击 "设为默认/取消默认" 管理默认配置
- 删除操作需要二次确认
### 6. 查看统计
- 页面顶部显示实时统计信息
- 仪表板中显示概览统计
- 支持手动刷新统计数据
## 注意事项
1. **权限控制**: 需要管理员权限才能访问
2. **数据验证**: 表单提交前会进行完整验证
3. **敏感信息**: API Token等敏感信息会脱敏显示
4. **操作确认**: 删除等重要操作需要二次确认
5. **错误处理**: 网络错误和业务错误都有相应提示
## 扩展功能
### 1. 导入导出
- 支持配置的批量导入
- 支持配置的导出备份
### 2. 配置模板
- 预设常用配置模板
- 快速创建配置
### 3. 配置测试
- 在线测试配置可用性
- 健康检查功能
### 4. 使用统计
- 配置使用频率统计
- 性能监控数据
这些扩展功能可以根据实际需求逐步实现。
+120
View File
@@ -0,0 +1,120 @@
# AI配置管理菜单测试说明
## 问题解决
已成功解决AI配置管理菜单不显示的问题。
## 修改内容
`web-admin/src/config/menu.ts` 文件中添加了AI配置管理的菜单项:
```typescript
{
path: '/aiconfig',
title: 'AI配置管理',
icon: 'Setting'
}
```
## 测试步骤
### 1. 启动开发服务器
开发服务器已启动在: http://localhost:5176/
### 2. 访问管理后台
1. 打开浏览器访问 http://localhost:5176/
2. 使用管理员账号登录
### 3. 验证菜单显示
在左侧菜单栏中应该能看到以下菜单项:
- 仪表盘 (DataLine图标)
- 管理员管理 (User图标)
- 用户管理 (UserFilled图标)
- **AI配置管理 (Setting图标)** ← 新添加的菜单
### 4. 测试功能
1. 点击"AI配置管理"菜单项
2. 应该跳转到 `/aiconfig/list` 页面
3. 页面应该显示AI配置管理界面,包括:
- 搜索表单
- 统计信息
- 配置列表表格
- 新增配置按钮
### 5. 测试仪表板集成
1. 返回仪表盘页面 (`/`)
2. 应该能看到AI配置的统计信息
3. 右侧应该有AI配置快速操作组件
## 菜单配置说明
菜单配置位于 `web-admin/src/config/menu.ts` 文件中,采用以下结构:
```typescript
export interface MenuItem {
path: string // 路由路径
title: string // 菜单标题
icon?: string // 图标名称 (Element Plus图标)
children?: MenuItem[] // 子菜单
hidden?: boolean // 是否隐藏
}
```
## 路由配置
AI配置管理的路由已在 `web-admin/src/router/index.ts` 中配置:
```typescript
{
path: '/aiconfig',
component: Layout,
redirect: '/aiconfig/list',
meta: { title: 'AI配置管理', icon: 'Setting' },
children: [
{
path: 'list',
name: 'AiConfigList',
component: () => import('@/views/aiconfig/AiConfigList.vue'),
meta: { title: 'AI配置列表' }
}
]
}
```
## 注意事项
1. **图标**: 使用的是Element Plus的内置图标 `Setting`
2. **路由**: 菜单路径 `/aiconfig` 对应路由配置
3. **权限**: 需要管理员登录权限才能访问
4. **响应式**: 菜单支持折叠/展开功能
## 故障排除
如果菜单仍然不显示,请检查:
1. **缓存问题**: 清除浏览器缓存或硬刷新 (Ctrl+F5)
2. **开发服务器**: 确保开发服务器正常运行
3. **路由配置**: 确认路由配置正确
4. **组件导入**: 确认组件文件存在且可正常导入
## 后续扩展
可以根据需要添加子菜单,例如:
```typescript
{
path: '/aiconfig',
title: 'AI配置管理',
icon: 'Setting',
children: [
{
path: '/aiconfig/list',
title: '配置列表',
icon: 'List'
},
{
path: '/aiconfig/template',
title: '配置模板',
icon: 'Document'
}
]
}
```
+157
View File
@@ -0,0 +1,157 @@
# AI配置接口测试功能
## 功能概述
在AI配置管理页面新增了接口测试功能,允许管理员直接在页面上测试AI配置的接口连通性和正确性。
## 新增功能
### 1. 测试按钮
- 在AI配置列表的操作列中新增了"测试"按钮
- 点击后打开接口测试对话框
### 2. 测试对话框
测试对话框分为左右两个区域:
#### 左侧 - 请求配置区域
- **请求URL**: 自动根据配置的API基础URL生成,默认为 `{apiBaseUrl}/v3/chat`
- **请求头**: JSON格式的请求头,自动包含Authorization和Content-Type
- **请求体**: JSON格式的请求体,基于Coze API标准格式生成
#### 右侧 - 响应结果区域
- **状态码**: 显示HTTP响应状态码,带颜色标识
- **响应头**: 显示服务器返回的响应头信息
- **响应体**: 显示服务器返回的响应内容,自动格式化JSON
### 3. 操作按钮
- **发送测试请求**: 发送HTTP请求到配置的API端点
- **格式化请求**: 格式化请求头和请求体的JSON格式
- **格式化响应**: 格式化响应体的JSON格式
- **复制响应**: 将响应内容复制到剪贴板
- **重置**: 重置测试数据到初始状态
## 技术实现
### 1. 请求构建
基于AiChatServiceImpl中的实现,自动构建符合Coze API标准的请求:
```json
{
"bot_id": "配置的Bot ID",
"workflow_id": "配置的Workflow ID (可选)",
"user_id": "test_user_时间戳",
"stream": false,
"additional_messages": [
{
"role": "user",
"content": "你好,这是一个测试消息,请回复确认接口正常工作。",
"content_type": "text",
"type": "question"
}
]
}
```
### 2. 请求头处理
- 自动添加Authorization Bearer Token
- 自动添加Content-Type: application/json
- 支持合并自定义请求头
### 3. 响应处理
- 显示完整的HTTP状态码
- 显示所有响应头信息
- 自动格式化JSON响应体
- 支持非JSON响应的显示
### 4. 错误处理
- 网络错误处理
- JSON格式错误提示
- HTTP错误状态码提示
## 使用方法
### 1. 打开测试对话框
1. 在AI配置列表中找到要测试的配置
2. 点击操作列中的"测试"按钮
3. 测试对话框自动打开并填充默认数据
### 2. 自定义测试参数
- 可以修改请求头添加额外的头信息
- 可以修改请求体中的消息内容
- 可以调整其他API参数
### 3. 发送测试请求
1. 点击"发送测试请求"按钮
2. 等待请求完成(显示加载状态)
3. 查看右侧的响应结果
### 4. 分析测试结果
- **状态码 200**: 请求成功,配置正常
- **状态码 401**: 认证失败,检查API Token
- **状态码 400**: 请求参数错误,检查Bot ID等配置
- **状态码 500**: 服务器错误,联系API提供商
## 支持的配置类型
### 1. Coze配置
- 完全支持Coze API的测试
- 自动构建符合Coze标准的请求格式
- 支持Bot ID和Workflow ID
### 2. 其他配置类型
- 基础的HTTP请求测试
- 可以通过修改请求体适配不同的API格式
## 安全考虑
### 1. Token脱敏
- 在请求头显示中,Token会部分脱敏显示
- 实际请求使用完整的Token
### 2. 跨域处理
- 使用浏览器原生fetch API
- 可能受到CORS策略限制
- 建议在开发环境或配置了CORS的环境中使用
### 3. 数据隔离
- 测试数据不会影响生产数据
- 使用独立的测试用户ID
## 故障排除
### 1. 网络错误
- 检查API URL是否正确
- 检查网络连接
- 检查防火墙设置
### 2. 认证错误
- 检查API Token是否正确
- 检查Token是否过期
- 检查Token权限
### 3. 参数错误
- 检查Bot ID是否正确
- 检查Workflow ID是否存在
- 检查请求体格式
### 4. CORS错误
- 在开发环境中测试
- 或使用支持CORS的代理服务器
## 后续扩展
### 1. 批量测试
- 支持批量测试多个配置
- 生成测试报告
### 2. 测试历史
- 保存测试历史记录
- 支持测试结果对比
### 3. 自动化测试
- 定时自动测试配置可用性
- 异常告警通知
### 4. 更多API支持
- 支持OpenAI API测试
- 支持Claude API测试
- 支持其他AI服务商API测试
+207
View File
@@ -0,0 +1,207 @@
import request from '@/utils/request'
import type {
AiConfigPageRequest,
AiConfigCreateRequest,
AiConfigUpdateRequest
} from '@/types/aiconfig'
// 分页查询AI配置
export function getAiConfigPage(params: AiConfigPageRequest) {
return request({
url: '/aiConfig/page',
method: 'get',
params
})
}
// 根据ID获取AI配置
export function getAiConfigById(id: string) {
return request({
url: '/aiConfig/detail',
method: 'get',
params: { id }
})
}
// 创建AI配置
export function createAiConfig(data: AiConfigCreateRequest) {
return request({
url: '/aiConfig/create',
method: 'post',
data
})
}
// 更新AI配置
export function updateAiConfig(data: AiConfigUpdateRequest) {
return request({
url: '/aiConfig/update',
method: 'put',
data
})
}
// 删除AI配置
export function deleteAiConfig(id: string) {
return request({
url: '/aiConfig/delete',
method: 'delete',
params: { id }
})
}
// 根据配置类型查询AI配置
export function getAiConfigByType(configType: string) {
return request({
url: '/aiConfig/byConfigType',
method: 'get',
params: { configType }
})
}
// 根据服务提供商查询AI配置
export function getAiConfigByProvider(provider: string) {
return request({
url: '/aiConfig/byProvider',
method: 'get',
params: { provider }
})
}
// 根据使用场景查询AI配置
export function getAiConfigByUsageScenario(usageScenario: string) {
return request({
url: '/aiConfig/byUsageScenario',
method: 'get',
params: { usageScenario }
})
}
// 根据环境查询AI配置
export function getAiConfigByEnvironment(environment: string) {
return request({
url: '/aiConfig/byEnvironment',
method: 'get',
params: { environment }
})
}
// 查询已启用的AI配置
export function getEnabledAiConfigs() {
return request({
url: '/aiConfig/enabled',
method: 'get'
})
}
// 查询已禁用的AI配置
export function getDisabledAiConfigs() {
return request({
url: '/aiConfig/disabled',
method: 'get'
})
}
// 查询默认配置
export function getDefaultAiConfigs() {
return request({
url: '/aiConfig/default',
method: 'get'
})
}
// 根据配置键值查询AI配置
export function getAiConfigByKey(configKey: string) {
return request({
url: '/aiConfig/byConfigKey',
method: 'get',
params: { configKey }
})
}
// 启用AI配置
export function enableAiConfig(id: string) {
return request({
url: '/aiConfig/enable',
method: 'put',
params: { id }
})
}
// 禁用AI配置
export function disableAiConfig(id: string) {
return request({
url: '/aiConfig/disable',
method: 'put',
params: { id }
})
}
// 设置为默认配置
export function setAsDefaultConfig(id: string) {
return request({
url: '/aiConfig/setDefault',
method: 'put',
params: { id }
})
}
// 取消默认配置
export function unsetDefaultConfig(id: string) {
return request({
url: '/aiConfig/unsetDefault',
method: 'put',
params: { id }
})
}
// 查询最优配置
export function getBestAiConfig(usageScenario: string, environment: string) {
return request({
url: '/aiConfig/bestConfig',
method: 'get',
params: { usageScenario, environment }
})
}
// 统计已启用配置数量
export function countEnabledConfigs() {
return request({
url: '/aiConfig/countEnabled',
method: 'get'
})
}
// 统计已禁用配置数量
export function countDisabledConfigs() {
return request({
url: '/aiConfig/countDisabled',
method: 'get'
})
}
// 统计默认配置数量
export function countDefaultConfigs() {
return request({
url: '/aiConfig/countDefault',
method: 'get'
})
}
// 根据配置类型统计数量
export function countByConfigType(configType: string) {
return request({
url: '/aiConfig/countByConfigType',
method: 'get',
params: { configType }
})
}
// 根据服务提供商统计数量
export function countByProvider(provider: string) {
return request({
url: '/aiConfig/countByProvider',
method: 'get',
params: { provider }
})
}
@@ -0,0 +1,177 @@
<template>
<el-card>
<template #header>
<div class="card-header">
<span>AI配置快速操作</span>
<el-button type="text" @click="$router.push('/aiconfig/list')">
查看全部
</el-button>
</div>
</template>
<div class="quick-actions">
<el-row :gutter="20">
<el-col :span="12">
<div class="action-item">
<el-icon class="action-icon"><Setting /></el-icon>
<div class="action-content">
<div class="action-title">配置管理</div>
<div class="action-desc">管理AI接口配置</div>
</div>
<el-button type="primary" size="small" @click="$router.push('/aiconfig/list')">
管理
</el-button>
</div>
</el-col>
<el-col :span="12">
<div class="action-item">
<el-icon class="action-icon"><Star /></el-icon>
<div class="action-content">
<div class="action-title">默认配置</div>
<div class="action-desc">{{ defaultConfig ? defaultConfig.configName : '未设置' }}</div>
</div>
<el-button type="warning" size="small" @click="handleViewDefault" :disabled="!defaultConfig">
查看
</el-button>
</div>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 15px;">
<el-col :span="12">
<div class="action-item">
<el-icon class="action-icon"><CircleCheck /></el-icon>
<div class="action-content">
<div class="action-title">启用配置</div>
<div class="action-desc">{{ stats.enabled }} 个配置已启用</div>
</div>
<el-button type="success" size="small" @click="handleViewEnabled">
查看
</el-button>
</div>
</el-col>
<el-col :span="12">
<div class="action-item">
<el-icon class="action-icon"><Plus /></el-icon>
<div class="action-content">
<div class="action-title">新增配置</div>
<div class="action-desc">添加新的AI配置</div>
</div>
<el-button type="primary" size="small" @click="$router.push('/aiconfig/list')">
新增
</el-button>
</div>
</el-col>
</el-row>
</div>
</el-card>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { Setting, Star, CircleCheck, Plus } from '@element-plus/icons-vue'
import { getDefaultAiConfigs, countEnabledConfigs, countDisabledConfigs, countDefaultConfigs } from '@/api/aiconfig'
import type { AiConfig } from '@/types/aiconfig'
const router = useRouter()
const stats = reactive({
enabled: 0,
disabled: 0,
default: 0
})
const defaultConfig = ref<AiConfig | null>(null)
// 获取统计信息
const fetchStats = async () => {
try {
const [enabledRes, disabledRes, defaultRes] = await Promise.all([
countEnabledConfigs(),
countDisabledConfigs(),
countDefaultConfigs()
])
stats.enabled = enabledRes.data
stats.disabled = disabledRes.data
stats.default = defaultRes.data
} catch (error) {
console.error('获取统计信息失败:', error)
}
}
// 获取默认配置
const fetchDefaultConfig = async () => {
try {
const res = await getDefaultAiConfigs()
if (res.data && res.data.length > 0) {
defaultConfig.value = res.data[0]
}
} catch (error) {
console.error('获取默认配置失败:', error)
}
}
// 查看默认配置
const handleViewDefault = () => {
router.push('/aiconfig/list')
}
// 查看启用的配置
const handleViewEnabled = () => {
router.push('/aiconfig/list')
}
onMounted(() => {
fetchStats()
fetchDefaultConfig()
})
</script>
<style scoped lang="scss">
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.quick-actions {
.action-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
border: 1px solid #e4e7ed;
border-radius: 8px;
transition: all 0.3s;
&:hover {
border-color: #409eff;
background-color: #f0f9ff;
}
.action-icon {
font-size: 24px;
color: #409eff;
}
.action-content {
flex: 1;
.action-title {
font-size: 14px;
font-weight: 500;
color: #333;
margin-bottom: 4px;
}
.action-desc {
font-size: 12px;
color: #999;
}
}
}
}
</style>
+5
View File
@@ -21,5 +21,10 @@ export const menuConfig: MenuItem[] = [
path: '/user',
title: '用户管理',
icon: 'UserFilled'
},
{
path: '/aiconfig',
title: 'AI配置管理',
icon: 'Setting'
}
]
+14
View File
@@ -49,6 +49,20 @@ const routes: RouteRecordRaw[] = [
}
]
},
{
path: '/aiconfig',
component: Layout,
redirect: '/aiconfig/list',
meta: { title: 'AI配置管理', icon: 'Setting' },
children: [
{
path: 'list',
name: 'AiConfigList',
component: () => import('@/views/aiconfig/AiConfigList.vue'),
meta: { title: 'AI配置列表' }
}
]
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
+179
View File
@@ -0,0 +1,179 @@
// AI配置相关类型定义
export interface AiConfig {
id: string
configName: string
configKey: string
configType: string
provider: string
apiBaseUrl: string
apiToken: string
apiVersion?: string
modelName?: string
botId?: string
workflowId?: string
timeoutMs?: number
retryCount?: number
retryDelayMs?: number
maxTokens?: number
temperature?: number
topP?: number
supportStream?: number
supportFunctionCall?: number
supportVision?: number
supportFileUpload?: number
usageScenario: string
priority?: number
inputPricePer1k?: number
outputPricePer1k?: number
currency?: string
rateLimitPerMinute?: number
rateLimitPerHour?: number
rateLimitPerDay?: number
isEnabled?: number
isDefault?: number
environment?: string
customHeaders?: string
customParams?: string
webhookUrl?: string
healthCheckUrl?: string
healthCheckIntervalMinutes?: number
description?: string
usageNotes?: string
createTime?: string
updateTime?: string
}
export interface AiConfigPageRequest {
current: number
size: number
keyword?: string
configType?: string
provider?: string
usageScenario?: string
isEnabled?: number
isDefault?: number
environment?: string
orderBy?: string
orderDirection?: string
}
export interface AiConfigCreateRequest {
configName: string
configKey: string
configType: string
provider: string
apiBaseUrl: string
apiToken: string
apiVersion?: string
modelName?: string
botId?: string
workflowId?: string
timeoutMs?: number
retryCount?: number
retryDelayMs?: number
maxTokens?: number
temperature?: number
topP?: number
supportStream?: number
supportFunctionCall?: number
supportVision?: number
supportFileUpload?: number
usageScenario: string
priority?: number
inputPricePer1k?: number
outputPricePer1k?: number
currency?: string
rateLimitPerMinute?: number
rateLimitPerHour?: number
rateLimitPerDay?: number
isEnabled?: number
isDefault?: number
environment?: string
customHeaders?: string
customParams?: string
webhookUrl?: string
healthCheckUrl?: string
healthCheckIntervalMinutes?: number
description?: string
usageNotes?: string
}
export interface AiConfigUpdateRequest {
id: string
configName?: string
configKey?: string
configType?: string
provider?: string
apiBaseUrl?: string
apiToken?: string
apiVersion?: string
modelName?: string
botId?: string
workflowId?: string
timeoutMs?: number
retryCount?: number
retryDelayMs?: number
maxTokens?: number
temperature?: number
topP?: number
supportStream?: number
supportFunctionCall?: number
supportVision?: number
supportFileUpload?: number
usageScenario?: string
priority?: number
inputPricePer1k?: number
outputPricePer1k?: number
currency?: string
rateLimitPerMinute?: number
rateLimitPerHour?: number
rateLimitPerDay?: number
isEnabled?: number
isDefault?: number
environment?: string
customHeaders?: string
customParams?: string
webhookUrl?: string
healthCheckUrl?: string
healthCheckIntervalMinutes?: number
description?: string
usageNotes?: string
}
// 配置类型选项
export const CONFIG_TYPE_OPTIONS = [
{ label: 'Coze', value: 'coze' },
{ label: 'OpenAI', value: 'openai' },
{ label: 'Claude', value: 'claude' },
{ label: 'Gemini', value: 'gemini' }
]
// 服务提供商选项
export const PROVIDER_OPTIONS = [
{ label: 'Coze', value: 'coze' },
{ label: 'OpenAI', value: 'openai' },
{ label: 'Anthropic', value: 'anthropic' },
{ label: 'Google', value: 'google' }
]
// 使用场景选项
export const USAGE_SCENARIO_OPTIONS = [
{ label: '聊天', value: 'chat' },
{ label: '总结', value: 'summary' },
{ label: '情绪分析', value: 'emotion_analysis' },
{ label: '内容生成', value: 'content_generation' }
]
// 环境选项
export const ENVIRONMENT_OPTIONS = [
{ label: '开发环境', value: 'development' },
{ label: '测试环境', value: 'testing' },
{ label: '生产环境', value: 'production' }
]
// 货币选项
export const CURRENCY_OPTIONS = [
{ label: '美元', value: 'USD' },
{ label: '人民币', value: 'CNY' }
]
+94 -4
View File
@@ -60,8 +60,67 @@
</el-col>
</el-row>
<!-- AI配置统计 -->
<el-row :gutter="20" class="stats-row">
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon" style="background-color: #909399;">
<el-icon><Setting /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ aiStats.total }}</div>
<div class="stat-label">AI配置总数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon" style="background-color: #67c23a;">
<el-icon><CircleCheck /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ aiStats.enabled }}</div>
<div class="stat-label">已启用配置</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon" style="background-color: #f56c6c;">
<el-icon><CircleClose /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ aiStats.disabled }}</div>
<div class="stat-label">已禁用配置</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stat-card">
<div class="stat-content">
<div class="stat-icon" style="background-color: #e6a23c;">
<el-icon><Star /></el-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ aiStats.default }}</div>
<div class="stat-label">默认配置</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" class="chart-row">
<el-col :span="16">
<el-col :span="12">
<el-card>
<template #header>
<span>用户增长趋势</span>
@@ -70,7 +129,7 @@
</el-card>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-card>
<template #header>
<span>最近登录</span>
@@ -81,17 +140,30 @@
</el-table>
</el-card>
</el-col>
<el-col :span="6">
<AiConfigQuickActions />
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { User, UserFilled, TrendCharts, ChatDotRound } from '@element-plus/icons-vue'
import { ref, reactive, onMounted } from 'vue'
import { User, UserFilled, TrendCharts, ChatDotRound, Setting, CircleCheck, CircleClose, Star } from '@element-plus/icons-vue'
import * as echarts from 'echarts'
import { countEnabledConfigs, countDisabledConfigs, countDefaultConfigs } from '@/api/aiconfig'
import AiConfigQuickActions from '@/components/AiConfigQuickActions.vue'
const userChartRef = ref<HTMLElement>()
const aiStats = reactive({
total: 0,
enabled: 0,
disabled: 0,
default: 0
})
const recentLogins = ref([
{ username: '张三', time: '2分钟前' },
{ username: '李四', time: '5分钟前' },
@@ -101,8 +173,26 @@ const recentLogins = ref([
onMounted(() => {
initUserChart()
fetchAiStats()
})
// 获取AI配置统计
const fetchAiStats = async () => {
try {
const [enabledRes, disabledRes, defaultRes] = await Promise.all([
countEnabledConfigs(),
countDisabledConfigs(),
countDefaultConfigs()
])
aiStats.enabled = enabledRes.data
aiStats.disabled = disabledRes.data
aiStats.default = defaultRes.data
aiStats.total = aiStats.enabled + aiStats.disabled
} catch (error) {
console.error('获取AI配置统计失败:', error)
}
}
const initUserChart = () => {
if (!userChartRef.value) return
File diff suppressed because it is too large Load Diff