Add platform config website_url, admin tab, fix writer DB config fallback, add trigger endpoints
This commit is contained in:
+168
-18
@@ -25,12 +25,14 @@
|
||||
<el-button size="default" :type="activeTab === 'cases' ? 'primary' : ''" @click="switchTab('cases')">案例管理</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>
|
||||
|
||||
@@ -53,10 +55,10 @@
|
||||
</div>
|
||||
<el-table :data="paginatedCases" 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="120" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="field" label="领域" width="100"></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="100"></el-table-column>
|
||||
<el-table-column prop="source" label="来源" width="140"></el-table-column>
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button size="small" @click="showCaseDialog(scope.row)">编辑</el-button>
|
||||
@@ -129,18 +131,25 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="llmConfigs" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column prop="id" label="ID" width="70"></el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="120"></el-table-column>
|
||||
<el-table-column prop="model" label="模型" min-width="180"></el-table-column>
|
||||
<el-table-column prop="temperature" label="温度" width="80"></el-table-column>
|
||||
<el-table-column prop="max_tokens" label="最大Token" width="110"></el-table-column>
|
||||
<el-table-column prop="is_active" label="激活" width="70">
|
||||
<el-table-column prop="id" label="ID" width="60"></el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="100"></el-table-column>
|
||||
<el-table-column prop="provider" label="供应商" width="110"></el-table-column>
|
||||
<el-table-column prop="model" label="模型" min-width="160"></el-table-column>
|
||||
<el-table-column prop="base_url" label="接口地址" min-width="200">
|
||||
<template #default="scope">{{ scope.row.base_url ? scope.row.base_url.replace(/^https?:\/\//, '') : '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="temperature" label="温度" width="70"></el-table-column>
|
||||
<el-table-column prop="max_tokens" label="最大Token" width="100"></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="140" fixed="right">
|
||||
<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">
|
||||
<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>
|
||||
@@ -151,8 +160,9 @@
|
||||
<div class="card-list-mobile">
|
||||
<div v-for="item in llmConfigs" :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.provider }}</span></div>
|
||||
<div class="card-row"><span class="card-label">模型</span><span class="card-value">{{ item.model }}</span></div>
|
||||
<div class="card-row"><span class="card-label">温度</span><span class="card-value">{{ item.temperature }}</span></div>
|
||||
<div class="card-row"><span class="card-label">温度/最大Token</span><span class="card-value">{{ item.temperature }} / {{ item.max_tokens }}</span></div>
|
||||
<div class="card-row"><span class="card-label">激活</span><span class="card-value">{{ item.is_active ? '是' : '否' }}</span></div>
|
||||
<div class="card-actions">
|
||||
<el-button size="small" @click="showLLMConfigDialog(item)">编辑</el-button>
|
||||
@@ -162,6 +172,45 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'platformconfigs'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showPlatformConfigDialog()">新增配置</el-button>
|
||||
</div>
|
||||
<div v-if="platformConfigsLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="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>
|
||||
</div>
|
||||
<div v-if="activeTab === 'systemconfigs'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showSystemConfigDialog()">新增配置</el-button>
|
||||
@@ -396,6 +445,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'assistant'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="saveAssistantPrompt" :loading="assistantSaving">保存配置</el-button>
|
||||
<span style="font-size:13px;color:#909399;margin-left:8px;">AI 助手在各页面右下角显示悬浮按钮,点击可打开对话窗口</span>
|
||||
</div>
|
||||
<el-form label-width="120px" style="max-width:700px;margin-top:16px;">
|
||||
<el-form-item label="系统提示词">
|
||||
<el-input type="textarea" v-model="assistantPrompt" :rows="12" placeholder="设置 AI 助手的角色和行为说明,留空则使用默认提示词"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="启用助手" v-if="false">
|
||||
<el-switch v-model="assistantEnabled"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'sources'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showSourceDialog()">新增信息源</el-button>
|
||||
@@ -500,7 +564,7 @@
|
||||
<el-col :span="12"><el-form-item label="温度"><el-input-number v-model="llmConfigForm.temperature" :min="0" :max="2" :step="0.1" style="width:100%"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="最大Token"><el-input-number v-model="llmConfigForm.max_tokens" :min="1" :max="10000" style="width:100%"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="最大Token"><el-input-number v-model="llmConfigForm.max_tokens" :min="1" :max="999999" style="width:100%"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="激活"><el-switch v-model="llmConfigForm.is_active"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
@@ -516,6 +580,39 @@
|
||||
</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">
|
||||
@@ -666,7 +763,7 @@
|
||||
const llmConfigsLoading = ref(false);
|
||||
const llmConfigDialogVisible = ref(false);
|
||||
const llmConfigDialogTitle = ref('新增配置');
|
||||
const llmConfigForm = reactive({ id: null, name: '', system_prompt: '', user_prompt_template: '', temperature: 0.7, max_tokens: 2000, model: '', provider: 'opencode-go', base_url: '', api_key: '', is_active: true });
|
||||
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 () => {
|
||||
@@ -679,14 +776,14 @@
|
||||
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 ?? 2000;
|
||||
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;
|
||||
} else {
|
||||
llmConfigDialogTitle.value = '新增配置'; editingLLMConfigId.value = null;
|
||||
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 = 2000; llmConfigForm.system_prompt = ''; llmConfigForm.user_prompt_template = ''; llmConfigForm.is_active = true;
|
||||
llmConfigForm.max_tokens = 131072; llmConfigForm.system_prompt = ''; llmConfigForm.user_prompt_template = ''; llmConfigForm.is_active = true;
|
||||
}
|
||||
llmConfigDialogVisible.value = true;
|
||||
};
|
||||
@@ -702,6 +799,37 @@
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
|
||||
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 loadPlatformConfigs = async () => {
|
||||
platformConfigsLoading.value = true;
|
||||
try { platformConfigs.value = await api.get('/api/platform-config'); } 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 () => {
|
||||
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();
|
||||
} catch (e) { ElMessage.error('保存失败: ' + e.message); }
|
||||
};
|
||||
|
||||
const systemConfigs = ref([]);
|
||||
const systemConfigsLoading = ref(false);
|
||||
const systemConfigDialogVisible = ref(false);
|
||||
@@ -841,6 +969,25 @@
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
|
||||
const assistantPrompt = ref('');
|
||||
const assistantEnabled = ref(true);
|
||||
const assistantSaving = ref(false);
|
||||
const loadAssistantConfig = async () => {
|
||||
try {
|
||||
const configs = await api.get('/api/admin/systemconfigs');
|
||||
const cfg = configs.find(c => c.key === 'assistant_system_prompt');
|
||||
assistantPrompt.value = cfg ? (typeof cfg.value === 'string' ? cfg.value : '') : '';
|
||||
} catch (e) { console.error('加载AI助手配置失败', e); }
|
||||
};
|
||||
const saveAssistantPrompt = async () => {
|
||||
assistantSaving.value = true;
|
||||
try {
|
||||
await api.post('/api/admin/systemconfigs', { key: 'assistant_system_prompt', value: assistantPrompt.value, description: 'AI 助手系统提示词' });
|
||||
ElementPlus.ElMessage.success('保存成功');
|
||||
} catch (e) { ElementPlus.ElMessage.error('保存失败: ' + e.message); }
|
||||
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); }
|
||||
@@ -896,10 +1043,10 @@
|
||||
|
||||
const tabLoaders = {
|
||||
cases: loadCases, tasklogs: loadTaskLogs,
|
||||
llmconfigs: loadLLMConfigs, systemconfigs: loadSystemConfigs,
|
||||
llmconfigs: loadLLMConfigs, platformconfigs: loadPlatformConfigs, systemconfigs: loadSystemConfigs,
|
||||
users: fetchUsers,
|
||||
categories: loadCategories, sources: loadSources,
|
||||
orgs: loadOrgs,
|
||||
orgs: loadOrgs, assistant: loadAssistantConfig,
|
||||
};
|
||||
const loadedTabs = new Set(['cases']);
|
||||
|
||||
@@ -924,6 +1071,7 @@
|
||||
casePage, casePageSize, paginatedCases,
|
||||
taskLogs, taskLogsLoading, loadTaskLogs,
|
||||
llmConfigs, llmConfigsLoading, llmConfigDialogVisible, llmConfigForm, llmConfigDialogTitle, showLLMConfigDialog, saveLLMConfig, deleteLLMConfig,
|
||||
platformConfigs, platformConfigsLoading, platformConfigDialogVisible, platformConfigForm, platformConfigDialogTitle, showPlatformConfigDialog, savePlatformConfig, editingPlatformConfigPlatform,
|
||||
systemConfigs, systemConfigsLoading, systemConfigDialogVisible, systemConfigForm, systemConfigDialogTitle, showSystemConfigDialog, saveSystemConfig, deleteSystemConfig,
|
||||
categories, categoriesLoading, categoryDialogVisible, categoryForm, categoryDialogTitle, showCategoryDialog, saveCategory, deleteCategory,
|
||||
categoryPage, categoryPageSize, paginatedCategories,
|
||||
@@ -934,7 +1082,8 @@
|
||||
logout, currentUser, isAdmin, redirectToPage,
|
||||
users, usersLoading, userDialogVisible, userSubmitting, userForm, userPage, userPageSize, paginatedUsers,
|
||||
fetchUsers, addUser, submitUser, deleteUser,
|
||||
logType, logDate, logContent, logsLoading, fetchLogs
|
||||
logType, logDate, logContent, logsLoading, fetchLogs,
|
||||
assistantPrompt, assistantEnabled, assistantSaving, saveAssistantPrompt,
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -944,5 +1093,6 @@
|
||||
if (window.installUniNav) { window.installUniNav(app); }
|
||||
app.mount('#app');
|
||||
</script>
|
||||
<script src="ai-assistant.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user