feat(admin-web): preserve template wizard entry source and refresh AI banner
- Carry `from=workspace` query from the workbench into the wizard so cancel and post-generate redirects return to the workbench instead of always landing on /articles/templates. - AppShell now resolves an effective navKey from `route.query.from`, so the sidebar highlight and breadcrumb track the entry source while inside the wizard. - Redesign the brand-info AI analyze CTA into a standalone banner with template-aware copy (default / product_review / research_report) and add the matching i18n keys. - Drop the unused 批量生成文章 button and its styling from the templates page. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
HolderOutlined,
|
||||
LeftOutlined,
|
||||
PlusOutlined,
|
||||
RobotOutlined,
|
||||
StarFilled,
|
||||
} from '@ant-design/icons-vue'
|
||||
import type {
|
||||
@@ -254,7 +255,7 @@ const mutation = useMutation({
|
||||
])
|
||||
message.info(t('templates.wizard.messages.queued'))
|
||||
allowWizardLeave = true
|
||||
await router.replace('/articles/templates')
|
||||
await router.replace(resolveExitPath())
|
||||
},
|
||||
onError: (error) => {
|
||||
message.error(formatError(error))
|
||||
@@ -408,6 +409,25 @@ const previewCardCopy = computed<TemplateCardCopy>(
|
||||
const analyzeButtonLabel = computed(
|
||||
() => wizardConfig.value?.basic?.analyze_button_label || t('templates.wizard.actions.analyze'),
|
||||
)
|
||||
const aiBannerCopy = computed(() => {
|
||||
switch (templateDetail.value?.template_key) {
|
||||
case 'product_review':
|
||||
return {
|
||||
title: t('templates.wizard.aiBanner.title'),
|
||||
description: t('templates.wizard.aiBanner.productReviewDescription'),
|
||||
}
|
||||
case 'research_report':
|
||||
return {
|
||||
title: t('templates.wizard.aiBanner.title'),
|
||||
description: t('templates.wizard.aiBanner.researchReportDescription'),
|
||||
}
|
||||
default:
|
||||
return {
|
||||
title: t('templates.wizard.aiBanner.title'),
|
||||
description: t('templates.wizard.aiBanner.defaultDescription'),
|
||||
}
|
||||
}
|
||||
})
|
||||
const reviewAlertMessage = computed(
|
||||
() => wizardConfig.value?.review?.alert_message || t('templates.wizard.hints.async'),
|
||||
)
|
||||
@@ -1216,8 +1236,12 @@ async function saveWizardDraft(): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
function resolveExitPath(): string {
|
||||
return route.query.from === 'workspace' ? '/workspace' : '/articles/templates'
|
||||
}
|
||||
|
||||
async function handleSaveDraftAndExit(): Promise<void> {
|
||||
await router.replace('/articles/templates')
|
||||
await router.replace(resolveExitPath())
|
||||
}
|
||||
|
||||
function dedupeStrings(values: string[]): string[] {
|
||||
@@ -2223,9 +2247,6 @@ function onStructureDragEnd(): void {
|
||||
<h3>{{ brandCardCopy.title || t('templates.wizard.sections.brandInfo') }}</h3>
|
||||
<p>{{ brandCardCopy.hint || t('templates.wizard.hints.brandFlow') }}</p>
|
||||
</div>
|
||||
<a-button type="primary" ghost :loading="assistBusy" @click="handleAnalyze">
|
||||
{{ analyzeButtonLabel }}
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<div class="field-grid">
|
||||
@@ -2277,6 +2298,25 @@ function onStructureDragEnd(): void {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wizard-ai-banner">
|
||||
<div class="wizard-ai-banner__info">
|
||||
<RobotOutlined class="wizard-ai-banner__icon" />
|
||||
<div class="wizard-ai-banner__text">
|
||||
<h4>{{ aiBannerCopy.title }}</h4>
|
||||
<p>{{ aiBannerCopy.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<a-button
|
||||
type="primary"
|
||||
class="wizard-ai-banner__btn"
|
||||
size="large"
|
||||
:loading="assistBusy"
|
||||
@click="handleAnalyze"
|
||||
>
|
||||
{{ analyzeButtonLabel }}
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<div class="summary-box">
|
||||
<div class="summary-box__label">
|
||||
{{ t('templates.wizard.sections.brandSummary') }}
|
||||
@@ -2959,6 +2999,60 @@ function onStructureDragEnd(): void {
|
||||
content: '*';
|
||||
}
|
||||
|
||||
.wizard-ai-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 24px;
|
||||
padding: 16px 20px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, rgba(22, 119, 255, 0.04), rgba(22, 119, 255, 0.08));
|
||||
border: 1px solid rgba(22, 119, 255, 0.15);
|
||||
box-shadow: 0 4px 16px rgba(22, 119, 255, 0.05);
|
||||
}
|
||||
|
||||
.wizard-ai-banner__info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.wizard-ai-banner__icon {
|
||||
font-size: 28px;
|
||||
background: linear-gradient(135deg, #1677ff, #36cfc9);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.wizard-ai-banner__text h4 {
|
||||
margin: 0 0 4px;
|
||||
color: #1f2f4d;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.wizard-ai-banner__text p {
|
||||
margin: 0;
|
||||
color: #6b7a99;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.wizard-ai-banner__btn {
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
padding: 0 24px;
|
||||
background: linear-gradient(135deg, #1677ff, #0958d9);
|
||||
border: none;
|
||||
box-shadow: 0 4px 12px rgba(22, 119, 255, 0.2);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.wizard-ai-banner__btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 16px rgba(22, 119, 255, 0.3);
|
||||
}
|
||||
|
||||
.summary-box {
|
||||
margin-top: 18px;
|
||||
padding: 16px 18px;
|
||||
|
||||
Reference in New Issue
Block a user