feat(kol): dynamic-form generation view

This commit is contained in:
2026-04-17 14:59:52 +08:00
parent 97c18cdbef
commit 4b6d434556
5 changed files with 226 additions and 9 deletions
+1 -1
View File
@@ -180,7 +180,7 @@ const enUS = {
generate: {
title: "Generate Article",
submit: "Generate",
fillVariables: "Please fill in all required variables",
fillVariables: "Please fill in the following variables and click generate",
},
manage: {
title: "KOL Prompt Management",
+1 -1
View File
@@ -180,7 +180,7 @@ const zhCN = {
generate: {
title: "生成文章",
submit: "生成",
fillVariables: "填写所有必填变量",
fillVariables: "填写以下变量后点击生成",
},
manage: {
title: "KOL 提示词管理",
+15
View File
@@ -39,6 +39,9 @@ import type {
KolPromptDetail,
KolPromptRevision,
KolSaveDraftRequest,
KolSubscriptionPromptSchema,
KolGenerateRequest,
KolGenerateResponse,
Keyword,
KeywordRequest,
LoginRequest,
@@ -359,6 +362,18 @@ export const kolMarketplaceApi = {
},
};
export const kolGenerateApi = {
schema(id: number | string) {
return apiClient.get<KolSubscriptionPromptSchema>(`/api/tenant/kol/subscription-prompts/${id}/schema`);
},
submit(id: number | string, body: KolGenerateRequest) {
return apiClient.post<KolGenerateResponse, KolGenerateRequest>(
`/api/tenant/kol/subscription-prompts/${id}/generate`,
body,
);
},
};
export const articlesApi = {
list(params: ArticleListParams) {
return apiClient.get<ArticleListResponse>("/api/tenant/articles", { params });
-7
View File
@@ -4,10 +4,3 @@ declare module "@/views/KolDashboardView.vue" {
const component: DefineComponent<Record<string, never>, Record<string, never>, any>;
export default component;
}
declare module "@/views/KolGenerateView.vue" {
import type { DefineComponent } from "vue";
const component: DefineComponent<Record<string, never>, Record<string, never>, any>;
export default component;
}
@@ -0,0 +1,209 @@
<script setup lang="ts">
import { computed, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
import { useMutation, useQuery } from "@tanstack/vue-query";
import { message } from "ant-design-vue";
import { ArrowLeftOutlined, ThunderboltOutlined } from "@ant-design/icons-vue";
import { kolGenerateApi } from "@/lib/api";
import KolDynamicForm from "@/components/kol/KolDynamicForm.vue";
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
const subscriptionPromptId = computed(() => String(route.params.subscriptionPromptId));
const schemaQuery = useQuery({
queryKey: ["kol", "subscription-prompt", "schema", subscriptionPromptId],
queryFn: () => kolGenerateApi.schema(subscriptionPromptId.value),
enabled: computed(() => !!subscriptionPromptId.value),
});
const values = ref<Record<string, any>>({});
// Initialize values from schema
watch(
() => schemaQuery.data.value,
(schema) => {
if (schema?.schema_json?.variables) {
const newValues: Record<string, any> = {};
schema.schema_json.variables.forEach((v) => {
newValues[v.key] = v.type === "checkbox" ? [] : undefined;
});
values.value = newValues;
}
},
{ immediate: true }
);
const generateMutation = useMutation({
mutationFn: (variables: Record<string, any>) =>
kolGenerateApi.submit(subscriptionPromptId.value, { variables }),
onSuccess: (data) => {
message.success(t("common.copySuccess")); // Or a generic success message
void router.push({ name: "article-editor", params: { id: String(data.article_id) } });
},
onError: (error: any) => {
message.error(error.message || t("common.noData"));
},
});
function handleBack() {
router.back();
}
function handleSubmit() {
const schema = schemaQuery.data.value;
if (!schema?.schema_json?.variables) return;
const missingLabels: string[] = [];
schema.schema_json.variables.forEach((v) => {
if (v.required && !values.value[v.key]) {
missingLabels.push(v.label);
}
});
if (missingLabels.length > 0) {
message.warning(`${t("kol.generate.fillVariables")}: ${missingLabels.join(", ")}`);
return;
}
generateMutation.mutate(values.value);
}
const schema = computed(() => schemaQuery.data.value);
</script>
<template>
<div class="kol-generate-view">
<div class="view-header">
<a-button type="link" @click="handleBack" class="back-btn">
<template #icon><ArrowLeftOutlined /></template>
{{ t("common.back") }}
</a-button>
</div>
<div v-if="schemaQuery.isPending.value" class="loading-state">
<a-spin size="large" />
</div>
<div v-else-if="schema" class="view-content">
<a-row :gutter="24" justify="center">
<a-col :xs="24" :lg="16" :xl="12">
<a-card class="generate-card">
<template #title>
<div class="card-header">
<span class="package-name">{{ schema.package_name }}</span>
<span class="divider">/</span>
<span class="prompt-name">{{ schema.prompt_name }}</span>
<a-tag v-if="schema.platform_hint" color="cyan" class="platform-badge">
{{ schema.platform_hint }}
</a-tag>
</div>
</template>
<div class="hint-text">
<ThunderboltOutlined class="hint-icon" />
{{ t("kol.generate.fillVariables") }}
</div>
<KolDynamicForm
v-model="values"
:variables="schema.schema_json.variables"
class="form-body"
/>
<div class="form-actions">
<a-button
type="primary"
size="large"
block
:loading="generateMutation.isPending.value"
@click="handleSubmit"
>
{{ t("kol.generate.submit") }}
</a-button>
</div>
</a-card>
</a-col>
</a-row>
</div>
</div>
</template>
<style scoped>
.kol-generate-view {
padding: 0;
}
.view-header {
margin-bottom: 16px;
}
.back-btn {
padding: 0;
color: #595959;
}
.loading-state {
display: flex;
justify-content: center;
padding: 100px;
}
.generate-card {
border-radius: 12px;
border: 1px solid #e6edf5;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02);
}
.card-header {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.package-name {
color: #8c8c8c;
font-weight: 400;
}
.divider {
color: #d9d9d9;
}
.prompt-name {
color: #1a1a1a;
font-weight: 600;
}
.platform-badge {
margin-left: 4px;
}
.hint-text {
margin-bottom: 24px;
padding: 12px 16px;
background: #f0f7ff;
border-radius: 8px;
color: #003a8c;
font-size: 14px;
display: flex;
align-items: center;
gap: 8px;
}
.hint-icon {
color: #1890ff;
}
.form-body {
margin-bottom: 24px;
}
.form-actions {
margin-top: 32px;
}
</style>