feat(aihr): align figma pages for release
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<div class="prompt-page">
|
||||
<div class="page-heading">
|
||||
<div>
|
||||
<h1>提示词模板管理</h1>
|
||||
<p>统一维护训练、评分、复盘和准备卡模板</p>
|
||||
</div>
|
||||
<el-button type="danger" :icon="Plus" @click="createPrompt">新增模板</el-button>
|
||||
</div>
|
||||
<div class="prompt-layout">
|
||||
<section class="list-card">
|
||||
<h2>模板列表</h2>
|
||||
<button
|
||||
v-for="item in templates"
|
||||
:key="item.id"
|
||||
class="template-item"
|
||||
:class="{ active: item.id === selectedId }"
|
||||
@click="selectPrompt(item)"
|
||||
>
|
||||
<strong>{{ item.name }}</strong
|
||||
><span>{{ item.code }}</span
|
||||
><em>{{ item.enabled ? '已启用' : '已停用' }}</em>
|
||||
</button>
|
||||
<el-empty v-if="!loading && !templates.length" description="暂无模板" />
|
||||
</section>
|
||||
<section v-if="form" class="editor-card">
|
||||
<div class="editor-heading">
|
||||
<div>
|
||||
<h2>编辑:{{ form.name }}</h2>
|
||||
<p>版本 {{ form.version }} · 分类 {{ form.category }}</p>
|
||||
</div>
|
||||
<el-tag :type="form.enabled ? 'success' : 'info'">{{ form.enabled ? '启用' : '停用' }}</el-tag>
|
||||
</div>
|
||||
<div class="field-grid">
|
||||
<el-form-item label="模板代码"><el-input v-model="form.code" :disabled="form.id > 0" /></el-form-item>
|
||||
<el-form-item label="模板名称"><el-input v-model="form.name" /></el-form-item>
|
||||
<el-form-item label="输出文体"><el-input v-model="form.outputFormat" /></el-form-item>
|
||||
</div>
|
||||
<el-form-item label="系统提示词"><el-input v-model="form.systemPrompt" type="textarea" :rows="3" /></el-form-item>
|
||||
<el-form-item label="提示词模板"><el-input v-model="form.template" type="textarea" :rows="5" /></el-form-item>
|
||||
<el-form-item label="模板变量"><el-input v-model="form.variables" placeholder='["scenario","dialogue","score"]' /></el-form-item>
|
||||
<div class="check-card"><strong>变量校验</strong><span>保存前校验变量格式;未知变量不会发布到生产调用链。</span></div>
|
||||
<div class="editor-actions">
|
||||
<el-button @click="save(false)">保存草稿</el-button><el-button type="danger" :loading="saving" @click="save(true)">保存并启用</el-button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { Plus } from '@element-plus/icons-vue';
|
||||
import {
|
||||
createPromptTemplate,
|
||||
listPromptTemplates,
|
||||
updatePromptTemplate,
|
||||
type PromptTemplateRequest,
|
||||
type PromptTemplateResponse
|
||||
} from '@/api/aihr/practice';
|
||||
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const templates = ref<PromptTemplateResponse[]>([]);
|
||||
const selectedId = ref(0);
|
||||
const form = ref<PromptTemplateRequest & { id: number }>();
|
||||
|
||||
const clone = (item: PromptTemplateResponse): PromptTemplateRequest & { id: number } => ({ ...item });
|
||||
const selectPrompt = (item: PromptTemplateResponse) => {
|
||||
selectedId.value = item.id;
|
||||
form.value = reactive(clone(item));
|
||||
};
|
||||
const load = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await listPromptTemplates();
|
||||
templates.value = result.data ?? [];
|
||||
if (!selectedId.value && templates.value[0]) selectPrompt(templates.value[0]);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
const createPrompt = () => {
|
||||
selectedId.value = 0;
|
||||
form.value = reactive({
|
||||
id: 0,
|
||||
code: '',
|
||||
name: '新提示词模板',
|
||||
category: 'practice',
|
||||
outputFormat: 'summary_card',
|
||||
systemPrompt: '',
|
||||
template: '',
|
||||
variables: '',
|
||||
version: 'v1',
|
||||
enabled: false,
|
||||
description: ''
|
||||
});
|
||||
};
|
||||
const save = async (enabled: boolean) => {
|
||||
if (!form.value?.code.trim() || !form.value.name.trim() || !form.value.template.trim()) {
|
||||
ElMessage.warning('请填写模板代码、名称和模板内容');
|
||||
return;
|
||||
}
|
||||
saving.value = true;
|
||||
try {
|
||||
const payload = { ...form.value, enabled };
|
||||
const result = form.value.id ? await updatePromptTemplate(form.value.id, payload) : await createPromptTemplate(payload);
|
||||
ElMessage.success(enabled ? '模板已保存并启用' : '模板草稿已保存');
|
||||
await load();
|
||||
if (result.data) selectPrompt(result.data);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
};
|
||||
onMounted(load);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.prompt-page {
|
||||
min-height: calc(100vh - 84px);
|
||||
padding: 28px 32px 40px;
|
||||
background: #f4f6fa;
|
||||
color: #1d2635;
|
||||
}
|
||||
.page-heading,
|
||||
.editor-heading,
|
||||
.editor-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.page-heading {
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
}
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
.page-heading p,
|
||||
.editor-heading p {
|
||||
margin-top: 8px;
|
||||
color: #858e9e;
|
||||
font-size: 13px;
|
||||
}
|
||||
.prompt-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 410px minmax(0, 1fr);
|
||||
gap: 18px;
|
||||
align-items: start;
|
||||
}
|
||||
.list-card,
|
||||
.editor-card {
|
||||
min-height: 520px;
|
||||
padding: 18px 16px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
box-shadow: 0 8px 24px rgb(33 43 54 / 4%);
|
||||
}
|
||||
.list-card h2 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.template-item {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 9px;
|
||||
padding: 15px 14px;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
background: #fafbfc;
|
||||
color: #253044;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
.template-item.active {
|
||||
background: #fff0f1;
|
||||
color: #e60012;
|
||||
}
|
||||
.template-item strong,
|
||||
.template-item span {
|
||||
display: block;
|
||||
}
|
||||
.template-item span {
|
||||
margin-top: 7px;
|
||||
color: #858e9e;
|
||||
font-size: 12px;
|
||||
}
|
||||
.template-item em {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 14px;
|
||||
color: #16a05d;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
}
|
||||
.editor-card {
|
||||
padding: 20px;
|
||||
}
|
||||
.editor-heading {
|
||||
align-items: flex-start;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.field-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
.check-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin: 10px 0 14px;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
background: #f7f9fb;
|
||||
}
|
||||
.check-card span {
|
||||
color: #858e9e;
|
||||
font-size: 12px;
|
||||
}
|
||||
.editor-actions {
|
||||
gap: 10px;
|
||||
}
|
||||
.editor-actions .el-button:last-child {
|
||||
flex: 1;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.prompt-layout,
|
||||
.field-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user