fix: 后台编辑器字段全覆盖 + 新建 about/versions/pricing 专用编辑器

- tour 编辑器补全:selectionGuide 3维度、isMainProduct、timeline 正确结构
- camp 编辑器补全:name、campEssentials
- calendar 补全:quickRecommendations、travelAdvice、recommendedProducts
- guides 补全:穿搭章节 months[] 每月数据
- brand 补全:ctaButtons、conversionPath、icpUrl、eContract
- qualifications 补全:enterpriseMatrix
- youji 补全:notes[].tags
- gallery 补全:页面配置(intro/stats/categories)
- 新建 about.vue 专用编辑器(14个分区)
- 新建 versions.vue 专用编辑器(8个分区)
- 新建 pricing.vue 专用编辑器(5个分区)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
这个提交包含在:
Mimingguang
2026-03-25 09:06:50 +08:00
共同撰写人 Claude Opus 4.6
父节点 ee078b61bc
当前提交 810ae2724d
共修改 11 个文件,包含 1136 行新增和 19 行删除
+49 -10
查看文件
@@ -78,11 +78,12 @@
<n-gi><n-form-item label="名称"><n-input v-model:value="ver.name" /></n-form-item></n-gi>
<n-gi><n-form-item label="标签"><n-input v-model:value="ver.tag" /></n-form-item></n-gi>
</n-grid>
<n-grid :cols="4" :x-gap="12">
<n-grid :cols="5" :x-gap="12">
<n-gi><n-form-item label="天数"><n-input-number v-model:value="ver.days" :min="1" style="width:100%" /></n-form-item></n-gi>
<n-gi><n-form-item label="晚数"><n-input-number v-model:value="ver.nights" :min="0" style="width:100%" /></n-form-item></n-gi>
<n-gi><n-form-item label="线路"><n-input v-model:value="ver.line" placeholder="南线/北线" /></n-form-item></n-gi>
<n-gi><n-form-item label="受众"><n-input v-model:value="ver.audience" /></n-form-item></n-gi>
<n-gi><n-form-item label="主推产品"><n-switch v-model:value="ver.isMainProduct" /></n-form-item></n-gi>
</n-grid>
<n-form-item label="路线"><n-input v-model:value="ver.route" /></n-form-item>
<n-form-item label="描述"><n-input v-model:value="ver.description" type="textarea" :rows="3" /></n-form-item>
@@ -134,15 +135,46 @@
<n-button size="tiny" type="error" quaternary @click="data.selectionGuide.byVacationLength.splice(i,1)">删</n-button>
</div>
</n-card>
<n-card title="版本迭代时间线" style="margin-top:16px">
<template #header-extra><n-button size="small" @click="addToArray(data, 'timeline', {year:'',title:'',description:''})">添加</n-button></template>
<div v-for="(t, i) in data.timeline || []" :key="i" style="margin-bottom:8px;display:flex;gap:8px;align-items:center;">
<n-input v-model:value="t.year" placeholder="年份" style="width:80px" />
<n-input v-model:value="t.title" placeholder="标题" style="width:160px" />
<n-input v-model:value="t.description" placeholder="描述" style="flex:1" />
<n-button size="tiny" type="error" quaternary @click="data.timeline.splice(i,1)">删</n-button>
<n-card title="按孩子年龄推荐" style="margin-top:16px">
<template #header-extra><n-button size="small" @click="ensureArray(data, 'selectionGuide.byChildAge'); data.selectionGuide.byChildAge.push({condition:'',recommendation:'',reason:''})">添加</n-button></template>
<div v-for="(g, i) in data.selectionGuide?.byChildAge || []" :key="i" style="margin-bottom:8px;display:flex;gap:8px;align-items:center;">
<n-input v-model:value="g.condition" placeholder="条件" style="width:120px" />
<n-input v-model:value="g.recommendation" placeholder="推荐产品ID" style="width:160px" />
<n-input v-model:value="g.reason" placeholder="原因" style="flex:1" />
<n-button size="tiny" type="error" quaternary @click="data.selectionGuide.byChildAge.splice(i,1)">删</n-button>
</div>
</n-card>
<n-card title="按偏好推荐" style="margin-top:16px">
<template #header-extra><n-button size="small" @click="ensureArray(data, 'selectionGuide.byPreference'); data.selectionGuide.byPreference.push({condition:'',recommendation:'',reason:''})">添加</n-button></template>
<div v-for="(g, i) in data.selectionGuide?.byPreference || []" :key="i" style="margin-bottom:8px;display:flex;gap:8px;align-items:center;">
<n-input v-model:value="g.condition" placeholder="条件" style="width:120px" />
<n-input v-model:value="g.recommendation" placeholder="推荐产品ID" style="width:160px" />
<n-input v-model:value="g.reason" placeholder="原因" style="flex:1" />
<n-button size="tiny" type="error" quaternary @click="data.selectionGuide.byPreference.splice(i,1)">删</n-button>
</div>
</n-card>
<n-card title="版本迭代时间线" style="margin-top:16px">
<template #header-extra><n-button size="small" @click="addToArray(data, 'timeline', {version:'',date:'',reason:'',changes:[]})">添加</n-button></template>
<n-collapse>
<n-collapse-item v-for="(t, i) in data.timeline || []" :key="i" :name="i">
<template #header><strong>{{ t.version || '版本 '+(i+1) }}</strong> <span style="color:#9ca3af;font-size:12px;margin-left:8px">{{ t.date }}</span></template>
<template #header-extra><n-button size="tiny" type="error" quaternary @click.stop="data.timeline.splice(i,1)">删除</n-button></template>
<n-grid :cols="2" :x-gap="12">
<n-gi><n-form-item label="版本号"><n-input v-model:value="t.version" /></n-form-item></n-gi>
<n-gi><n-form-item label="日期"><n-input v-model:value="t.date" /></n-form-item></n-gi>
</n-grid>
<n-form-item label="更新原因"><n-input v-model:value="t.reason" /></n-form-item>
<n-form-item label="变更列表">
<div v-for="(c, ci) in t.changes || []" :key="ci" style="display:flex;gap:8px;margin-bottom:6px;">
<n-input v-model:value="c.type" placeholder="类型" style="width:100px" />
<n-input v-model:value="c.text" placeholder="变更内容" style="flex:1" />
<n-button size="tiny" type="error" quaternary @click="t.changes.splice(ci,1)">删</n-button>
</div>
<n-button size="tiny" dashed @click="if(!t.changes) t.changes=[]; t.changes.push({type:'',text:''})">添加变更</n-button>
</n-form-item>
</n-collapse-item>
</n-collapse>
</n-card>
</n-tab-pane>
<n-tab-pane name="faq" tab="常见问答">
@@ -155,6 +187,7 @@
<n-tab-pane name="basic" tab="基本信息">
<n-card>
<n-form label-placement="top">
<n-form-item label="营地名称"><n-input v-model:value="data.name" /></n-form-item>
<n-form-item label="营地定位"><n-input v-model:value="data.positioning" type="textarea" :rows="3" /></n-form-item>
<n-grid :cols="4" :x-gap="12">
<n-gi><n-form-item label="天数"><n-input-number v-model:value="data.days" :min="1" style="width:100%" /></n-form-item></n-gi>
@@ -244,6 +277,12 @@
<n-input v-model:value="data.serviceConfig[i]" style="flex:1" /><n-button size="tiny" type="error" quaternary @click="data.serviceConfig.splice(i,1)">删</n-button>
</div>
</n-card>
<n-card v-if="data.campEssentials !== undefined" title="营地必备清单" style="margin-top:16px">
<template #header-extra><n-button size="small" @click="data.campEssentials.push('')">添加</n-button></template>
<div v-for="(_, i) in data.campEssentials" :key="i" style="margin-bottom:8px;display:flex;gap:8px;">
<n-input v-model:value="data.campEssentials[i]" style="flex:1" /><n-button size="tiny" type="error" quaternary @click="data.campEssentials.splice(i,1)">删</n-button>
</div>
</n-card>
<n-card v-if="data.coreActivities !== undefined" title="核心体验" style="margin-top:16px">
<template #header-extra><n-button size="small" @click="data.coreActivities.push('')">添加</n-button></template>
<div v-for="(_, i) in data.coreActivities" :key="i" style="margin-bottom:8px;display:flex;gap:8px;">
@@ -359,7 +398,7 @@ import { h } from 'vue'
import {
NButton, NCard, NSpace, NTag, NGrid, NGi, NSpin, NForm, NFormItem,
NInput, NInputNumber, NBreadcrumb, NBreadcrumbItem,
NTabs, NTabPane, NCollapse, NCollapseItem, NDynamicInput, NEmpty,
NTabs, NTabPane, NCollapse, NCollapseItem, NDynamicInput, NEmpty, NSwitch,
} from 'naive-ui'
import { useMessage } from 'naive-ui'
@@ -462,7 +501,7 @@ function normalizeVersion(v) {
return {
id: v.id || '', name: v.name || '', tag: v.tag || '',
days: v.days || 0, nights: v.nights || 0, line: v.line || '', route: v.route || '',
audience: v.audience || '', description: v.description || '',
audience: v.audience || '', description: v.description || '', isMainProduct: v.isMainProduct || false,
highlights: Array.isArray(v.highlights) ? v.highlights : [],
itinerary: normalizeItinerary(v.itinerary || []),
pricing: {