配置全面迁移数据库:PromptConfig、TaskConfig动态调度、敏感词/清洗规则/趋势映射/平台标签/痛点模板全部可编辑
- 新增 PromptConfig 模型 + API,支持提示词在线编辑(16条默认) - 调度器动态读取 TaskConfig.schedule,admin 可调执行时间 - 新增 KeywordDomainMap、SensitiveWord、ContentCleanRule、TrendFieldMapping 表 - DOMAINS、TREND_DOMAIN_MAP、PLATFORM_TAGS、china_pains、RSS关键词、priority_weights 全部迁移到 DB - tasks.html 重构:卡片网格+配置/产出/历史/提示词四个Tab,折叠显示 - 清理冗余代码:DEFAULT_PROMPTS死代码、collector.py unreachable代码、compliance_checker bug - strip_thinking_html 改用 DB 规则优先
This commit is contained in:
+148
-461
@@ -22,65 +22,70 @@
|
||||
<div class="page-header">
|
||||
<h2 class="page-title"><el-icon style="vertical-align:-2px;"><IconSetting /></el-icon> 系统管理</h2>
|
||||
<div class="filter-bar">
|
||||
<el-button size="default" :type="activeTab === 'cases' ? 'primary' : ''" @click="switchTab('cases')">案例管理</el-button>
|
||||
<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 === 'users' ? 'primary' : ''" @click="switchTab('users')">用户管理</el-button>
|
||||
<el-button size="default" :type="activeTab === 'categories' ? 'primary' : ''" @click="switchTab('categories')">采集类别</el-button>
|
||||
<el-button size="default" :type="activeTab === 'sources' ? 'primary' : ''" @click="switchTab('sources')">信息源</el-button>
|
||||
<el-button size="default" :type="activeTab === 'orgs' ? 'primary' : ''" @click="switchTab('orgs')">组织管理</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>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'cases'">
|
||||
<div v-if="activeTab === 'users'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showCaseDialog()">新增案例</el-button>
|
||||
<span style="font-size:13px;color:#909399;margin-left:8px;">共 {{ cases.length }} 个</span>
|
||||
<el-button type="primary" size="small" @click="addUser">新增用户</el-button>
|
||||
<span style="font-size:13px;color:#909399;margin-left:8px;">共 {{ users.length }} 个</span>
|
||||
</div>
|
||||
<div v-if="casesLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="cases.length === 0">
|
||||
<div v-if="usersLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="users.length === 0">
|
||||
<div class="empty-state">
|
||||
<el-icon style="font-size:48px;color:#c0c4cc;"><IconTopic /></el-icon>
|
||||
<div class="empty-text">暂无案例数据</div>
|
||||
<el-button type="primary" size="small" @click="showCaseDialog()">新增第一个案例</el-button>
|
||||
<el-icon style="font-size:48px;color:#c0c4cc;"><IconUser /></el-icon>
|
||||
<div class="empty-text">暂无用户数据</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="stat-summary">
|
||||
<div class="stat-item"><span class="num">{{ cases.length }}</span><span class="label">总案例</span></div>
|
||||
</div>
|
||||
<el-table :data="paginatedCases" border stripe class="data-table" style="width:100%">
|
||||
<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="title" label="标题" min-width="80" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="field" label="领域" width="140"></el-table-column>
|
||||
<el-table-column prop="summary" label="概述" min-width="200" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="source" label="来源" width="140"></el-table-column>
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<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">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="showCaseDialog(scope.row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteCase(scope.row.id)">删除</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteUser(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="cases.length > casePageSize" style="display:flex;justify-content:center;margin:12px 0;">
|
||||
<el-pagination background layout="prev, pager, next" :total="cases.length" :page-size="casePageSize" :current-page="casePage" @current-change="casePage = $event"></el-pagination>
|
||||
<div v-if="users.length > userPageSize" style="display:flex;justify-content:center;margin:12px 0;">
|
||||
<el-pagination background layout="prev, pager, next" :total="users.length" :page-size="userPageSize" :current-page="userPage" @current-change="userPage = $event"></el-pagination>
|
||||
</div>
|
||||
</template>
|
||||
<div class="card-list-mobile">
|
||||
<div v-for="item in paginatedCases" :key="item.id" class="card-item">
|
||||
<div class="card-row"><span class="card-label">标题</span><span class="card-value">{{ item.title }}</span></div>
|
||||
<div class="card-row"><span class="card-label">领域</span><span class="card-value">{{ item.field }}</span></div>
|
||||
<div class="card-row"><span class="card-label">来源</span><span class="card-value">{{ item.source }}</span></div>
|
||||
<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-actions">
|
||||
<el-button size="small" @click="showCaseDialog(item)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteCase(item.id)">删除</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-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-select v-model="userForm.role" style="width:100%">
|
||||
<el-option label="编辑" value="editor"></el-option>
|
||||
<el-option label="管理员" value="admin"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="userDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitUser" :loading="userSubmitting">创建</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'tasklogs'">
|
||||
@@ -173,43 +178,87 @@
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'platformconfigs'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showPlatformConfigDialog()">新增配置</el-button>
|
||||
<div class="page-header">
|
||||
<h2 class="page-title"><el-icon style="vertical-align:-2px;"><IconGlobe /></el-icon> 平台配置</h2>
|
||||
<div class="toolbar">
|
||||
<el-button-group>
|
||||
<el-button :type="pcShowActiveOnly ? 'primary' : ''" @click="pcShowActiveOnly = true; loadPlatformConfigs()">启用中</el-button>
|
||||
<el-button :type="!pcShowActiveOnly ? 'primary' : ''" @click="pcShowActiveOnly = false; loadPlatformConfigs()">全部</el-button>
|
||||
</el-button-group>
|
||||
<el-button type="primary" size="small" @click="showPcDialogFn()">新增配置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="platformConfigsLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="platformConfigs.length === 0">
|
||||
<template v-else-if="!platformConfigs || platformConfigs.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="platformConfigs" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column prop="platform" label="标识" width="80"></el-table-column>
|
||||
<el-table-column prop="name" label="名称" width="80"></el-table-column>
|
||||
<el-table-column prop="website_url" label="平台网址" min-width="160">
|
||||
<template #default="scope"><a :href="scope.row.website_url" target="_blank" style="color:#409eff;">{{ scope.row.website_url }}</a></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="字数范围" width="120">
|
||||
<template #default="scope">{{ scope.row.min_words }} - {{ scope.row.max_words }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="配图" width="100">
|
||||
<template #default="scope">{{ scope.row.requires_image ? `${scope.row.image_count_min}-${scope.row.image_count_max}张` : '不需要' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="api_endpoint" label="API地址" min-width="160"></el-table-column>
|
||||
<el-table-column prop="default_format" label="默认格式" min-width="160">
|
||||
<template #default="scope">{{ (scope.row.default_format || '').slice(0, 30) }}{{ (scope.row.default_format || '').length > 30 ? '...' : '' }}</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="100" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="showPlatformConfigDialog(scope.row)">编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
<el-table v-else :data="platformConfigs" border stripe style="width:100%">
|
||||
<el-table-column prop="platform" label="标识" width="120"></el-table-column>
|
||||
<el-table-column prop="name" label="名称" width="120"></el-table-column>
|
||||
<el-table-column prop="icon" label="图标" width="100">
|
||||
<template #default="scope"><span>{{ scope.row.icon || '—' }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="website_url" label="平台网址" min-width="160">
|
||||
<template #default="scope"><a v-if="scope.row.website_url" :href="scope.row.website_url" target="_blank" style="color:#409eff;">{{ scope.row.website_url }}</a><span v-else>—</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="api_endpoint" label="API地址" min-width="160">
|
||||
<template #default="scope"><span style="color:#606266;">{{ scope.row.api_endpoint || '—' }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="字数范围" width="120">
|
||||
<template #default="scope">{{ scope.row.min_words }} - {{ scope.row.max_words }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="配图" width="120">
|
||||
<template #default="scope">{{ scope.row.requires_image ? `${scope.row.image_count_min}-${scope.row.image_count_max}张` : '不需要' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="default_format" label="默认格式" min-width="180">
|
||||
<template #default="scope">{{ (scope.row.default_format || '').slice(0, 50) }}{{ (scope.row.default_format || '').length > 50 ? '...' : '' }}</template>
|
||||
</el-table-column>
|
||||
<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">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="showPcDialogFn(scope.row)">编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-dialog v-model="showPcDialog" :title="pcDialogTitle" width="750px" :close-on-click-modal="false">
|
||||
<el-form :model="pcForm" label-width="100px">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="标识"><el-input v-model="pcForm.platform" placeholder="唯一标识,如 weixin" :disabled="!!editingPcPlatform"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="名称"><el-input v-model="pcForm.name" placeholder="微信公众号"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="图标"><el-input v-model="pcForm.icon" placeholder="IconWechat"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="官网"><el-input v-model="pcForm.website_url" placeholder="https://mp.weixin.qq.com"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24"><el-form-item label="API地址"><el-input v-model="pcForm.api_endpoint" placeholder="https://..."/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="最少字数"><el-input-number v-model="pcForm.min_words" :min="0" :max="99999" style="width:100%"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="最多字数"><el-input-number v-model="pcForm.max_words" :min="0" :max="99999" style="width:100%"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8"><el-form-item label="需要配图"><el-switch v-model="pcForm.requires_image"/></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="最少张数"><el-input-number v-model="pcForm.image_count_min" :min="0" :max="99" style="width:100%"/></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="最多张数"><el-input-number v-model="pcForm.image_count_max" :min="0" :max="99" style="width:100%"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24"><el-form-item label="默认格式"><el-input type="textarea" v-model="pcForm.default_format" :rows="4" placeholder="文章格式要求"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="激活"><el-switch v-model="pcForm.is_active"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="showPcDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="savePcForm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<div v-if="activeTab === 'systemconfigs'">
|
||||
<div class="toolbar">
|
||||
@@ -253,121 +302,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'users'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="addUser">+ 新建用户</el-button>
|
||||
<el-button size="small" @click="fetchUsers">刷新</el-button>
|
||||
<span style="font-size:13px;color:#909399;margin-left:8px;">共 {{ users.length }} 个</span>
|
||||
</div>
|
||||
<div v-if="usersLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="users.length === 0">
|
||||
<div class="empty-state">
|
||||
<el-icon style="font-size:48px;color:#c0c4cc;"><IconUser /></el-icon>
|
||||
<div class="empty-text">暂无用户</div>
|
||||
<el-button type="primary" size="small" @click="addUser">新增第一个用户</el-button>
|
||||
</div>
|
||||
</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="用户名"></el-table-column>
|
||||
<el-table-column prop="role" label="角色" width="100">
|
||||
<template #default="scope"><el-tag :type="scope.row.role === 'admin' ? 'danger' : 'info'">{{ scope.row.role === 'admin' ? '管理员' : '编辑' }}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="org_id" label="组织" width="100"></el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间"><template #default="scope">{{ formatDate(scope.row.created_at) }}</template></el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template #default="scope">
|
||||
<el-button size="small" type="danger" @click="deleteUser(scope.row.id)" :disabled="scope.row.role === 'admin'">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="users.length > userPageSize" style="display:flex;justify-content:center;margin:12px 0;">
|
||||
<el-pagination background layout="prev, pager, next" :total="users.length" :page-size="userPageSize" :current-page="userPage" @current-change="userPage = $event"></el-pagination>
|
||||
</div>
|
||||
<div class="card-list-mobile">
|
||||
<div v-for="u in users" :key="u.id" class="card-item">
|
||||
<div class="card-row"><span class="card-label">用户名</span><span class="card-value">{{ u.username }} <el-tag size="small" :type="u.role === 'admin' ? 'danger' : 'info'">{{ u.role === 'admin' ? '管理员' : '编辑' }}</el-tag></span></div>
|
||||
<div class="card-row"><span class="card-label">组织</span><span class="card-value">{{ u.org_id || '-' }}</span></div>
|
||||
<div class="card-row"><span class="card-label">创建时间</span><span class="card-value">{{ formatDate(u.created_at) }}</span></div>
|
||||
<div class="card-actions">
|
||||
<el-button size="small" type="danger" plain @click="deleteUser(u.id)" :disabled="u.role === 'admin'">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-dialog v-model="userDialogVisible" title="新建用户" width="400px" :close-on-click-modal="false">
|
||||
<el-form :model="userForm" label-width="80px">
|
||||
<el-form-item label="用户名" required>
|
||||
<el-input v-model="userForm.username" placeholder="2-20个字符" maxlength="20" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" required>
|
||||
<el-input v-model="userForm.password" type="password" placeholder="至少6位" show-password></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色">
|
||||
<el-select v-model="userForm.role" style="width:100%">
|
||||
<el-option label="编辑" value="editor"></el-option>
|
||||
<el-option label="管理员" value="admin"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="userDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitUser" :loading="userSubmitting">创建</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'categories'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showCategoryDialog()">新增类别</el-button>
|
||||
<el-button size="small" @click="loadCategories">刷新</el-button>
|
||||
<span style="font-size:13px;color:#909399;margin-left:8px;">共 {{ categories.length }} 个</span>
|
||||
</div>
|
||||
<div v-if="categoriesLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="categories.length === 0">
|
||||
<div class="empty-state">
|
||||
<el-icon style="font-size:48px;color:#c0c4cc;"><IconPicture /></el-icon>
|
||||
<div class="empty-text">暂无采集类别</div>
|
||||
<el-button type="primary" size="small" @click="showCategoryDialog()">新增类别</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="paginatedCategories" 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="search_query" label="搜索词" min-width="200"></el-table-column>
|
||||
<el-table-column prop="source_count" label="信息源数" width="100"></el-table-column>
|
||||
<el-table-column prop="is_active" label="激活" width="70">
|
||||
<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="140" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="showCategoryDialog(scope.row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteCategory(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="categories.length > categoryPageSize" style="display:flex;justify-content:center;margin:12px 0;">
|
||||
<el-pagination background layout="prev, pager, next" :total="categories.length" :page-size="categoryPageSize" :current-page="categoryPage" @current-change="categoryPage = $event"></el-pagination>
|
||||
</div>
|
||||
</template>
|
||||
<div class="card-list-mobile" v-if="categories.length > 0">
|
||||
<div v-for="item in paginatedCategories" :key="item.id" class="card-item">
|
||||
<div class="card-row"><span class="card-label">名称</span><span class="card-value">{{ item.name }}</span></div>
|
||||
<div class="card-row"><span class="card-label">搜索词</span><span class="card-value">{{ item.search_query }}</span></div>
|
||||
<div class="card-row"><span class="card-label">源数</span><span class="card-value">{{ item.source_count }}</span></div>
|
||||
<div class="card-actions">
|
||||
<el-button size="small" @click="showCategoryDialog(item)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteCategory(item.id)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'orgs'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showOrgDialog()">新增组织</el-button>
|
||||
@@ -460,90 +394,11 @@
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'sources'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showSourceDialog()">新增信息源</el-button>
|
||||
<el-button size="small" @click="loadSources">刷新</el-button>
|
||||
<span style="font-size:13px;color:#909399;margin-left:8px;">共 {{ sources.length }} 个</span>
|
||||
</div>
|
||||
<div v-if="sourcesLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="sources.length === 0">
|
||||
<div class="empty-state">
|
||||
<el-icon style="font-size:48px;color:#c0c4cc;"><IconGlobe /></el-icon>
|
||||
<div class="empty-text">暂无信息源</div>
|
||||
<el-button type="primary" size="small" @click="showSourceDialog()">新增信息源</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="paginatedSources" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column prop="id" label="ID" width="50"></el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="130"></el-table-column>
|
||||
<el-table-column prop="source_type" label="类型" width="90">
|
||||
<template #default="scope">{{ {rss:'RSS',web_search:'搜索',local:'本地'}[scope.row.source_type] || scope.row.source_type }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="query" label="查询词/URL" min-width="250" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="focus" label="聚焦" min-width="120"></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="120" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="showSourceDialog(scope.row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteSource(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="sources.length > sourcePageSize" style="display:flex;justify-content:center;margin:12px 0;">
|
||||
<el-pagination background layout="prev, pager, next" :total="sources.length" :page-size="sourcePageSize" :current-page="sourcePage" @current-change="sourcePage = $event"></el-pagination>
|
||||
</div>
|
||||
</template>
|
||||
<div class="card-list-mobile" v-if="sources.length > 0">
|
||||
<div v-for="item in paginatedSources" :key="item.id" class="card-item">
|
||||
<div class="card-row"><span class="card-label">名称</span><span class="card-value">{{ item.name }}</span></div>
|
||||
<div class="card-row"><span class="card-label">类型</span><span class="card-value">{{ item.source_type }}</span></div>
|
||||
<div class="card-row"><span class="card-label">查询</span><span class="card-value">{{ item.query || item.url }}</span></div>
|
||||
<div class="card-actions">
|
||||
<el-button size="small" @click="showSourceDialog(item)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteSource(item.id)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="caseDialogVisible" :title="caseDialogTitle" width="700px" :close-on-click-modal="false">
|
||||
<el-form :model="caseForm" label-width="80px">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="标题"><el-input v-model="caseForm.title"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="领域"><el-input v-model="caseForm.field"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24"><el-form-item label="概述"><el-input type="textarea" v-model="caseForm.summary" :rows="3"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="关键指标"><el-input v-model="caseForm.key_metrics"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="日期"><el-input v-model="caseForm.date"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="来源"><el-input v-model="caseForm.source"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="来源URL"><el-input v-model="caseForm.source_url"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="可信度"><el-input v-model="caseForm.credibility_rating"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="国内适用性"><el-input v-model="caseForm.china_applicability"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="caseDialogVisible=false">取消</el-button>
|
||||
<el-button type="primary" @click="saveCase">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="llmConfigDialogVisible" :title="llmConfigDialogTitle" width="750px" :close-on-click-modal="false">
|
||||
<el-form :model="llmConfigForm" label-width="110px">
|
||||
<el-row :gutter="16">
|
||||
@@ -580,39 +435,6 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="platformConfigDialogVisible" :title="platformConfigDialogTitle" width="750px" :close-on-click-modal="false">
|
||||
<el-form :model="platformConfigForm" label-width="110px">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8"><el-form-item label="标识"><el-input v-model="platformConfigForm.platform" :disabled="!!editingPlatformConfigPlatform" placeholder="zhihu"/></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="名称"><el-input v-model="platformConfigForm.name" placeholder="知乎"/></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="图标"><el-input v-model="platformConfigForm.icon" placeholder="📕"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="平台网址"><el-input v-model="platformConfigForm.website_url" placeholder="https://www.zhihu.com"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="API地址"><el-input v-model="platformConfigForm.api_endpoint" placeholder="https://api.zhihu.com"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="最少字数"><el-input-number v-model="platformConfigForm.min_words" :min="0" :max="50000" style="width:100%"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="最多字数"><el-input-number v-model="platformConfigForm.max_words" :min="0" :max="50000" style="width:100%"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="需要配图"><el-switch v-model="platformConfigForm.requires_image"/></el-form-item></el-col>
|
||||
<el-col :span="6"><el-form-item label="最少"><el-input-number v-model="platformConfigForm.image_count_min" :min="0" :max="20" style="width:100%"/></el-form-item></el-col>
|
||||
<el-col :span="6"><el-form-item label="最多"><el-input-number v-model="platformConfigForm.image_count_max" :min="0" :max="20" style="width:100%"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24"><el-form-item label="默认格式"><el-input type="textarea" v-model="platformConfigForm.default_format" :rows="2" placeholder="图文笔记,300-800字,emoji+标签"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24"><el-form-item label="活跃"><el-switch v-model="platformConfigForm.is_active"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="platformConfigDialogVisible=false">取消</el-button>
|
||||
<el-button type="primary" @click="savePlatformConfig">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="systemConfigDialogVisible" :title="systemConfigDialogTitle" width="600px" :close-on-click-modal="false">
|
||||
<el-form :model="systemConfigForm" label-width="80px">
|
||||
<el-row :gutter="16">
|
||||
@@ -629,48 +451,6 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="categoryDialogVisible" :title="categoryDialogTitle" width="500px" :close-on-click-modal="false">
|
||||
<el-form :model="categoryForm" label-width="80px">
|
||||
<el-form-item label="名称"><el-input v-model="categoryForm.name" placeholder="如:循环消费"/></el-form-item>
|
||||
<el-form-item label="搜索词"><el-input v-model="categoryForm.search_query" placeholder="如:以旧换新 二手交易 闲置 2026"/></el-form-item>
|
||||
<el-form-item label="描述"><el-input type="textarea" v-model="categoryForm.description" :rows="3"/></el-form-item>
|
||||
<el-form-item label="排序"><el-input-number v-model="categoryForm.sort_order" :min="0"/></el-form-item>
|
||||
<el-form-item label="激活"><el-switch v-model="categoryForm.is_active"/></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="categoryDialogVisible=false">取消</el-button>
|
||||
<el-button type="primary" @click="saveCategory">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="sourceDialogVisible" :title="sourceDialogTitle" width="550px" :close-on-click-modal="false">
|
||||
<el-form :model="sourceForm" label-width="80px">
|
||||
<el-form-item label="名称"><el-input v-model="sourceForm.name" placeholder="如:循环消费搜索"/></el-form-item>
|
||||
<el-form-item label="类型">
|
||||
<el-select v-model="sourceForm.source_type" style="width:100%">
|
||||
<el-option label="RSS订阅" value="rss"></el-option>
|
||||
<el-option label="搜索引擎" value="web_search"></el-option>
|
||||
<el-option label="本地文件" value="local"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="查询词/URL"><el-input v-model="sourceForm.query" :placeholder="sourceForm.source_type==='web_search'?'搜索关键词':'RSS URL或本地路径'"/></el-form-item>
|
||||
<el-form-item label="聚焦领域"><el-input v-model="sourceForm.focus"/></el-form-item>
|
||||
<el-form-item label="可信度">
|
||||
<el-select v-model="sourceForm.credibility" style="width:100%">
|
||||
<el-option label="高" value="high"></el-option>
|
||||
<el-option label="中" value="medium"></el-option>
|
||||
<el-option label="低" value="low"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序"><el-input-number v-model="sourceForm.sort_order" :min="0"/></el-form-item>
|
||||
<el-form-item label="激活"><el-switch v-model="sourceForm.is_active"/></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="sourceDialogVisible=false">取消</el-button>
|
||||
<el-button type="primary" @click="saveSource">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="orgDialogVisible" :title="orgDialogTitle" width="500px" :close-on-click-modal="false">
|
||||
<el-form :model="orgForm" label-width="80px">
|
||||
<el-form-item label="组织ID"><el-input v-model="orgForm.org_id" :disabled="!!editingOrgId" placeholder="唯一标识,如:org_a"/></el-form-item>
|
||||
@@ -703,7 +483,7 @@
|
||||
delete: (url) => fetch(apiBase + url, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }).then(r => { if (!r.ok) throw new Error(r.statusText); return r.json(); }),
|
||||
};
|
||||
|
||||
const activeTab = ref('cases');
|
||||
const activeTab = ref('users');
|
||||
const currentUser = ref({ username: '' });
|
||||
const isAdmin = ref(false);
|
||||
|
||||
@@ -719,38 +499,6 @@
|
||||
|
||||
const redirectToPage = (page) => { window.location.href = page.startsWith('/') ? page : '/' + page; };
|
||||
|
||||
const cases = ref([]);
|
||||
const casesLoading = ref(false);
|
||||
const caseDialogVisible = ref(false);
|
||||
const caseDialogTitle = ref('新增案例');
|
||||
const caseForm = reactive({ id: null, title: '', field: '', summary: '', key_metrics: '', date: '', source: '', source_url: '', credibility_rating: '', china_applicability: '' });
|
||||
const editingCaseId = ref(null);
|
||||
|
||||
const loadCases = async () => {
|
||||
casesLoading.value = true;
|
||||
try { cases.value = await api.get('/api/admin/cases'); } catch (e) { ElMessage.error('加载案例失败: ' + e.message); }
|
||||
finally { casesLoading.value = false; }
|
||||
};
|
||||
const showCaseDialog = (row = null) => {
|
||||
if (row) { caseDialogTitle.value = '编辑案例'; editingCaseId.value = row.id; Object.assign(caseForm, row); }
|
||||
else { caseDialogTitle.value = '新增案例'; editingCaseId.value = null; Object.keys(caseForm).forEach(k => { if (k === 'id') caseForm.id = null; else caseForm[k] = ''; }); }
|
||||
caseDialogVisible.value = true;
|
||||
};
|
||||
const saveCase = async () => {
|
||||
try {
|
||||
if (editingCaseId.value) { await api.put(`/api/admin/cases/${editingCaseId.value}`, caseForm); ElMessage.success('更新成功'); }
|
||||
else { await api.post('/api/admin/cases', caseForm); ElMessage.success('创建成功'); }
|
||||
caseDialogVisible.value = false; await loadCases();
|
||||
} catch (e) { ElMessage.error('保存失败: ' + e.message); }
|
||||
};
|
||||
const deleteCase = async (id) => {
|
||||
try { await ElMessageBox.confirm('确定删除该案例吗?', '提示', { type: 'warning' }); await api.delete(`/api/admin/cases/${id}`); ElMessage.success('删除成功'); await loadCases(); }
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
const casePage = ref(1);
|
||||
const casePageSize = 10;
|
||||
const paginatedCases = computed(() => { const s = (casePage.value - 1) * casePageSize; return cases.value.slice(s, s + casePageSize); });
|
||||
|
||||
const taskLogs = ref([]);
|
||||
const taskLogsLoading = ref(false);
|
||||
const loadTaskLogs = async () => {
|
||||
@@ -801,32 +549,41 @@
|
||||
|
||||
const platformConfigs = ref([]);
|
||||
const platformConfigsLoading = ref(false);
|
||||
const platformConfigDialogVisible = ref(false);
|
||||
const platformConfigDialogTitle = ref('新增配置');
|
||||
const platformConfigForm = reactive({ 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 editingPlatformConfigPlatform = ref(null);
|
||||
const platformConfigsRequested = ref(false);
|
||||
const pcShowActiveOnly = ref(true);
|
||||
const showPcDialog = ref(false);
|
||||
const pcDialogTitle = ref('新增配置');
|
||||
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 editingPcPlatform = ref(null);
|
||||
const loadPlatformConfigs = async () => {
|
||||
platformConfigsLoading.value = true;
|
||||
try { platformConfigs.value = await api.get('/api/platform-config'); } catch (e) { ElMessage.error('加载平台配置失败: ' + e.message); }
|
||||
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 showPlatformConfigDialog = (row = null) => {
|
||||
if (row) {
|
||||
platformConfigDialogTitle.value = '编辑配置'; editingPlatformConfigPlatform.value = row.platform;
|
||||
Object.assign(platformConfigForm, { ...row });
|
||||
} else {
|
||||
platformConfigDialogTitle.value = '新增配置'; editingPlatformConfigPlatform.value = null;
|
||||
platformConfigForm.platform = ''; platformConfigForm.name = ''; platformConfigForm.icon = ''; platformConfigForm.website_url = ''; platformConfigForm.api_endpoint = '';
|
||||
platformConfigForm.min_words = 300; platformConfigForm.max_words = 3000; platformConfigForm.requires_image = false;
|
||||
platformConfigForm.image_count_min = 0; platformConfigForm.image_count_max = 0; platformConfigForm.default_format = ''; platformConfigForm.is_active = true;
|
||||
}
|
||||
platformConfigDialogVisible.value = true;
|
||||
};
|
||||
const savePlatformConfig = async () => {
|
||||
const showPcDialogFn = (row = null) => {
|
||||
try {
|
||||
if (editingPlatformConfigPlatform.value) { await api.put(`/api/platform-config/${platformConfigForm.platform}`, platformConfigForm); ElMessage.success('更新成功'); }
|
||||
else { await api.post('/api/platform-config', platformConfigForm); ElMessage.success('创建成功'); }
|
||||
platformConfigDialogVisible.value = false; await loadPlatformConfigs();
|
||||
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); }
|
||||
};
|
||||
const savePcForm = async () => {
|
||||
try {
|
||||
if (editingPcPlatform.value) { await api.put(`/api/platform-config/${pcForm.value.platform}`, 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); }
|
||||
};
|
||||
|
||||
@@ -837,20 +594,6 @@
|
||||
const systemConfigForm = reactive({ key: '', value: '', description: '' });
|
||||
const editingSystemConfigKey = ref(null);
|
||||
|
||||
const categories = ref([]);
|
||||
const categoriesLoading = ref(false);
|
||||
const categoryDialogVisible = ref(false);
|
||||
const categoryDialogTitle = ref('新增类别');
|
||||
const categoryForm = reactive({ name: '', search_query: '', description: '', sort_order: 0, is_active: true });
|
||||
const editingCategoryId = ref(null);
|
||||
|
||||
const sources = ref([]);
|
||||
const sourcesLoading = ref(false);
|
||||
const sourceDialogVisible = ref(false);
|
||||
const sourceDialogTitle = ref('新增信息源');
|
||||
const sourceForm = reactive({ name: '', source_type: 'web_search', query: '', credibility: 'medium', focus: '', sort_order: 0, is_active: true });
|
||||
const editingSourceId = ref(null);
|
||||
|
||||
const orgs = ref([]);
|
||||
const orgsLoading = ref(false);
|
||||
const orgDialogVisible = ref(false);
|
||||
@@ -988,95 +731,39 @@
|
||||
finally { assistantSaving.value = false; }
|
||||
};
|
||||
|
||||
const loadCategories = async () => {
|
||||
categoriesLoading.value = true;
|
||||
try { categories.value = await api.get('/api/admin/collector/categories'); } catch (e) { ElMessage.error('加载类别失败: ' + e.message); }
|
||||
finally { categoriesLoading.value = false; }
|
||||
};
|
||||
const showCategoryDialog = (row = null) => {
|
||||
if (row) { categoryDialogTitle.value = '编辑类别'; editingCategoryId.value = row.id; Object.assign(categoryForm, { name: row.name, search_query: row.search_query || '', description: row.description || '', sort_order: row.sort_order || 0, is_active: row.is_active }); }
|
||||
else { categoryDialogTitle.value = '新增类别'; editingCategoryId.value = null; categoryForm.name = ''; categoryForm.search_query = ''; categoryForm.description = ''; categoryForm.sort_order = 0; categoryForm.is_active = true; }
|
||||
categoryDialogVisible.value = true;
|
||||
};
|
||||
const saveCategory = async () => {
|
||||
try {
|
||||
if (editingCategoryId.value) { await api.put(`/api/admin/collector/categories/${editingCategoryId.value}`, categoryForm); ElMessage.success('更新成功'); }
|
||||
else { await api.post('/api/admin/collector/categories', categoryForm); ElMessage.success('创建成功'); }
|
||||
categoryDialogVisible.value = false; await loadCategories();
|
||||
} catch (e) { ElMessage.error('保存失败: ' + e.message); }
|
||||
};
|
||||
const deleteCategory = async (id) => {
|
||||
try { await ElMessageBox.confirm('确定删除该类别及其关联的信息源吗?', '提示', { type: 'warning' }); await api.delete(`/api/admin/collector/categories/${id}`); ElMessage.success('删除成功'); await loadCategories(); }
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
const categoryPage = ref(1);
|
||||
const categoryPageSize = 10;
|
||||
const paginatedCategories = computed(() => { const s = (categoryPage.value - 1) * categoryPageSize; return categories.value.slice(s, s + categoryPageSize); });
|
||||
|
||||
const loadSources = async () => {
|
||||
sourcesLoading.value = true;
|
||||
try { sources.value = await api.get('/api/admin/collector/sources'); } catch (e) { ElMessage.error('加载信息源失败: ' + e.message); }
|
||||
finally { sourcesLoading.value = false; }
|
||||
};
|
||||
const showSourceDialog = (row = null) => {
|
||||
if (row) { sourceDialogTitle.value = '编辑信息源'; editingSourceId.value = row.id; Object.assign(sourceForm, { name: row.name, source_type: row.source_type, query: row.query || '', credibility: row.credibility || 'medium', focus: row.focus || '', sort_order: row.sort_order || 0, is_active: row.is_active }); }
|
||||
else { sourceDialogTitle.value = '新增信息源'; editingSourceId.value = null; sourceForm.name = ''; sourceForm.source_type = 'web_search'; sourceForm.query = ''; sourceForm.credibility = 'medium'; sourceForm.focus = ''; sourceForm.sort_order = 0; sourceForm.is_active = true; }
|
||||
sourceDialogVisible.value = true;
|
||||
};
|
||||
const saveSource = async () => {
|
||||
try {
|
||||
if (editingSourceId.value) { await api.put(`/api/admin/collector/sources/${editingSourceId.value}`, sourceForm); ElMessage.success('更新成功'); }
|
||||
else { await api.post('/api/admin/collector/sources', sourceForm); ElMessage.success('创建成功'); }
|
||||
sourceDialogVisible.value = false; await loadSources();
|
||||
} catch (e) { ElMessage.error('保存失败: ' + e.message); }
|
||||
};
|
||||
const deleteSource = async (id) => {
|
||||
try { await ElMessageBox.confirm('确定删除该信息源吗?', '提示', { type: 'warning' }); await api.delete(`/api/admin/collector/sources/${id}`); ElMessage.success('删除成功'); await loadSources(); }
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
const sourcePage = ref(1);
|
||||
const sourcePageSize = 10;
|
||||
const paginatedSources = computed(() => { const s = (sourcePage.value - 1) * sourcePageSize; return sources.value.slice(s, s + sourcePageSize); });
|
||||
|
||||
const formatDate = (dateStr) => { if (!dateStr) return '-'; return new Date(dateStr.replace(' ', 'T')).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); };
|
||||
const logout = () => { localStorage.removeItem('authToken'); localStorage.removeItem('userRole'); localStorage.removeItem('currentUser'); window.location.href = '/login.html'; };
|
||||
|
||||
const tabLoaders = {
|
||||
cases: loadCases, tasklogs: loadTaskLogs,
|
||||
tasklogs: loadTaskLogs,
|
||||
llmconfigs: loadLLMConfigs, platformconfigs: loadPlatformConfigs, systemconfigs: loadSystemConfigs,
|
||||
users: fetchUsers,
|
||||
categories: loadCategories, sources: loadSources,
|
||||
orgs: loadOrgs, assistant: loadAssistantConfig,
|
||||
};
|
||||
const loadedTabs = new Set(['cases']);
|
||||
const loadedTabs = new Set([]);
|
||||
|
||||
const switchTab = (name) => {
|
||||
console.log('[switchTab]', name, 'loadedTabs has?', loadedTabs.has(name));
|
||||
activeTab.value = name;
|
||||
if (!loadedTabs.has(name)) {
|
||||
loadedTabs.add(name);
|
||||
tabLoaders[name]();
|
||||
if (tabLoaders[name]) tabLoaders[name]();
|
||||
else console.warn('[switchTab] no loader for', name);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadedTabs.add('cases');
|
||||
loadCases();
|
||||
const hash = window.location.hash.replace('#tab=', '');
|
||||
if (hash && tabLoaders[hash]) { switchTab(hash); }
|
||||
else { switchTab('users'); }
|
||||
});
|
||||
|
||||
return {
|
||||
activeTab, switchTab,
|
||||
cases, casesLoading, caseDialogVisible, caseForm, caseDialogTitle, showCaseDialog, saveCase, deleteCase,
|
||||
casePage, casePageSize, paginatedCases,
|
||||
taskLogs, taskLogsLoading, loadTaskLogs,
|
||||
llmConfigs, llmConfigsLoading, llmConfigDialogVisible, llmConfigForm, llmConfigDialogTitle, showLLMConfigDialog, saveLLMConfig, deleteLLMConfig,
|
||||
platformConfigs, platformConfigsLoading, platformConfigDialogVisible, platformConfigForm, platformConfigDialogTitle, showPlatformConfigDialog, savePlatformConfig, editingPlatformConfigPlatform,
|
||||
platformConfigs, platformConfigsLoading, platformConfigsRequested, pcShowActiveOnly, showPcDialog, pcForm, pcDialogTitle, showPcDialogFn, savePcForm, editingPcPlatform,
|
||||
systemConfigs, systemConfigsLoading, systemConfigDialogVisible, systemConfigForm, systemConfigDialogTitle, showSystemConfigDialog, saveSystemConfig, deleteSystemConfig,
|
||||
categories, categoriesLoading, categoryDialogVisible, categoryForm, categoryDialogTitle, showCategoryDialog, saveCategory, deleteCategory,
|
||||
categoryPage, categoryPageSize, paginatedCategories,
|
||||
sources, sourcesLoading, sourceDialogVisible, sourceForm, sourceDialogTitle, showSourceDialog, saveSource, deleteSource,
|
||||
sourcePage, sourcePageSize, paginatedSources,
|
||||
orgs, orgsLoading, orgDialogVisible, orgForm, orgDialogTitle, showOrgDialog, saveOrg, deleteOrg,
|
||||
orgPage, orgPageSize, paginatedOrgs,
|
||||
logout, currentUser, isAdmin, redirectToPage,
|
||||
|
||||
Reference in New Issue
Block a user