feat(ui): 新增 LLM 智能增强设置页 + 路由/侧栏 + ArticleDetail 展示排版/分类/插图/点评
This commit is contained in:
183
frontend/src/views/AdminLlmSettings.vue
Normal file
183
frontend/src/views/AdminLlmSettings.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import {
|
||||
NCard, NSpace, NButton, NInput, NInputNumber, NSwitch, NAlert, useMessage, NSpin, NDivider, NText, NCode,
|
||||
} from 'naive-ui'
|
||||
import { adminApi, type LlmSetting } from '@/api/articles'
|
||||
|
||||
const message = useMessage()
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const testing = ref(false)
|
||||
const setting = ref<LlmSetting>({
|
||||
format_prompt: '',
|
||||
classify_prompt: '',
|
||||
commentary_prompt: '',
|
||||
image_prompt_template: '',
|
||||
image_size: '1024x768',
|
||||
chat_model: 'agnes-2.0-flash',
|
||||
image_model: 'agnes-image-2.1-flash',
|
||||
interval_sec: 2.0,
|
||||
enabled: true,
|
||||
})
|
||||
|
||||
const testResult = ref<{ ok: boolean; detail: string; configured: boolean } | null>(null)
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
setting.value = await adminApi.getLlmSettings()
|
||||
} catch (e: any) {
|
||||
message.error(e?.response?.data?.title || '加载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function save() {
|
||||
saving.value = true
|
||||
try {
|
||||
const updated = await adminApi.updateLlmSettings(setting.value)
|
||||
setting.value = updated
|
||||
message.success('已保存')
|
||||
} catch (e: any) {
|
||||
message.error(e?.response?.data?.title || '保存失败')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function reset() {
|
||||
if (!confirm('恢复所有提示词为默认值?(模型名 / 限速 / enabled 保留)')) return
|
||||
try {
|
||||
const out = await adminApi.resetLlmSettings()
|
||||
message.success(out.detail || '已重置')
|
||||
await load()
|
||||
} catch (e: any) {
|
||||
message.error(e?.response?.data?.title || '重置失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function test() {
|
||||
testing.value = true
|
||||
testResult.value = null
|
||||
try {
|
||||
testResult.value = await adminApi.testLlmConnection()
|
||||
if (testResult.value.ok) message.success('连接 OK')
|
||||
else message.warning('连接失败')
|
||||
} catch (e: any) {
|
||||
testResult.value = { ok: false, detail: e?.message || '请求失败', configured: true }
|
||||
message.error('测试失败')
|
||||
} finally {
|
||||
testing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NSpace vertical>
|
||||
<NSpace justify="space-between" align="center">
|
||||
<div>
|
||||
<h2 style="margin: 0">🤖 LLM 智能增强设置</h2>
|
||||
<NText depth="3" style="font-size: 12px">
|
||||
翻译完成后,自动调用 Agnes LLM 跑 4 项任务:排版 / 分类 / 插图 / 点评
|
||||
</NText>
|
||||
</div>
|
||||
<NSpace>
|
||||
<NButton :loading="testing" @click="test">测连接</NButton>
|
||||
<NButton @click="reset">重置默认提示词</NButton>
|
||||
<NButton type="primary" :loading="saving" :disabled="loading" @click="save">保存</NButton>
|
||||
</NSpace>
|
||||
</NSpace>
|
||||
|
||||
<NAlert v-if="testResult" :type="testResult.ok ? 'success' : 'warning'" :show-icon="false">
|
||||
<NText strong>测连接结果:</NText>
|
||||
<NText>{{ testResult.detail }}</NText>
|
||||
</NAlert>
|
||||
|
||||
<NSpin :show="loading">
|
||||
<NCard title="总开关 + 模型">
|
||||
<NSpace vertical>
|
||||
<NSpace align="center">
|
||||
<NText>启用 LLM 增强:</NText>
|
||||
<NSwitch v-model:value="setting.enabled" />
|
||||
<NText v-if="!setting.enabled" depth="3" style="font-size: 12px">(关闭后翻译后不再调 LLM)</NText>
|
||||
</NSpace>
|
||||
<NSpace>
|
||||
<NText>文生文模型:</NText>
|
||||
<NInput v-model:value="setting.chat_model" placeholder="agnes-2.0-flash" style="width: 240px" />
|
||||
</NSpace>
|
||||
<NSpace>
|
||||
<NText>文生图模型:</NText>
|
||||
<NInput v-model:value="setting.image_model" placeholder="agnes-image-2.1-flash" style="width: 240px" />
|
||||
</NSpace>
|
||||
<NSpace>
|
||||
<NText>插图尺寸:</NText>
|
||||
<NInput v-model:value="setting.image_size" placeholder="1024x768" style="width: 160px" />
|
||||
<NText depth="3" style="font-size: 12px">(格式: WIDTHxHEIGHT,如 1024x768)</NText>
|
||||
</NSpace>
|
||||
<NSpace>
|
||||
<NText>LLM 调用间隔(秒):</NText>
|
||||
<NInputNumber v-model:value="setting.interval_sec" :min="0" :max="60" :step="0.5" />
|
||||
<NText depth="3" style="font-size: 12px">(chat + image 各 1 个串行,每次调用后等这么久)</NText>
|
||||
</NSpace>
|
||||
</NSpace>
|
||||
</NCard>
|
||||
|
||||
<NCard title="排版提示词" style="margin-top: 16px">
|
||||
<NText depth="3" style="font-size: 12px">
|
||||
模板变量: <NCode>{body}</NCode> = 译文正文
|
||||
</NText>
|
||||
<NInput
|
||||
v-model:value="setting.format_prompt"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 6, maxRows: 20 }"
|
||||
placeholder="留空用默认"
|
||||
style="margin-top: 8px"
|
||||
/>
|
||||
</NCard>
|
||||
|
||||
<NCard title="分类提示词" style="margin-top: 16px">
|
||||
<NText depth="3" style="font-size: 12px">
|
||||
模板变量: <NCode>{title}</NCode> = 译后标题, <NCode>{summary}</NCode> = 摘要。<br />
|
||||
期望返回 JSON,形如 <NCode>{`{"categories": ["时政", "国际"]}`}</NCode>
|
||||
</NText>
|
||||
<NInput
|
||||
v-model:value="setting.classify_prompt"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 4, maxRows: 12 }"
|
||||
placeholder="留空用默认"
|
||||
style="margin-top: 8px"
|
||||
/>
|
||||
</NCard>
|
||||
|
||||
<NCard title="点评提示词" style="margin-top: 16px">
|
||||
<NText depth="3" style="font-size: 12px">
|
||||
模板变量: <NCode>{title}</NCode> = 译后标题, <NCode>{body}</NCode> = 译文正文
|
||||
</NText>
|
||||
<NInput
|
||||
v-model:value="setting.commentary_prompt"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 4, maxRows: 12 }"
|
||||
placeholder="留空用默认"
|
||||
style="margin-top: 8px"
|
||||
/>
|
||||
</NCard>
|
||||
|
||||
<NCard title="插图 prompt 模板" style="margin-top: 16px">
|
||||
<NText depth="3" style="font-size: 12px">
|
||||
模板变量: <NCode>{title}</NCode> = 标题(优先译后)。最终 prompt 会拼成英文描述发给文生图模型
|
||||
</NText>
|
||||
<NInput
|
||||
v-model:value="setting.image_prompt_template"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 3, maxRows: 8 }"
|
||||
placeholder="留空用默认"
|
||||
style="margin-top: 8px"
|
||||
/>
|
||||
</NCard>
|
||||
</NSpin>
|
||||
</NSpace>
|
||||
</template>
|
||||
Reference in New Issue
Block a user