feat(schedule): support KOL subscription targets and random time windows
Extend schedule tasks to dispatch KOL subscription prompts in addition to prompt rules, add daily/weekly scheduling with fixed or random time windows, and track dispatch outcomes (last run, status, consecutive failures). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ import {
|
||||
PauseCircleOutlined,
|
||||
PlayCircleOutlined,
|
||||
} from '@ant-design/icons-vue'
|
||||
import type { ScheduleTask, ScheduleTaskListParams } from '@geo/shared-types'
|
||||
import type { ScheduleTargetType, ScheduleTask, ScheduleTaskListParams } from '@geo/shared-types'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { message, type TableColumnsType } from 'ant-design-vue'
|
||||
import type { Dayjs } from 'dayjs'
|
||||
@@ -16,7 +16,7 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
import GeneratedArticleLinksDrawer from '@/components/GeneratedArticleLinksDrawer.vue'
|
||||
import ScheduleTaskModal from '@/components/ScheduleTaskModal.vue'
|
||||
import { promptRulesApi, schedulesApi } from '@/lib/api'
|
||||
import { kolMarketplaceApi, promptRulesApi, schedulesApi } from '@/lib/api'
|
||||
import { formatDateTime, getGenerateStatusMeta, hasActiveGenerationStatus } from '@/lib/display'
|
||||
import { formatError } from '@/lib/errors'
|
||||
import { formatPublishPlatformList } from '@/lib/publish-platforms'
|
||||
@@ -35,7 +35,9 @@ const articleLinksOpen = ref(false)
|
||||
const selectedTask = ref<ScheduleTask | null>(null)
|
||||
|
||||
const draftFilters = reactive<{
|
||||
target_type?: ScheduleTargetType
|
||||
prompt_rule_id?: number
|
||||
subscription_prompt_id?: number
|
||||
status?: string
|
||||
keyword?: string
|
||||
}>({
|
||||
@@ -43,7 +45,9 @@ const draftFilters = reactive<{
|
||||
})
|
||||
|
||||
const appliedFilters = reactive<{
|
||||
target_type?: ScheduleTargetType
|
||||
prompt_rule_id?: number
|
||||
subscription_prompt_id?: number
|
||||
status?: string
|
||||
keyword?: string
|
||||
created_from?: string
|
||||
@@ -65,6 +69,23 @@ const promptOptions = computed(() =>
|
||||
})),
|
||||
)
|
||||
|
||||
const subscriptionPromptsQuery = useQuery({
|
||||
queryKey: ['kol', 'subscription-prompts', 'schedule-task-filter'],
|
||||
queryFn: () => kolMarketplaceApi.mySubscriptionPrompts(),
|
||||
})
|
||||
|
||||
const subscriptionPromptOptions = computed(() =>
|
||||
(subscriptionPromptsQuery.data.value ?? []).map((item) => ({
|
||||
label: `${item.package_name} / ${item.prompt_name}`,
|
||||
value: item.id,
|
||||
})),
|
||||
)
|
||||
|
||||
const sourceOptions = computed(() => [
|
||||
{ label: t('custom.task.sourcePromptRule'), value: 'prompt_rule' },
|
||||
{ label: t('custom.task.sourceKolTemplate'), value: 'kol_subscription_prompt' },
|
||||
])
|
||||
|
||||
const statusOptions = computed(() => [
|
||||
{ label: t('custom.schedule.enabled'), value: 'enabled' },
|
||||
{ label: t('custom.schedule.disabled'), value: 'disabled' },
|
||||
@@ -75,7 +96,11 @@ const listParams = computed<ScheduleTaskListParams>(() => {
|
||||
page: page.value,
|
||||
page_size: pageSize.value,
|
||||
}
|
||||
if (appliedFilters.target_type) params.target_type = appliedFilters.target_type
|
||||
if (appliedFilters.prompt_rule_id) params.prompt_rule_id = appliedFilters.prompt_rule_id
|
||||
if (appliedFilters.subscription_prompt_id) {
|
||||
params.subscription_prompt_id = appliedFilters.subscription_prompt_id
|
||||
}
|
||||
if (appliedFilters.status) params.status = appliedFilters.status
|
||||
if (appliedFilters.keyword?.trim()) params.keyword = appliedFilters.keyword.trim()
|
||||
if (appliedFilters.created_from) params.created_from = appliedFilters.created_from
|
||||
@@ -92,10 +117,28 @@ const listQuery = useQuery({
|
||||
const columns = computed<TableColumnsType<ScheduleTask>>(() => [
|
||||
{ title: t('custom.schedule.name'), dataIndex: 'name', key: 'name', width: 220 },
|
||||
{
|
||||
title: t('custom.schedule.rule'),
|
||||
title: t('custom.task.source'),
|
||||
dataIndex: 'target_type',
|
||||
key: 'target_type',
|
||||
width: 130,
|
||||
},
|
||||
{
|
||||
title: t('custom.schedule.target'),
|
||||
dataIndex: 'prompt_rule_name',
|
||||
key: 'prompt_rule_name',
|
||||
width: 180,
|
||||
key: 'target_name',
|
||||
width: 260,
|
||||
},
|
||||
{
|
||||
title: t('custom.schedule.plan'),
|
||||
dataIndex: 'schedule_kind',
|
||||
key: 'schedule_plan',
|
||||
width: 220,
|
||||
},
|
||||
{
|
||||
title: t('custom.schedule.nextRun'),
|
||||
dataIndex: 'next_run_at',
|
||||
key: 'next_run_at',
|
||||
width: 170,
|
||||
},
|
||||
{
|
||||
title: t('custom.instant.executionStatus'),
|
||||
@@ -104,9 +147,9 @@ const columns = computed<TableColumnsType<ScheduleTask>>(() => [
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
title: t('custom.instant.executionTime'),
|
||||
dataIndex: 'execution_time',
|
||||
key: 'execution_time',
|
||||
title: t('custom.schedule.dispatchStatus'),
|
||||
dataIndex: 'last_dispatch_status',
|
||||
key: 'dispatch_status',
|
||||
width: 170,
|
||||
},
|
||||
{
|
||||
@@ -155,7 +198,9 @@ const toggleStatusMutation = useMutation({
|
||||
|
||||
function applyFilters(): void {
|
||||
page.value = 1
|
||||
appliedFilters.target_type = draftFilters.target_type
|
||||
appliedFilters.prompt_rule_id = draftFilters.prompt_rule_id
|
||||
appliedFilters.subscription_prompt_id = draftFilters.subscription_prompt_id
|
||||
appliedFilters.status = draftFilters.status
|
||||
appliedFilters.keyword = draftFilters.keyword?.trim() || ''
|
||||
appliedFilters.created_from = createdRange.value?.[0]?.startOf('day').toISOString()
|
||||
@@ -163,7 +208,9 @@ function applyFilters(): void {
|
||||
}
|
||||
|
||||
function resetFilters(): void {
|
||||
draftFilters.target_type = undefined
|
||||
draftFilters.prompt_rule_id = undefined
|
||||
draftFilters.subscription_prompt_id = undefined
|
||||
draftFilters.status = undefined
|
||||
draftFilters.keyword = ''
|
||||
createdRange.value = null
|
||||
@@ -218,8 +265,12 @@ watch(
|
||||
watch(
|
||||
() => companyStore.currentBrandId,
|
||||
() => {
|
||||
draftFilters.target_type = undefined
|
||||
draftFilters.prompt_rule_id = undefined
|
||||
draftFilters.subscription_prompt_id = undefined
|
||||
appliedFilters.target_type = undefined
|
||||
appliedFilters.prompt_rule_id = undefined
|
||||
appliedFilters.subscription_prompt_id = undefined
|
||||
editingTask.value = null
|
||||
modalOpen.value = false
|
||||
page.value = 1
|
||||
@@ -229,11 +280,96 @@ watch(
|
||||
onBeforeUnmount(() => {
|
||||
stopPolling()
|
||||
})
|
||||
|
||||
function targetTypeLabel(targetType?: string | null): string {
|
||||
if (targetType === 'kol_subscription_prompt') {
|
||||
return t('custom.task.sourceKolTemplate')
|
||||
}
|
||||
return t('custom.task.sourcePromptRule')
|
||||
}
|
||||
|
||||
function scheduleTargetName(record: ScheduleTask): string {
|
||||
if (record.target_type === 'kol_subscription_prompt') {
|
||||
const parts = [
|
||||
record.subscription_package_name,
|
||||
record.subscription_prompt_name,
|
||||
record.subscription_kol_name,
|
||||
].filter(Boolean)
|
||||
return parts.length ? parts.join(' / ') : '--'
|
||||
}
|
||||
return record.prompt_rule_name || '--'
|
||||
}
|
||||
|
||||
function schedulePlanText(record: ScheduleTask): string {
|
||||
const days =
|
||||
record.schedule_kind === 'daily'
|
||||
? t('custom.schedule.kindDaily')
|
||||
: (record.schedule_days ?? []).map((day) => t(`custom.schedule.weekdays.${day}`)).join('、')
|
||||
return `${days} · ${scheduleTimeText(record)}`
|
||||
}
|
||||
|
||||
function scheduleTimeText(record: ScheduleTask): string {
|
||||
if (record.schedule_time_mode === 'random_window') {
|
||||
return `${formatClock(record.random_window_start)}-${formatClock(record.random_window_end)}`
|
||||
}
|
||||
return parseCronClock(record.cron_expr) ?? '--'
|
||||
}
|
||||
|
||||
function formatClock(value?: string | null): string {
|
||||
const text = String(value ?? '').trim()
|
||||
return text ? text.slice(0, 5) : '--'
|
||||
}
|
||||
|
||||
function parseCronClock(value?: string | null): string | null {
|
||||
const parts = String(value ?? '')
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean)
|
||||
if (parts.length !== 5 && parts.length !== 6) {
|
||||
return null
|
||||
}
|
||||
const minute = parts.length === 5 ? parts[0] : parts[1]
|
||||
const hour = parts.length === 5 ? parts[1] : parts[2]
|
||||
if (!/^\d+$/.test(hour) || !/^\d+$/.test(minute)) {
|
||||
return null
|
||||
}
|
||||
const hourValue = Number(hour)
|
||||
const minuteValue = Number(minute)
|
||||
if (hourValue > 23 || minuteValue > 59) {
|
||||
return null
|
||||
}
|
||||
return `${String(hourValue).padStart(2, '0')}:${String(minuteValue).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
function dispatchStatusMeta(status?: string | null): { label: string; color: string } {
|
||||
if (status === 'success') {
|
||||
return { label: t('custom.schedule.dispatchSuccess'), color: 'success' }
|
||||
}
|
||||
if (status === 'failed') {
|
||||
return { label: t('custom.schedule.dispatchFailed'), color: 'error' }
|
||||
}
|
||||
if (status === 'partial') {
|
||||
return { label: t('custom.schedule.dispatchPartialSuccess'), color: 'warning' }
|
||||
}
|
||||
return { label: t('custom.schedule.notDispatched'), color: 'default' }
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="task-tab">
|
||||
<div class="task-tab__filters">
|
||||
<div class="task-tab__filter">
|
||||
<label>{{ t('custom.task.source') }}:</label>
|
||||
<a-select
|
||||
v-model:value="draftFilters.target_type"
|
||||
allow-clear
|
||||
size="large"
|
||||
:options="sourceOptions"
|
||||
:placeholder="t('common.selectPlease')"
|
||||
@change="applyFilters"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="task-tab__filter">
|
||||
<label>{{ t('custom.filters.promptRule') }}:</label>
|
||||
<a-select
|
||||
@@ -247,6 +383,21 @@ onBeforeUnmount(() => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="task-tab__filter">
|
||||
<label>{{ t('custom.task.kolTemplate') }}:</label>
|
||||
<a-select
|
||||
v-model:value="draftFilters.subscription_prompt_id"
|
||||
allow-clear
|
||||
show-search
|
||||
size="large"
|
||||
:options="subscriptionPromptOptions"
|
||||
:placeholder="t('custom.task.kolTemplatePlaceholder')"
|
||||
:loading="subscriptionPromptsQuery.isPending.value"
|
||||
option-filter-prop="label"
|
||||
@change="applyFilters"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="task-tab__filter">
|
||||
<label>{{ t('custom.filters.status') }}:</label>
|
||||
<a-select
|
||||
@@ -297,12 +448,28 @@ onBeforeUnmount(() => {
|
||||
showSizeChanger: true,
|
||||
}"
|
||||
row-key="id"
|
||||
:scroll="{ x: 1360 }"
|
||||
:scroll="{ x: 1620 }"
|
||||
@change="(p: any) => handleTableChange(p.current, p.pageSize)"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'prompt_rule_name'">
|
||||
{{ record.prompt_rule_name || '--' }}
|
||||
<template v-if="column.key === 'target_type'">
|
||||
<a-tag :color="record.target_type === 'kol_subscription_prompt' ? 'cyan' : 'blue'">
|
||||
{{ targetTypeLabel(record.target_type) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'target_name'">
|
||||
<div class="task-tab__target">
|
||||
<strong>{{ scheduleTargetName(record) }}</strong>
|
||||
<span v-if="record.target_type === 'kol_subscription_prompt'">
|
||||
{{ record.subscription_kol_name || '--' }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'schedule_plan'">
|
||||
<span class="task-tab__plan">{{ schedulePlanText(record) }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'next_run_at'">
|
||||
{{ formatDateTime(record.next_run_at) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'generation_status'">
|
||||
<a-tag :color="getGenerateStatusMeta(record.generation_status).color">
|
||||
@@ -312,8 +479,12 @@ onBeforeUnmount(() => {
|
||||
</span>
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'execution_time'">
|
||||
{{ formatDateTime(record.execution_time) }}
|
||||
<template v-else-if="column.key === 'dispatch_status'">
|
||||
<a-tooltip :title="record.last_dispatch_error || formatDateTime(record.last_run_at)">
|
||||
<a-tag :color="dispatchStatusMeta(record.last_dispatch_status).color">
|
||||
{{ dispatchStatusMeta(record.last_dispatch_status).label }}
|
||||
</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'auto_publish_platforms'">
|
||||
<a-tag :color="record.auto_publish ? 'blue' : 'default'">
|
||||
@@ -402,10 +573,7 @@ onBeforeUnmount(() => {
|
||||
</a-table>
|
||||
|
||||
<ScheduleTaskModal v-model:open="modalOpen" :task="editingTask" />
|
||||
<GeneratedArticleLinksDrawer
|
||||
v-model:open="articleLinksOpen"
|
||||
:articles="selectedTaskArticles"
|
||||
/>
|
||||
<GeneratedArticleLinksDrawer v-model:open="articleLinksOpen" :articles="selectedTaskArticles" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -413,8 +581,8 @@ onBeforeUnmount(() => {
|
||||
.task-tab__filters {
|
||||
display: grid;
|
||||
grid-template-columns:
|
||||
minmax(180px, 240px) minmax(180px, 240px) minmax(240px, 320px) minmax(240px, 1fr)
|
||||
auto;
|
||||
repeat(3, minmax(180px, 240px)) minmax(180px, 220px) minmax(240px, 320px)
|
||||
minmax(240px, 1fr) auto;
|
||||
gap: 16px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
@@ -448,6 +616,29 @@ onBeforeUnmount(() => {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.task-tab__target {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.task-tab__target strong {
|
||||
overflow: hidden;
|
||||
color: #1f2937;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.task-tab__target span,
|
||||
.task-tab__plan {
|
||||
color: #667085;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 1400px) {
|
||||
.task-tab__filters {
|
||||
grid-template-columns: repeat(2, minmax(260px, 1fr));
|
||||
|
||||
@@ -1612,6 +1612,20 @@ const enUS = {
|
||||
coverUpload: 'Upload cover image',
|
||||
coverUploadHint: 'Local preview only for now',
|
||||
scheduleCoverUploadHint: 'Choose from local files or the asset library',
|
||||
source: 'Generation source',
|
||||
sourcePromptRule: 'Custom Prompt',
|
||||
sourceKolTemplate: 'Refined Template',
|
||||
kolTemplate: 'Refined template',
|
||||
kolTemplatePlaceholder: 'Select a subscribed refined template',
|
||||
kolTemplateEmptyTitle: 'No refined templates available',
|
||||
kolTemplateEmptyHint:
|
||||
'This account has no active subscribed refined template package yet. Subscribe in the marketplace first.',
|
||||
scheduleSection: 'Schedule Policy',
|
||||
kolInputSection: 'Refined Variables',
|
||||
kolWebSearch: 'Web search',
|
||||
kolWebSearchHint: 'Enable it when this refined template allows web search.',
|
||||
kolKnowledge: 'Knowledge base',
|
||||
kolKnowledgePlaceholder: 'Optional. Select your knowledge groups for generation',
|
||||
scheduleTime: 'Schedule article generation',
|
||||
scheduleEveryDay: 'Every day',
|
||||
enableWebSearch: 'Enable web search for generation',
|
||||
@@ -1629,11 +1643,29 @@ const enUS = {
|
||||
schedule: {
|
||||
name: 'Task name',
|
||||
rule: 'Prompt rule',
|
||||
target: 'Target',
|
||||
plan: 'Schedule',
|
||||
cron: 'Frequency',
|
||||
frequency: 'Frequency',
|
||||
kindDaily: 'Every day',
|
||||
kindWeekly: 'Weekdays',
|
||||
weekdaysLabel: 'Run days',
|
||||
executionTime: 'Run time',
|
||||
executionTimeHint:
|
||||
'New tasks default to a random time between 01:00 and 05:00 to smooth overnight load. You can edit it before saving.',
|
||||
executionTimeDefault: 'Default staggered time',
|
||||
to: 'to',
|
||||
autoPublishPlatforms: 'Auto publish platforms',
|
||||
autoPublish: 'Auto publish',
|
||||
manualPublish: 'Manual publish',
|
||||
nextRun: 'Next run',
|
||||
dispatchStatus: 'Last dispatch',
|
||||
dispatchSuccess: 'Success',
|
||||
dispatchFailed: 'Failed',
|
||||
dispatchPartialSuccess: 'Partial success',
|
||||
notDispatched: 'Not dispatched',
|
||||
footerHint:
|
||||
'New tasks start with a random time between 01:00 and 05:00, then run at the saved time.',
|
||||
startAt: 'Start time',
|
||||
endAt: 'End time',
|
||||
brand: 'Brand',
|
||||
@@ -1650,6 +1682,15 @@ const enUS = {
|
||||
weekly: 'Once weekly',
|
||||
custom: 'Custom Cron',
|
||||
},
|
||||
weekdays: {
|
||||
mon: 'Mon',
|
||||
tue: 'Tue',
|
||||
wed: 'Wed',
|
||||
thu: 'Thu',
|
||||
fri: 'Fri',
|
||||
sat: 'Sat',
|
||||
sun: 'Sun',
|
||||
},
|
||||
},
|
||||
instant: {
|
||||
title: 'Instant Generate',
|
||||
@@ -1700,8 +1741,11 @@ const enUS = {
|
||||
generateSubmitted: 'Generation task submitted.',
|
||||
missingTaskName: 'Enter a task name first.',
|
||||
missingPromptRule: 'Choose a prompt first.',
|
||||
missingKolTemplate: 'Choose a subscribed refined template first.',
|
||||
invalidGenerateCount: 'Article count must be at least 1.',
|
||||
invalidScheduleTime: 'Choose a valid schedule time.',
|
||||
invalidRandomWindow: 'The schedule time configuration is invalid.',
|
||||
missingScheduleDays: 'Select at least one run day.',
|
||||
missingPublishAccounts: 'Choose at least one media account when auto publish is enabled.',
|
||||
missingScheduleCover: 'A selected media account platform requires a cover image.',
|
||||
missingPromptName: 'Enter a prompt name first.',
|
||||
|
||||
@@ -1520,6 +1520,19 @@ const zhCN = {
|
||||
coverUpload: "上传封面图",
|
||||
coverUploadHint: "当前仅做本地预览",
|
||||
scheduleCoverUploadHint: "从本地或素材库选择",
|
||||
source: "生成来源",
|
||||
sourcePromptRule: "自定义 Prompt",
|
||||
sourceKolTemplate: "精调模版",
|
||||
kolTemplate: "精调模版",
|
||||
kolTemplatePlaceholder: "请选择已订阅的精调模版",
|
||||
kolTemplateEmptyTitle: "暂无可用精调模版",
|
||||
kolTemplateEmptyHint: "当前账号还没有订阅可用的精调模版包,请先到精调模版市场完成订阅。",
|
||||
scheduleSection: "调度策略",
|
||||
kolInputSection: "精调变量",
|
||||
kolWebSearch: "联网搜索",
|
||||
kolWebSearchHint: "该精调模版允许联网搜索时可开启。",
|
||||
kolKnowledge: "知识库引用",
|
||||
kolKnowledgePlaceholder: "可选,选择你的知识库分组辅助生成",
|
||||
scheduleTime: "定时生成文章",
|
||||
scheduleEveryDay: "每天",
|
||||
enableWebSearch: "生成文章联网搜索",
|
||||
@@ -1537,11 +1550,28 @@ const zhCN = {
|
||||
schedule: {
|
||||
name: "任务名称",
|
||||
rule: "关联规则",
|
||||
target: "生成目标",
|
||||
plan: "调度计划",
|
||||
cron: "执行频率",
|
||||
frequency: "频率",
|
||||
kindDaily: "每天",
|
||||
kindWeekly: "按星期",
|
||||
weekdaysLabel: "执行日",
|
||||
executionTime: "执行时间",
|
||||
executionTimeHint:
|
||||
"新建任务会默认随机落在 01:00-05:00,避免凌晨任务集中;你也可以手动修改为固定执行时间。",
|
||||
executionTimeDefault: "默认错峰生成",
|
||||
to: "至",
|
||||
autoPublishPlatforms: "自动发布平台",
|
||||
autoPublish: "自动发布",
|
||||
manualPublish: "手动发布",
|
||||
nextRun: "下次执行",
|
||||
dispatchStatus: "最近调度",
|
||||
dispatchSuccess: "成功",
|
||||
dispatchFailed: "失败",
|
||||
dispatchPartialSuccess: "部分成功",
|
||||
notDispatched: "未调度",
|
||||
footerHint: "新建任务默认给出凌晨 1 点到 5 点之间的随机时间,保存后按所选时间执行。",
|
||||
startAt: "开始时间",
|
||||
endAt: "结束时间",
|
||||
brand: "关联品牌",
|
||||
@@ -1558,6 +1588,15 @@ const zhCN = {
|
||||
weekly: "每周一次",
|
||||
custom: "自定义 Cron",
|
||||
},
|
||||
weekdays: {
|
||||
mon: "周一",
|
||||
tue: "周二",
|
||||
wed: "周三",
|
||||
thu: "周四",
|
||||
fri: "周五",
|
||||
sat: "周六",
|
||||
sun: "周日",
|
||||
},
|
||||
},
|
||||
instant: {
|
||||
title: "即时生成",
|
||||
@@ -1608,8 +1647,11 @@ const zhCN = {
|
||||
generateSubmitted: "生成任务已提交",
|
||||
missingTaskName: "请先填写任务名称",
|
||||
missingPromptRule: "请先选择 Prompt",
|
||||
missingKolTemplate: "请先选择已订阅的精调模版",
|
||||
invalidGenerateCount: "生成篇数至少为 1",
|
||||
invalidScheduleTime: "请选择有效的定时时间",
|
||||
invalidRandomWindow: "执行时间配置不正确",
|
||||
missingScheduleDays: "请至少选择一个执行日",
|
||||
missingPublishAccounts: "开启自动发布后,请至少选择一个媒体账号",
|
||||
missingScheduleCover: "已选媒体账号的平台要求封面图,请先设置封面",
|
||||
missingPromptName: "请先填写 Prompt 名称",
|
||||
|
||||
Reference in New Issue
Block a user