- PptSettingsAdmin.vue: full settings page with provider/model/API key config for PPT extraction, poster copy, and poster image generation - Backend: GET /insurance/admin/ppt/available-models to fetch models from Dify API (falls back to builtin list when unavailable) - LLM client: read model config from SystemSetting DB with 60s cache, priority: database > environment variables > defaults - ppt-admin-api.ts: add getAvailableModels() method Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
234 lines
7.7 KiB
Vue
234 lines
7.7 KiB
Vue
<template>
|
|
<div class="page-container">
|
|
<div class="page-header">
|
|
<h2>PPT/海报模型设置</h2>
|
|
</div>
|
|
|
|
<el-form v-loading="loading" :model="form" label-width="140px" style="max-width: 680px">
|
|
<!-- PPT 解析模型 -->
|
|
<el-divider content-position="left">PPT 解析模型</el-divider>
|
|
|
|
<el-form-item label="供应商">
|
|
<el-select v-model="form.ppt_llm_provider" placeholder="选择供应商" style="width: 100%" @change="onPptProviderChange">
|
|
<el-option label="DeepSeek" value="deepseek" />
|
|
<el-option label="MiniMax" value="minimax" />
|
|
<el-option label="Gemini" value="gemini" />
|
|
<el-option label="OpenAI" value="openai" />
|
|
<el-option label="自定义" value="custom" />
|
|
</el-select>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="模型名称">
|
|
<div style="display: flex; gap: 8px; width: 100%">
|
|
<el-input v-model="form.ppt_llm_model" placeholder="如 deepseek-chat" />
|
|
<el-dropdown v-if="difyModels.length" trigger="click" @command="(cmd: string) => applyDifyModel('ppt', cmd)">
|
|
<el-button>从 Dify 获取 <el-icon><ArrowDown /></el-icon></el-button>
|
|
<template #dropdown>
|
|
<el-dropdown-menu>
|
|
<el-dropdown-item v-for="m in difyModels" :key="m.provider + '/' + m.model"
|
|
:command="m.provider + '|' + m.model">
|
|
{{ m.label }} ({{ m.provider }}/{{ m.model }})
|
|
</el-dropdown-item>
|
|
</el-dropdown-menu>
|
|
</template>
|
|
</el-dropdown>
|
|
<el-button v-else @click="fetchDifyModels" :loading="fetchingModels">从 Dify 获取</el-button>
|
|
</div>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="API Key">
|
|
<el-input v-model="form.ppt_llm_api_key" type="password" show-password placeholder="留空则使用环境变量配置" />
|
|
</el-form-item>
|
|
|
|
<el-form-item v-if="form.ppt_llm_provider === 'custom'" label="Base URL">
|
|
<el-input v-model="form.ppt_llm_base_url" placeholder="如 https://api.openai.com/v1" />
|
|
</el-form-item>
|
|
|
|
<!-- 海报文案模型 -->
|
|
<el-divider content-position="left">海报文案模型</el-divider>
|
|
|
|
<el-form-item label="供应商">
|
|
<el-select v-model="form.poster_llm_provider" placeholder="选择供应商" style="width: 100%" @change="onPosterProviderChange">
|
|
<el-option label="DeepSeek" value="deepseek" />
|
|
<el-option label="MiniMax" value="minimax" />
|
|
<el-option label="Gemini" value="gemini" />
|
|
<el-option label="OpenAI" value="openai" />
|
|
<el-option label="自定义" value="custom" />
|
|
</el-select>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="模型名称">
|
|
<div style="display: flex; gap: 8px; width: 100%">
|
|
<el-input v-model="form.poster_llm_model" placeholder="如 deepseek-chat" />
|
|
<el-dropdown v-if="difyModels.length" trigger="click" @command="(cmd: string) => applyDifyModel('poster', cmd)">
|
|
<el-button>从 Dify 获取 <el-icon><ArrowDown /></el-icon></el-button>
|
|
<template #dropdown>
|
|
<el-dropdown-menu>
|
|
<el-dropdown-item v-for="m in difyModels" :key="m.provider + '/' + m.model"
|
|
:command="m.provider + '|' + m.model">
|
|
{{ m.label }} ({{ m.provider }}/{{ m.model }})
|
|
</el-dropdown-item>
|
|
</el-dropdown-menu>
|
|
</template>
|
|
</el-dropdown>
|
|
</div>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="API Key">
|
|
<el-input v-model="form.poster_llm_api_key" type="password" show-password placeholder="留空则使用环境变量配置" />
|
|
</el-form-item>
|
|
|
|
<el-form-item v-if="form.poster_llm_provider === 'custom'" label="Base URL">
|
|
<el-input v-model="form.poster_llm_base_url" placeholder="如 https://api.openai.com/v1" />
|
|
</el-form-item>
|
|
|
|
<!-- 海报图片模型 -->
|
|
<el-divider content-position="left">海报图片模型</el-divider>
|
|
|
|
<el-form-item label="模型名称">
|
|
<el-input v-model="form.poster_image_model" placeholder="默认 gpt-image-1" />
|
|
</el-form-item>
|
|
|
|
<el-form-item label="API Key">
|
|
<el-input v-model="form.poster_image_api_key" type="password" show-password placeholder="留空则使用环境变量配置" />
|
|
</el-form-item>
|
|
|
|
<!-- Dify 集成 -->
|
|
<el-divider content-position="left">Dify 集成</el-divider>
|
|
|
|
<el-form-item label="Workspace API Key">
|
|
<el-input v-model="form.dify_workspace_api_key" type="password" show-password placeholder="用于从 Dify 获取可用模型列表" />
|
|
</el-form-item>
|
|
|
|
<el-form-item>
|
|
<el-button type="primary" @click="onSave" :loading="saving">保存设置</el-button>
|
|
<el-button @click="loadSettings">重置</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
import { ElMessage } from 'element-plus'
|
|
import { ArrowDown } from '@element-plus/icons-vue'
|
|
import { pptAdminApi } from '@/utils/ppt-admin-api'
|
|
|
|
const loading = ref(false)
|
|
const saving = ref(false)
|
|
const fetchingModels = ref(false)
|
|
const difyModels = ref<Array<{ provider: string; model: string; label: string }>>([])
|
|
|
|
const form = reactive({
|
|
ppt_llm_provider: 'deepseek',
|
|
ppt_llm_model: '',
|
|
ppt_llm_api_key: '',
|
|
ppt_llm_base_url: '',
|
|
poster_llm_provider: 'deepseek',
|
|
poster_llm_model: '',
|
|
poster_llm_api_key: '',
|
|
poster_llm_base_url: '',
|
|
poster_image_model: 'gpt-image-1',
|
|
poster_image_api_key: '',
|
|
dify_workspace_api_key: '',
|
|
})
|
|
|
|
const DEFAULT_MODELS: Record<string, string> = {
|
|
deepseek: 'deepseek-chat',
|
|
minimax: 'MiniMax-2.7-Flash',
|
|
gemini: 'gemini-2.5-flash',
|
|
openai: 'gpt-4o',
|
|
}
|
|
|
|
function onPptProviderChange(val: string) {
|
|
if (val !== 'custom' && !form.ppt_llm_model) {
|
|
form.ppt_llm_model = DEFAULT_MODELS[val] || ''
|
|
}
|
|
if (val !== 'custom') {
|
|
form.ppt_llm_base_url = ''
|
|
}
|
|
}
|
|
|
|
function onPosterProviderChange(val: string) {
|
|
if (val !== 'custom' && !form.poster_llm_model) {
|
|
form.poster_llm_model = DEFAULT_MODELS[val] || ''
|
|
}
|
|
if (val !== 'custom') {
|
|
form.poster_llm_base_url = ''
|
|
}
|
|
}
|
|
|
|
function applyDifyModel(target: 'ppt' | 'poster', command: string) {
|
|
const [provider, model] = command.split('|')
|
|
if (target === 'ppt') {
|
|
form.ppt_llm_provider = provider
|
|
form.ppt_llm_model = model
|
|
} else {
|
|
form.poster_llm_provider = provider
|
|
form.poster_llm_model = model
|
|
}
|
|
}
|
|
|
|
async function fetchDifyModels() {
|
|
fetchingModels.value = true
|
|
try {
|
|
const res: any = await pptAdminApi.getAvailableModels()
|
|
difyModels.value = res?.data?.data?.models || []
|
|
if (!difyModels.value.length) {
|
|
ElMessage.warning('未获取到可用模型,请检查 Dify Workspace API Key 配置')
|
|
}
|
|
} catch {
|
|
ElMessage.error('获取模型列表失败')
|
|
} finally {
|
|
fetchingModels.value = false
|
|
}
|
|
}
|
|
|
|
async function loadSettings() {
|
|
loading.value = true
|
|
try {
|
|
const res: any = await pptAdminApi.getSettings()
|
|
const data = res?.data?.data || {}
|
|
for (const key of Object.keys(form)) {
|
|
if (data[key]) {
|
|
;(form as any)[key] = data[key]
|
|
}
|
|
}
|
|
} catch {
|
|
ElMessage.error('加载配置失败')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function onSave() {
|
|
saving.value = true
|
|
try {
|
|
await pptAdminApi.updateSettings({ ...form })
|
|
ElMessage.success('保存成功')
|
|
} catch {
|
|
ElMessage.error('保存失败')
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(loadSettings)
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page-container {
|
|
padding: 24px;
|
|
}
|
|
.page-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 24px;
|
|
}
|
|
.page-header h2 {
|
|
margin: 0;
|
|
font-size: 20px;
|
|
}
|
|
</style>
|