From 8991edabb5fda49222f2c2750bf726c1fca0f2aa Mon Sep 17 00:00:00 2001 From: liangxu Date: Sun, 24 May 2026 01:48:04 +0800 Subject: [PATCH] feat(kol): support multiple platform hints with shared tag renderer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turn the KOL prompt platform hint into a multi-select backed by the media platforms API, persist hints as a 、-joined string, and add a reusable KolPlatformTags component so manage/generate/package/template/ workspace views and the generate-task drawer all render the hints consistently with truncation and overflow handling. Also auto-select the first available subscription prompt in GenerateTaskDrawer when switching to the subscription-prompt target so users land on a usable option without an extra click. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/components/GenerateTaskDrawer.vue | 39 ++++++- .../src/components/kol/KolPlatformTags.vue | 105 ++++++++++++++++++ .../src/components/kol/KolPromptEditor.vue | 44 +++++--- .../src/components/kol/KolPromptFormModal.vue | 47 ++++++-- .../admin-web/src/lib/kol-platform-options.ts | 46 +++++++- apps/admin-web/src/views/KolGenerateView.vue | 13 ++- apps/admin-web/src/views/KolManageView.vue | 9 +- .../src/views/KolPackageDetailView.vue | 10 +- apps/admin-web/src/views/TemplatesView.vue | 33 ++++-- apps/admin-web/src/views/WorkspaceView.vue | 33 ++++-- 10 files changed, 322 insertions(+), 57 deletions(-) create mode 100644 apps/admin-web/src/components/kol/KolPlatformTags.vue diff --git a/apps/admin-web/src/components/GenerateTaskDrawer.vue b/apps/admin-web/src/components/GenerateTaskDrawer.vue index 2f8da34..fea7f99 100644 --- a/apps/admin-web/src/components/GenerateTaskDrawer.vue +++ b/apps/admin-web/src/components/GenerateTaskDrawer.vue @@ -22,6 +22,7 @@ import { useI18n } from 'vue-i18n' import CoverPickerModal from '@/components/CoverPickerModal.vue' import KnowledgeGroupSelect from '@/components/KnowledgeGroupSelect.vue' import KolDynamicForm from '@/components/kol/KolDynamicForm.vue' +import KolPlatformTags from '@/components/kol/KolPlatformTags.vue' import PromptRuleModal from '@/components/PromptRuleModal.vue' import { generateApi, @@ -254,10 +255,15 @@ watch( form.kolKnowledgeGroupIds = [] } else { form.promptRuleId = undefined + selectDefaultSubscriptionPrompt() } }, ) +watch(subscriptionPromptOptions, () => { + selectDefaultSubscriptionPrompt() +}) + watch( () => form.scheduleKind, (kind) => { @@ -372,6 +378,23 @@ function hydrateForm(): void { ? (parseCronTime(task.cron_expr) ?? normalizeClock(task.random_window_start, randomDefaultScheduleTime())) : randomDefaultScheduleTime() + selectDefaultSubscriptionPrompt() +} + +function selectDefaultSubscriptionPrompt(): void { + if ( + !props.open || + !isSchedule.value || + props.task?.id || + form.targetType !== 'kol_subscription_prompt' || + form.subscriptionPromptId + ) { + return + } + const firstOption = subscriptionPromptOptions.value[0] + if (firstOption) { + form.subscriptionPromptId = firstOption.value + } } function buildSchedulePayload() { @@ -824,9 +847,14 @@ function normalizeJsonRecord(value: Record): Record - - {{ selectedSubscriptionPrompt.platform_hint }} - +
): Record +import { computed } from 'vue' + +import { parseKolPlatformHints } from '@/lib/kol-platform-options' + +const props = withDefaults( + defineProps<{ + value?: string | null + max?: number + size?: 'small' | 'default' + wrap?: boolean + truncate?: boolean + }>(), + { + max: 0, + size: 'default', + wrap: true, + truncate: true, + }, +) + +const labels = computed(() => parseKolPlatformHints(props.value)) +const visibleLabels = computed(() => + props.max > 0 ? labels.value.slice(0, props.max) : labels.value, +) +const hiddenCount = computed(() => Math.max(labels.value.length - visibleLabels.value.length, 0)) +const title = computed(() => labels.value.join('、')) + + + + + diff --git a/apps/admin-web/src/components/kol/KolPromptEditor.vue b/apps/admin-web/src/components/kol/KolPromptEditor.vue index c5de98f..7e1b79f 100644 --- a/apps/admin-web/src/components/kol/KolPromptEditor.vue +++ b/apps/admin-web/src/components/kol/KolPromptEditor.vue @@ -12,7 +12,7 @@ import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } import { useI18n } from 'vue-i18n' import EditorAiAssistPanel from '@/components/editor-common/EditorAiAssistPanel.vue' -import { kolManageApi } from '@/lib/api' +import { kolManageApi, mediaApi } from '@/lib/api' import { formatError } from '@/lib/errors' import { mergeKolCardConfig, parseKolCardConfig } from '@/lib/kol-card-config' import { @@ -20,7 +20,13 @@ import { normalizeKolVariableDefinition, syncKolVariablesWithContent, } from '@/lib/kol-placeholders' -import { buildKolPlatformOptions } from '@/lib/kol-platform-options' +import { + buildKolPlatformOptions, + KOL_PLATFORM_HINT_DEFAULT, + normalizeKolPlatformHints, + parseKolPlatformHints, + serializeKolPlatformHints, +} from '@/lib/kol-platform-options' import type { KolAssistRequest, KolAssistStreamEvent, @@ -99,7 +105,7 @@ const variableConfigRef = ref<{ } | null>(null) const metadataForm = reactive({ name: '', - platform_hint: '通用', + platform_hints: [KOL_PLATFORM_HINT_DEFAULT] as string[], allow_web_search: false, allow_user_knowledge: true, }) @@ -113,14 +119,17 @@ const { data: promptDetail } = useQuery({ queryFn: () => kolManageApi.getPrompt(props.promptId), }) -const platformOptions = computed(() => { - const options = buildKolPlatformOptions(t('kol.manage.platformHintDefault')) - const currentValue = metadataForm.platform_hint.trim() - if (currentValue && !options.some((option) => option.value === currentValue)) { - return [{ label: currentValue, value: currentValue }, ...options] - } - return options +const platformsQuery = useQuery({ + queryKey: ['media', 'platforms', 'kol-prompt-editor'], + queryFn: () => mediaApi.platforms(), }) +const platformOptions = computed(() => + buildKolPlatformOptions( + t('kol.manage.platformHintDefault'), + platformsQuery.data.value ?? [], + metadataForm.platform_hints, + ), +) const selectionAiStreaming = computed(() => selectionAiStatus.value === 'generating') const selectionAiHasPreview = computed(() => selectionAiPreview.value.trim().length > 0) const selectionAiUiText = computed(() => ({ @@ -142,7 +151,9 @@ watch( const parsedCardConfig = parseKolCardConfig(nextCardConfig) metadataForm.name = detail.name - metadataForm.platform_hint = detail.platform_hint || '通用' + metadataForm.platform_hints = parseKolPlatformHints( + detail.platform_hint || KOL_PLATFORM_HINT_DEFAULT, + ) metadataForm.allow_web_search = parsedCardConfig.allow_web_search metadataForm.allow_user_knowledge = parsedCardConfig.allow_user_knowledge content.value = detail.latest_prompt_content ?? '' @@ -442,7 +453,7 @@ function applySelectionAiReplacement() { function buildPromptPayload(nextVariables: KolVariableDefinition[] = variables.value) { return { name: metadataForm.name.trim(), - platform_hint: metadataForm.platform_hint, + platform_hint: serializeKolPlatformHints(metadataForm.platform_hints), sort_order: promptDetail.value?.sort_order, content: content.value, schema: { variables: nextVariables }, @@ -526,6 +537,10 @@ function handlePreview() { previewOpen.value = true } +function handlePlatformHintChange(values: string[]) { + metadataForm.platform_hints = normalizeKolPlatformHints(values) +} + async function handleFocusVariable(key: string) { syncVariablesFromContent() await nextTick() @@ -642,11 +657,14 @@ onBeforeUnmount(() => { diff --git a/apps/admin-web/src/components/kol/KolPromptFormModal.vue b/apps/admin-web/src/components/kol/KolPromptFormModal.vue index 14c0a66..df561e0 100644 --- a/apps/admin-web/src/components/kol/KolPromptFormModal.vue +++ b/apps/admin-web/src/components/kol/KolPromptFormModal.vue @@ -1,9 +1,17 @@