新增用户管理/角色管理/菜单管理功能,修复创作流水线研究脚本
- 用户管理:新增编辑弹窗(修改用户名/角色/密码),增加组织/创建时间/最后登录列 - 角色管理:新增 Role 模型 + CRUD API,admin.html 新增角色管理 tab - 菜单管理:新增 Menu 模型 + CRUD API,导航栏从 API 动态加载菜单项 - 个人中心:右上角下拉菜单(个人信息/修改密码/退出),新增修改密码 API - 种子数据:initial_data.py 自动创建默认角色(admin/editor)和默认菜单(7项) - 修复 research.py 缺少 enrich_topic_research 函数导致导入失败 - 修复 db_helper.py 中 generated_at 条件导致重创作不更新时间戳 - admin.html 操作列加宽防止按钮换行,平台配置增加删除按钮 - articles.html 预览弹窗加 lock-scroll=false 防止页面尺寸跳动
This commit is contained in:
+250
-110
@@ -23,11 +23,12 @@
|
||||
<h2 class="page-title"><el-icon style="vertical-align:-2px;"><IconSetting /></el-icon> 系统管理</h2>
|
||||
<div class="filter-bar">
|
||||
<el-button size="default" :type="activeTab === 'users' ? 'primary' : ''" @click="switchTab('users')">用户管理</el-button>
|
||||
<el-button size="default" :type="activeTab === 'tasklogs' ? 'primary' : ''" @click="switchTab('tasklogs')">任务日志</el-button>
|
||||
<el-button size="default" :type="activeTab === 'llmconfigs' ? 'primary' : ''" @click="switchTab('llmconfigs')">LLM配置</el-button>
|
||||
<el-button size="default" :type="activeTab === 'platformconfigs' ? 'primary' : ''" @click="switchTab('platformconfigs')">平台配置</el-button>
|
||||
<el-button size="default" :type="activeTab === 'systemconfigs' ? 'primary' : ''" @click="switchTab('systemconfigs')">系统配置</el-button>
|
||||
<el-button size="default" :type="activeTab === 'orgs' ? 'primary' : ''" @click="switchTab('orgs')">组织管理</el-button>
|
||||
<el-button size="default" :type="activeTab === 'roles' ? 'primary' : ''" @click="switchTab('roles')">角色管理</el-button>
|
||||
<el-button size="default" :type="activeTab === 'menus' ? 'primary' : ''" @click="switchTab('menus')">菜单管理</el-button>
|
||||
<el-button size="default" :type="activeTab === 'logs' ? 'primary' : ''" @click="switchTab('logs')">运行日志</el-button>
|
||||
<el-button size="default" :type="activeTab === 'assistant' ? 'primary' : ''" @click="switchTab('assistant')">AI 助手</el-button>
|
||||
</div>
|
||||
@@ -47,11 +48,23 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="paginatedUsers" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column prop="id" label="ID" width="70"></el-table-column>
|
||||
<el-table-column prop="username" label="用户名" min-width="120"></el-table-column>
|
||||
<el-table-column prop="role" label="角色" width="80"></el-table-column>
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<el-table-column prop="id" label="ID" width="60"></el-table-column>
|
||||
<el-table-column prop="username" label="用户名" min-width="100"></el-table-column>
|
||||
<el-table-column prop="role" label="角色" width="80">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.role === 'admin' ? 'danger' : 'warning'" size="small">{{ scope.row.role }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="org_id" label="组织" width="70"></el-table-column>
|
||||
<el-table-column label="创建时间" width="140">
|
||||
<template #default="scope">{{ scope.row.created_at ? new Date(scope.row.created_at).toLocaleString('zh-CN') : '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最后登录" width="140">
|
||||
<template #default="scope">{{ scope.row.last_login ? new Date(scope.row.last_login).toLocaleString('zh-CN') : '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="showEditUserDialog(scope.row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteUser(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -64,16 +77,19 @@
|
||||
<div v-for="item in paginatedUsers" :key="item.id" class="card-item">
|
||||
<div class="card-row"><span class="card-label">用户名</span><span class="card-value">{{ item.username }}</span></div>
|
||||
<div class="card-row"><span class="card-label">角色</span><span class="card-value">{{ item.role }}</span></div>
|
||||
<div class="card-row"><span class="card-label">组织</span><span class="card-value">{{ item.org_id }}</span></div>
|
||||
<div class="card-row"><span class="card-label">创建</span><span class="card-value">{{ item.created_at ? new Date(item.created_at).toLocaleDateString('zh-CN') : '-' }}</span></div>
|
||||
<div class="card-actions">
|
||||
<el-button size="small" @click="showEditUserDialog(item)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteUser(item.id)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="userDialogVisible" title="新增用户" width="450px" :close-on-click-modal="false">
|
||||
<el-dialog v-model="userDialogVisible" :title="userDialogTitle" width="450px" :close-on-click-modal="false">
|
||||
<el-form :model="userForm" label-width="80px">
|
||||
<el-form-item label="用户名"><el-input v-model="userForm.username" placeholder="至少2个字符"/></el-form-item>
|
||||
<el-form-item label="密码"><el-input v-model="userForm.password" type="password" show-password placeholder="至少6位"/></el-form-item>
|
||||
<el-form-item label="用户名"><el-input v-model="userForm.username" placeholder="至少2个字符" :disabled="userDialogTitle !== '新增用户'"/></el-form-item>
|
||||
<el-form-item :label="userDialogTitle === '新增用户' ? '密码' : '新密码'"><el-input v-model="userForm.password" type="password" show-password :placeholder="userDialogTitle === '新增用户' ? '至少6位' : '留空则不修改'"/></el-form-item>
|
||||
<el-form-item label="角色">
|
||||
<el-select v-model="userForm.role" style="width:100%">
|
||||
<el-option label="编辑" value="editor"></el-option>
|
||||
@@ -83,45 +99,11 @@
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="userDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitUser" :loading="userSubmitting">创建</el-button>
|
||||
<el-button type="primary" @click="submitUser" :loading="userSubmitting">{{ userDialogTitle === '新增用户' ? '创建' : '保存' }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'tasklogs'">
|
||||
<div class="toolbar">
|
||||
<el-button size="small" @click="loadTaskLogs()">刷新</el-button>
|
||||
</div>
|
||||
<div v-if="taskLogsLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="taskLogs.length === 0">
|
||||
<div class="empty-state">
|
||||
<el-icon style="font-size:48px;color:#c0c4cc;"><IconDocument /></el-icon>
|
||||
<div class="empty-text">暂无任务日志</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="taskLogs" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column prop="id" label="ID" width="70"></el-table-column>
|
||||
<el-table-column prop="task_name" label="任务名称" min-width="120"></el-table-column>
|
||||
<el-table-column prop="topic_id" label="选题" width="100"></el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="80"></el-table-column>
|
||||
<el-table-column prop="message" label="消息" min-width="150" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="started_at" label="开始" width="150"></el-table-column>
|
||||
<el-table-column prop="finished_at" label="结束" width="150"></el-table-column>
|
||||
<el-table-column prop="duration" label="耗时" width="80"></el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
<div class="card-list-mobile">
|
||||
<div v-for="item in taskLogs" :key="item.id" class="card-item">
|
||||
<div class="card-row"><span class="card-label">任务</span><span class="card-value">{{ item.task_name }}</span></div>
|
||||
<div class="card-row"><span class="card-label">选题</span><span class="card-value">{{ item.topic_id }}</span></div>
|
||||
<div class="card-row"><span class="card-label">状态</span><span class="card-value">{{ item.status }}</span></div>
|
||||
<div class="card-row"><span class="card-label">消息</span><span class="card-value">{{ item.message }}</span></div>
|
||||
<div class="card-row"><span class="card-label">耗时</span><span class="card-value">{{ item.duration }}s</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'llmconfigs'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showLLMConfigDialog()">新增配置</el-button>
|
||||
@@ -154,7 +136,7 @@
|
||||
<el-table-column label="系统提示词" min-width="150">
|
||||
<template #default="scope">{{ (scope.row.system_prompt || '').slice(0, 30) }}{{ (scope.row.system_prompt || '').length > 30 ? '...' : '' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="130" fixed="right">
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="showLLMConfigDialog(scope.row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteLLMConfig(scope.row.id)">删除</el-button>
|
||||
@@ -219,9 +201,10 @@
|
||||
<el-table-column prop="is_active" label="激活" width="70">
|
||||
<template #default="scope"><span>{{ scope.row.is_active ? '是' : '否' }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" fixed="right">
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="showPcDialogFn(scope.row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deletePlatformConfig(scope.row.platform)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -260,6 +243,7 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'systemconfigs'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showSystemConfigDialog()">新增配置</el-button>
|
||||
@@ -353,6 +337,108 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'roles'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showRoleDialog()">新增角色</el-button>
|
||||
<span style="font-size:13px;color:#909399;margin-left:8px;">共 {{ roles.length }} 个</span>
|
||||
</div>
|
||||
<div v-if="rolesLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="roles.length === 0">
|
||||
<div class="empty-state">
|
||||
<el-icon style="font-size:48px;color:#c0c4cc;"><IconSetting /></el-icon>
|
||||
<div class="empty-text">暂无角色,请先创建</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="roles" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column prop="id" label="ID" width="60"></el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="120"></el-table-column>
|
||||
<el-table-column prop="description" label="描述" min-width="200"></el-table-column>
|
||||
<el-table-column prop="is_system" label="系统" width="60">
|
||||
<template #default="scope">
|
||||
<el-icon v-if="scope.row.is_system" style="color:#67C23A;"><IconCheck /></el-icon>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="showRoleDialog(scope.row)" :disabled="scope.row.is_system">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteRole(scope.row.id)" :disabled="scope.row.is_system">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
<el-dialog v-model="roleDialogVisible" :title="roleDialogTitle" width="450px" :close-on-click-modal="false">
|
||||
<el-form :model="roleForm" label-width="80px">
|
||||
<el-form-item label="名称"><el-input v-model="roleForm.name" placeholder="如:editor"/></el-form-item>
|
||||
<el-form-item label="描述"><el-input v-model="roleForm.description" type="textarea" :rows="3" placeholder="角色说明"/></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="roleDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveRole" :loading="roleSaving">{{ roleDialogTitle === '新增角色' ? '创建' : '保存' }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'menus'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showMenuDialog()">新增菜单</el-button>
|
||||
<span style="font-size:13px;color:#909399;margin-left:8px;">共 {{ menus.length }} 个</span>
|
||||
</div>
|
||||
<div v-if="menusLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="menus.length === 0">
|
||||
<div class="empty-state">
|
||||
<el-icon style="font-size:48px;color:#c0c4cc;"><IconMenu /></el-icon>
|
||||
<div class="empty-text">暂无菜单,请先创建</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="menus" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column prop="id" label="ID" width="60"></el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="120"></el-table-column>
|
||||
<el-table-column prop="path" label="路径" min-width="150"></el-table-column>
|
||||
<el-table-column prop="icon" label="图标" width="80"></el-table-column>
|
||||
<el-table-column prop="sort_order" label="排序" width="60"></el-table-column>
|
||||
<el-table-column prop="roles" label="可见角色" min-width="160">
|
||||
<template #default="scope">
|
||||
<el-tag v-for="r in (scope.row.roles || [])" :key="r" size="small" style="margin-right:4px;">{{ r }}</el-tag>
|
||||
<span v-if="!scope.row.roles || scope.row.roles.length === 0" style="color:#909399;">全部</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="is_active" label="激活" width="60">
|
||||
<template #default="scope">
|
||||
<el-icon v-if="scope.row.is_active" style="color:#67C23A;"><IconCheck /></el-icon>
|
||||
<el-icon v-else style="color:#F56C6C;"><IconClose /></el-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="showMenuDialog(scope.row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteMenu(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
<el-dialog v-model="menuDialogVisible" :title="menuDialogTitle" width="500px" :close-on-click-modal="false">
|
||||
<el-form :model="menuForm" label-width="80px">
|
||||
<el-form-item label="名称"><el-input v-model="menuForm.name" placeholder="如:仪表盘"/></el-form-item>
|
||||
<el-form-item label="路径"><el-input v-model="menuForm.path" placeholder="如:/ 或 topics.html"/></el-form-item>
|
||||
<el-form-item label="图标"><el-input v-model="menuForm.icon" placeholder="如:IconHome"/></el-form-item>
|
||||
<el-form-item label="排序"><el-input-number v-model="menuForm.sort_order" :min="0" style="width:100%"/></el-form-item>
|
||||
<el-form-item label="可见角色">
|
||||
<el-select v-model="menuForm.roles" multiple style="width:100%" placeholder="不选则全部可见">
|
||||
<el-option v-for="r in roles" :key="r.name" :label="r.name" :value="r.name"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="激活"><el-switch v-model="menuForm.is_active"/></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="menuDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveMenu" :loading="menuSaving">{{ menuDialogTitle === '新增菜单' ? '创建' : '保存' }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'logs'">
|
||||
<div class="toolbar">
|
||||
<el-select v-model="logType" placeholder="日志类型" style="width:200px;">
|
||||
@@ -499,21 +585,12 @@
|
||||
|
||||
const redirectToPage = (page) => { window.location.href = page.startsWith('/') ? page : '/' + page; };
|
||||
|
||||
const taskLogs = ref([]);
|
||||
const taskLogsLoading = ref(false);
|
||||
const loadTaskLogs = async () => {
|
||||
taskLogsLoading.value = true;
|
||||
try { taskLogs.value = await api.get('/api/admin/tasklogs'); } catch (e) { ElMessage.error('加载任务日志失败: ' + e.message); }
|
||||
finally { taskLogsLoading.value = false; }
|
||||
};
|
||||
|
||||
const llmConfigs = ref([]);
|
||||
const llmConfigs = ref([]);
|
||||
const llmConfigsLoading = ref(false);
|
||||
const llmConfigDialogVisible = ref(false);
|
||||
const llmConfigDialogTitle = ref('新增配置');
|
||||
const llmConfigDialogTitle = ref('新增 LLM 配置');
|
||||
const llmConfigForm = reactive({ id: null, name: '', system_prompt: '', user_prompt_template: '', temperature: 0.7, max_tokens: 131072, model: '', provider: 'opencode-go', base_url: '', api_key: '', is_active: true });
|
||||
const editingLLMConfigId = ref(null);
|
||||
|
||||
const loadLLMConfigs = async () => {
|
||||
llmConfigsLoading.value = true;
|
||||
try { llmConfigs.value = await api.get('/api/admin/llmconfigs'); } catch (e) { ElMessage.error('加载LLM配置失败: ' + e.message); }
|
||||
@@ -522,14 +599,10 @@
|
||||
const showLLMConfigDialog = (row = null) => {
|
||||
if (row) {
|
||||
llmConfigDialogTitle.value = '编辑配置'; editingLLMConfigId.value = row.id;
|
||||
llmConfigForm.id = row.id; llmConfigForm.name = row.name; llmConfigForm.provider = row.provider || 'opencode-go';
|
||||
llmConfigForm.model = row.model || ''; llmConfigForm.base_url = row.base_url || ''; llmConfigForm.api_key = '';
|
||||
llmConfigForm.temperature = row.temperature ?? 0.7; llmConfigForm.max_tokens = row.max_tokens ?? 131072;
|
||||
llmConfigForm.system_prompt = row.system_prompt || ''; llmConfigForm.user_prompt_template = row.user_prompt_template || '';
|
||||
llmConfigForm.is_active = row.is_active ?? true;
|
||||
Object.assign(llmConfigForm, row);
|
||||
} else {
|
||||
llmConfigDialogTitle.value = '新增配置'; editingLLMConfigId.value = null;
|
||||
llmConfigForm.id = null; llmConfigForm.name = ''; llmConfigForm.provider = 'opencode-go'; llmConfigForm.model = 'deepseek-v4-pro';
|
||||
llmConfigForm.id = null; llmConfigForm.name = ''; llmConfigForm.provider = 'opencode-go'; llmConfigForm.model = 'deepseek-v4-pro';
|
||||
llmConfigForm.base_url = 'https://opencode.ai/zen/go/v1'; llmConfigForm.api_key = ''; llmConfigForm.temperature = 0.7;
|
||||
llmConfigForm.max_tokens = 131072; llmConfigForm.system_prompt = ''; llmConfigForm.user_prompt_template = ''; llmConfigForm.is_active = true;
|
||||
}
|
||||
@@ -550,42 +623,38 @@
|
||||
const platformConfigs = ref([]);
|
||||
const platformConfigsLoading = ref(false);
|
||||
const platformConfigsRequested = ref(false);
|
||||
const pcShowActiveOnly = ref(true);
|
||||
const showPcDialog = ref(false);
|
||||
const pcDialogTitle = ref('新增配置');
|
||||
const pcShowActiveOnly = ref(false);
|
||||
const pcForm = ref({ platform: '', name: '', icon: '', website_url: '', api_endpoint: '', min_words: 300, max_words: 3000, requires_image: false, image_count_min: 0, image_count_max: 0, default_format: '', is_active: true });
|
||||
const pcDialogTitle = ref('新增平台');
|
||||
const editingPcPlatform = ref(null);
|
||||
const loadPlatformConfigs = async () => {
|
||||
platformConfigsLoading.value = true;
|
||||
platformConfigsRequested.value = true;
|
||||
try {
|
||||
const url = pcShowActiveOnly.value ? '/api/platform-config?active_only=true' : '/api/platform-config';
|
||||
const data = await api.get(url);
|
||||
platformConfigs.value = data;
|
||||
} catch (e) {
|
||||
ElMessage.error('加载平台配置失败: ' + e.message);
|
||||
}
|
||||
finally { platformConfigsLoading.value = false; }
|
||||
};
|
||||
const showPcDialog = ref(false);
|
||||
const showPcDialogFn = (row = null) => {
|
||||
try {
|
||||
if (row) {
|
||||
pcDialogTitle.value = '编辑配置'; editingPcPlatform.value = row.platform;
|
||||
pcForm.value = { ...row };
|
||||
} else {
|
||||
pcDialogTitle.value = '新增配置'; editingPcPlatform.value = null;
|
||||
pcForm.value = { platform: '', name: '', icon: '', website_url: '', api_endpoint: '', min_words: 300, max_words: 3000, requires_image: false, image_count_min: 0, image_count_max: 0, default_format: '', is_active: true };
|
||||
}
|
||||
showPcDialog.value = true;
|
||||
} catch (e) { console.error('showPcDialogFn error:', e); }
|
||||
if (row) { pcDialogTitle.value = '编辑平台'; editingPcPlatform.value = row.platform; pcForm.value = { ...row }; }
|
||||
else { pcDialogTitle.value = '新增平台'; editingPcPlatform.value = null; pcForm.value = { platform: '', name: '', icon: '', website_url: '', api_endpoint: '', min_words: 300, max_words: 3000, requires_image: false, image_count_min: 0, image_count_max: 0, default_format: '', is_active: true }; }
|
||||
showPcDialog.value = true;
|
||||
};
|
||||
const savePcForm = async () => {
|
||||
try {
|
||||
if (editingPcPlatform.value) { await api.put(`/api/platform-config/${pcForm.value.platform}`, pcForm.value); ElMessage.success('更新成功'); }
|
||||
if (editingPcPlatform.value) { await api.put(`/api/platform-config/${editingPcPlatform.value}`, pcForm.value); ElMessage.success('更新成功'); }
|
||||
else { await api.post('/api/platform-config', pcForm.value); ElMessage.success('创建成功'); }
|
||||
showPcDialog.value = false; await loadPlatformConfigs();
|
||||
} catch (e) { ElMessage.error('保存失败: ' + e.message); }
|
||||
};
|
||||
const loadPlatformConfigs = async () => {
|
||||
platformConfigsLoading.value = true;
|
||||
try { platformConfigs.value = await api.get('/api/platform-config'); } catch (e) { ElMessage.error('加载平台配置失败: ' + e.message); }
|
||||
finally { platformConfigsLoading.value = false; platformConfigsRequested.value = true; }
|
||||
};
|
||||
const deletePlatformConfig = async (platform) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要停用平台配置「${platform}」吗?`, '确认删除', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' });
|
||||
await api.delete(`/api/platform-config/${platform}`);
|
||||
ElMessage.success('已停用');
|
||||
await loadPlatformConfigs();
|
||||
} catch (e) {
|
||||
if (e !== 'cancel') ElMessage.error('删除失败: ' + (e.message || e));
|
||||
}
|
||||
};
|
||||
|
||||
const systemConfigs = ref([]);
|
||||
const systemConfigsLoading = ref(false);
|
||||
@@ -600,7 +669,12 @@
|
||||
const orgDialogTitle = ref('新增组织');
|
||||
const orgForm = reactive({ org_id: '', name: '', description: '' });
|
||||
const editingOrgId = ref(null);
|
||||
|
||||
const orgPage = ref(1);
|
||||
const orgPageSize = 10;
|
||||
const paginatedOrgs = computed(() => {
|
||||
const start = (orgPage.value - 1) * orgPageSize;
|
||||
return orgs.value.slice(start, start + orgPageSize);
|
||||
});
|
||||
const loadOrgs = async () => {
|
||||
orgsLoading.value = true;
|
||||
try { orgs.value = await api.get('/api/admin/orgs'); } catch (e) { ElMessage.error('加载组织失败: ' + e.message); }
|
||||
@@ -612,26 +686,87 @@
|
||||
orgDialogVisible.value = true;
|
||||
};
|
||||
const saveOrg = async () => {
|
||||
if (!orgForm.org_id || !orgForm.name) { ElMessage.warning('请填写组织ID和名称'); return; }
|
||||
try {
|
||||
if (editingOrgId.value) { await api.put(`/api/admin/orgs/${editingOrgId.value}`, orgForm); ElMessage.success('更新成功'); }
|
||||
else { await api.post('/api/admin/orgs', orgForm); ElMessage.success('创建成功'); }
|
||||
orgDialogVisible.value = false; await loadOrgs();
|
||||
} catch (e) { ElMessage.error('保存失败: ' + e.message); }
|
||||
};
|
||||
const deleteOrg = async (orgId) => {
|
||||
try { await ElMessageBox.confirm('确定删除该组织吗?关联的用户和选题不会被删除。', '提示', { type: 'warning' }); await api.delete(`/api/admin/orgs/${orgId}`); ElMessage.success('删除成功'); await loadOrgs(); }
|
||||
const deleteOrg = async (id) => {
|
||||
try { await ElMessageBox.confirm('确定删除该组织吗?', '提示', { type: 'warning' }); await api.delete(`/api/admin/orgs/${id}`); ElMessage.success('删除成功'); await loadOrgs(); }
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
const orgPage = ref(1);
|
||||
const orgPageSize = 10;
|
||||
const paginatedOrgs = computed(() => { const s = (orgPage.value - 1) * orgPageSize; return orgs.value.slice(s, s + orgPageSize); });
|
||||
|
||||
const roles = ref([]);
|
||||
const rolesLoading = ref(false);
|
||||
const roleDialogVisible = ref(false);
|
||||
const roleDialogTitle = ref('新增角色');
|
||||
const roleSaving = ref(false);
|
||||
const roleForm = reactive({ id: null, name: '', description: '' });
|
||||
const loadRoles = async () => {
|
||||
rolesLoading.value = true;
|
||||
try { roles.value = await api.get('/api/admin/roles'); } catch (e) { ElMessage.error('加载角色失败: ' + e.message); }
|
||||
finally { rolesLoading.value = false; }
|
||||
};
|
||||
const showRoleDialog = (row = null) => {
|
||||
if (row) { roleDialogTitle.value = '编辑角色'; roleForm.id = row.id; roleForm.name = row.name; roleForm.description = row.description || ''; }
|
||||
else { roleDialogTitle.value = '新增角色'; roleForm.id = null; roleForm.name = ''; roleForm.description = ''; }
|
||||
roleDialogVisible.value = true;
|
||||
};
|
||||
const saveRole = async () => {
|
||||
if (!roleForm.name) { ElMessage.warning('请输入角色名称'); return; }
|
||||
roleSaving.value = true;
|
||||
try {
|
||||
if (roleForm.id) { await api.put(`/api/admin/roles/${roleForm.id}`, { name: roleForm.name, description: roleForm.description }); ElMessage.success('更新成功'); }
|
||||
else { await api.post('/api/admin/roles', { name: roleForm.name, description: roleForm.description }); ElMessage.success('创建成功'); }
|
||||
roleDialogVisible.value = false; await loadRoles();
|
||||
} catch (e) { ElMessage.error('保存失败: ' + e.message); }
|
||||
finally { roleSaving.value = false; }
|
||||
};
|
||||
const deleteRole = async (id) => {
|
||||
try { await ElMessageBox.confirm('确定删除该角色吗?', '提示', { type: 'warning' }); await api.delete(`/api/admin/roles/${id}`); ElMessage.success('删除成功'); await loadRoles(); }
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
|
||||
const menus = ref([]);
|
||||
const menusLoading = ref(false);
|
||||
const menuDialogVisible = ref(false);
|
||||
const menuDialogTitle = ref('新增菜单');
|
||||
const menuSaving = ref(false);
|
||||
const menuForm = reactive({ id: null, name: '', path: '', icon: '', sort_order: 0, roles: [], is_active: true });
|
||||
const loadMenus = async () => {
|
||||
menusLoading.value = true;
|
||||
try { menus.value = await api.get('/api/admin/menus'); } catch (e) { ElMessage.error('加载菜单失败: ' + e.message); }
|
||||
finally { menusLoading.value = false; }
|
||||
};
|
||||
const showMenuDialog = (row = null) => {
|
||||
if (row) { menuDialogTitle.value = '编辑菜单'; menuForm.id = row.id; menuForm.name = row.name; menuForm.path = row.path; menuForm.icon = row.icon || ''; menuForm.sort_order = row.sort_order || 0; menuForm.roles = row.roles || []; menuForm.is_active = row.is_active !== false; }
|
||||
else { menuDialogTitle.value = '新增菜单'; menuForm.id = null; menuForm.name = ''; menuForm.path = ''; menuForm.icon = ''; menuForm.sort_order = 0; menuForm.roles = []; menuForm.is_active = true; }
|
||||
menuDialogVisible.value = true;
|
||||
};
|
||||
const saveMenu = async () => {
|
||||
if (!menuForm.name || !menuForm.path) { ElMessage.warning('请填写名称和路径'); return; }
|
||||
menuSaving.value = true;
|
||||
try {
|
||||
const body = { name: menuForm.name, path: menuForm.path, icon: menuForm.icon, sort_order: menuForm.sort_order, roles: menuForm.roles, is_active: menuForm.is_active };
|
||||
if (menuForm.id) { await api.put(`/api/admin/menus/${menuForm.id}`, body); ElMessage.success('更新成功'); }
|
||||
else { await api.post('/api/admin/menus', body); ElMessage.success('创建成功'); }
|
||||
menuDialogVisible.value = false; await loadMenus();
|
||||
} catch (e) { ElMessage.error('保存失败: ' + e.message); }
|
||||
finally { menuSaving.value = false; }
|
||||
};
|
||||
const deleteMenu = async (id) => {
|
||||
try { await ElMessageBox.confirm('确定删除该菜单吗?', '提示', { type: 'warning' }); await api.delete(`/api/admin/menus/${id}`); ElMessage.success('删除成功'); await loadMenus(); }
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
|
||||
const logType = ref('creator');
|
||||
const users = ref([]);
|
||||
const usersLoading = ref(false);
|
||||
const userDialogVisible = ref(false);
|
||||
const userDialogTitle = ref('新增用户');
|
||||
const userSubmitting = ref(false);
|
||||
const userForm = reactive({ username: '', password: '', role: 'editor' });
|
||||
const userForm = reactive({ id: null, username: '', password: '', role: 'editor' });
|
||||
const userPage = ref(1);
|
||||
const userPageSize = 10;
|
||||
const paginatedUsers = computed(() => {
|
||||
@@ -643,17 +778,24 @@
|
||||
try { users.value = await api.get('/api/admin/users'); } catch (e) { ElMessage.error('加载用户失败: ' + e.message); }
|
||||
finally { usersLoading.value = false; }
|
||||
};
|
||||
const addUser = () => { userForm.username = ''; userForm.password = ''; userForm.role = 'editor'; userDialogVisible.value = true; };
|
||||
const addUser = () => { userDialogTitle.value = '新增用户'; userForm.id = null; userForm.username = ''; userForm.password = ''; userForm.role = 'editor'; userDialogVisible.value = true; };
|
||||
const showEditUserDialog = (row) => { userDialogTitle.value = '编辑用户'; userForm.id = row.id; userForm.username = row.username; userForm.password = ''; userForm.role = row.role; userDialogVisible.value = true; };
|
||||
const submitUser = async () => {
|
||||
if (!userForm.username || userForm.username.length < 2) { ElMessage.warning('用户名至少2个字符'); return; }
|
||||
if (!userForm.password || userForm.password.length < 6) { ElMessage.warning('密码至少6位'); return; }
|
||||
if (userDialogTitle.value === '新增用户' && (!userForm.password || userForm.password.length < 6)) { ElMessage.warning('密码至少6位'); return; }
|
||||
userSubmitting.value = true;
|
||||
try {
|
||||
const u = await api.post('/api/admin/users', { ...userForm });
|
||||
users.value.push(u);
|
||||
ElMessage.success('创建成功');
|
||||
userDialogVisible.value = false;
|
||||
} catch (e) { ElMessage.error('创建失败: ' + e.message); }
|
||||
if (userForm.id) {
|
||||
const body = { username: userForm.username, role: userForm.role };
|
||||
if (userForm.password) body.password = userForm.password;
|
||||
await api.put(`/api/admin/users/${userForm.id}`, body);
|
||||
ElMessage.success('更新成功');
|
||||
} else {
|
||||
await api.post('/api/admin/users', { ...userForm });
|
||||
ElMessage.success('创建成功');
|
||||
}
|
||||
userDialogVisible.value = false; await fetchUsers();
|
||||
} catch (e) { ElMessage.error((userForm.id ? '更新' : '创建') + '失败: ' + e.message); }
|
||||
finally { userSubmitting.value = false; }
|
||||
};
|
||||
const deleteUser = async (id) => {
|
||||
@@ -665,8 +807,6 @@
|
||||
ElMessage.success('删除成功');
|
||||
} catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
|
||||
const logType = ref('creator');
|
||||
const logDate = ref(new Date().toISOString().slice(0, 10));
|
||||
const logContent = ref('');
|
||||
const logsLoading = ref(false);
|
||||
@@ -735,10 +875,9 @@
|
||||
const logout = () => { localStorage.removeItem('authToken'); localStorage.removeItem('userRole'); localStorage.removeItem('currentUser'); window.location.href = '/login.html'; };
|
||||
|
||||
const tabLoaders = {
|
||||
tasklogs: loadTaskLogs,
|
||||
llmconfigs: loadLLMConfigs, platformconfigs: loadPlatformConfigs, systemconfigs: loadSystemConfigs,
|
||||
users: fetchUsers,
|
||||
orgs: loadOrgs, assistant: loadAssistantConfig,
|
||||
orgs: loadOrgs, roles: loadRoles, menus: loadMenus, assistant: loadAssistantConfig, logs: fetchLogs,
|
||||
};
|
||||
const loadedTabs = new Set([]);
|
||||
|
||||
@@ -760,15 +899,16 @@
|
||||
|
||||
return {
|
||||
activeTab, switchTab,
|
||||
taskLogs, taskLogsLoading, loadTaskLogs,
|
||||
llmConfigs, llmConfigsLoading, llmConfigDialogVisible, llmConfigForm, llmConfigDialogTitle, showLLMConfigDialog, saveLLMConfig, deleteLLMConfig,
|
||||
platformConfigs, platformConfigsLoading, platformConfigsRequested, pcShowActiveOnly, showPcDialog, pcForm, pcDialogTitle, showPcDialogFn, savePcForm, editingPcPlatform,
|
||||
systemConfigs, systemConfigsLoading, systemConfigDialogVisible, systemConfigForm, systemConfigDialogTitle, showSystemConfigDialog, saveSystemConfig, deleteSystemConfig,
|
||||
orgs, orgsLoading, orgDialogVisible, orgForm, orgDialogTitle, showOrgDialog, saveOrg, deleteOrg,
|
||||
orgPage, orgPageSize, paginatedOrgs,
|
||||
roles, rolesLoading, roleDialogVisible, roleDialogTitle, roleSaving, roleForm, loadRoles, showRoleDialog, saveRole, deleteRole,
|
||||
menus, menusLoading, menuDialogVisible, menuDialogTitle, menuSaving, menuForm, loadMenus, showMenuDialog, saveMenu, deleteMenu,
|
||||
logout, currentUser, isAdmin, redirectToPage,
|
||||
users, usersLoading, userDialogVisible, userSubmitting, userForm, userPage, userPageSize, paginatedUsers,
|
||||
fetchUsers, addUser, submitUser, deleteUser,
|
||||
users, usersLoading, userDialogVisible, userDialogTitle, userSubmitting, userForm, userPage, userPageSize, paginatedUsers,
|
||||
fetchUsers, addUser, showEditUserDialog, submitUser, deleteUser,
|
||||
logType, logDate, logContent, logsLoading, fetchLogs,
|
||||
assistantPrompt, assistantEnabled, assistantSaving, saveAssistantPrompt,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user