- 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>
424 行
21 KiB
Vue
424 行
21 KiB
Vue
<template>
|
||
<div style="max-width: 1100px">
|
||
<n-space justify="space-between" align="center" style="margin-bottom: 20px">
|
||
<div>
|
||
<h1 style="margin: 0 0 4px; font-size: 20px; font-weight: 600; color: #111827">关于我们</h1>
|
||
<p style="margin: 0; font-size: 13px; color: #9ca3af">管理关于页面的所有内容:品牌故事、团队、资质、荣誉等</p>
|
||
</div>
|
||
<n-space>
|
||
<n-button tag="a" href="/about" target="_blank" secondary>查看前台 ↗</n-button>
|
||
<n-button type="primary" :loading="saving" @click="save">保存</n-button>
|
||
</n-space>
|
||
</n-space>
|
||
|
||
<n-spin :show="!loaded">
|
||
<n-collapse :default-expanded-names="['story']">
|
||
<!-- story -->
|
||
<n-collapse-item title="品牌故事" name="story">
|
||
<n-card>
|
||
<n-form label-placement="top">
|
||
<n-form-item label="标题">
|
||
<n-input v-model:value="data.story.title" />
|
||
</n-form-item>
|
||
<n-form-item label="段落">
|
||
<n-dynamic-input v-model:value="data.story.paragraphs" :min="1">
|
||
<template #default="{ value, index }">
|
||
<n-input
|
||
type="textarea"
|
||
:autosize="{ minRows: 2, maxRows: 5 }"
|
||
:value="value"
|
||
@update:value="v => data.story.paragraphs[index] = v"
|
||
placeholder="段落内容"
|
||
style="width: 100%"
|
||
/>
|
||
</template>
|
||
</n-dynamic-input>
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- serviceJourney -->
|
||
<n-collapse-item title="服务旅程" name="serviceJourney">
|
||
<n-card>
|
||
<n-form label-placement="top">
|
||
<n-grid :cols="2" :x-gap="12">
|
||
<n-gi><n-form-item label="标题"><n-input v-model:value="data.serviceJourney.title" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="副标题"><n-input v-model:value="data.serviceJourney.subtitle" /></n-form-item></n-gi>
|
||
</n-grid>
|
||
<n-form-item label="服务时刻">
|
||
<div style="width: 100%">
|
||
<div v-for="(m, i) in data.serviceJourney.moments" :key="i" style="display: flex; gap: 8px; align-items: flex-start; margin-bottom: 8px">
|
||
<n-input-number v-model:value="m.step" placeholder="步骤" style="width: 80px" :min="1" />
|
||
<n-input v-model:value="m.name" placeholder="名称" style="width: 140px" />
|
||
<n-input v-model:value="m.detail" placeholder="详情" style="flex: 1" />
|
||
<n-button size="tiny" type="error" quaternary @click="data.serviceJourney.moments.splice(i, 1)">删</n-button>
|
||
</div>
|
||
<n-button size="small" type="primary" dashed @click="data.serviceJourney.moments.push({ step: data.serviceJourney.moments.length + 1, name: '', detail: '' })">添加时刻</n-button>
|
||
</div>
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- team -->
|
||
<n-collapse-item title="团队" name="team">
|
||
<n-card>
|
||
<n-form label-placement="top">
|
||
<n-form-item label="团队介绍">
|
||
<n-input v-model:value="data.team.summary" type="textarea" :autosize="{ minRows: 3, maxRows: 8 }" />
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- heritage -->
|
||
<n-collapse-item title="成长纪事" name="heritage">
|
||
<n-card>
|
||
<n-form label-placement="top">
|
||
<n-form-item label="标题">
|
||
<n-input v-model:value="data.heritage.title" />
|
||
</n-form-item>
|
||
<n-form-item label="里程碑">
|
||
<div style="width: 100%">
|
||
<n-card v-for="(ms, i) in data.heritage.milestones" :key="i" size="small" style="margin-bottom: 12px">
|
||
<template #header>
|
||
<n-space align="center">
|
||
<n-input v-model:value="ms.year" placeholder="年份" style="width: 100px" />
|
||
<n-button size="tiny" type="error" quaternary @click="data.heritage.milestones.splice(i, 1)">删除此年</n-button>
|
||
</n-space>
|
||
</template>
|
||
<n-dynamic-input v-model:value="ms.paragraphs" :min="1">
|
||
<template #default="{ value, index }">
|
||
<n-input
|
||
type="textarea"
|
||
:autosize="{ minRows: 2, maxRows: 5 }"
|
||
:value="value"
|
||
@update:value="v => ms.paragraphs[index] = v"
|
||
placeholder="段落内容"
|
||
style="width: 100%"
|
||
/>
|
||
</template>
|
||
</n-dynamic-input>
|
||
</n-card>
|
||
<n-button size="small" type="primary" dashed @click="data.heritage.milestones.push({ year: '', paragraphs: [''] })">添加年份</n-button>
|
||
</div>
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- culture -->
|
||
<n-collapse-item title="企业文化" name="culture">
|
||
<n-card>
|
||
<n-form label-placement="top">
|
||
<n-grid :cols="2" :x-gap="12">
|
||
<n-gi><n-form-item label="核心价值观"><n-input v-model:value="data.culture.core" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="信念"><n-input v-model:value="data.culture.belief" /></n-form-item></n-gi>
|
||
</n-grid>
|
||
|
||
<n-form-item label="价值观">
|
||
<div style="width: 100%">
|
||
<n-card v-for="(val, i) in data.culture.values" :key="i" size="small" style="margin-bottom: 12px">
|
||
<template #header>
|
||
<n-space align="center">
|
||
<n-input v-model:value="val.name" placeholder="名称" style="width: 120px" />
|
||
<n-button size="tiny" type="error" quaternary @click="data.culture.values.splice(i, 1)">删除</n-button>
|
||
</n-space>
|
||
</template>
|
||
<n-dynamic-input v-model:value="val.points" :min="1">
|
||
<template #default="{ value, index }">
|
||
<n-input
|
||
type="textarea"
|
||
:autosize="{ minRows: 1, maxRows: 3 }"
|
||
:value="value"
|
||
@update:value="v => val.points[index] = v"
|
||
placeholder="要点"
|
||
style="width: 100%"
|
||
/>
|
||
</template>
|
||
</n-dynamic-input>
|
||
</n-card>
|
||
<n-button size="small" type="primary" dashed @click="data.culture.values.push({ name: '', points: [''] })">添加价值观</n-button>
|
||
</div>
|
||
</n-form-item>
|
||
|
||
<n-form-item label="Slogan">
|
||
<div style="width: 100%">
|
||
<div v-for="(line, i) in data.culture.slogan.lines" :key="i" style="display: flex; gap: 8px; align-items: center; margin-bottom: 8px">
|
||
<n-input v-model:value="line.label" placeholder="标签" style="width: 100px" />
|
||
<n-input v-model:value="line.text" placeholder="文字" style="flex: 1" />
|
||
<n-button size="tiny" type="error" quaternary @click="data.culture.slogan.lines.splice(i, 1)">删</n-button>
|
||
</div>
|
||
<n-button size="small" type="primary" dashed @click="data.culture.slogan.lines.push({ label: '', text: '' })">添加行</n-button>
|
||
</div>
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- xiaohongshu -->
|
||
<n-collapse-item title="小红书" name="xiaohongshu">
|
||
<n-card>
|
||
<n-form label-placement="top">
|
||
<n-grid :cols="2" :x-gap="12">
|
||
<n-gi><n-form-item label="账号"><n-input v-model:value="data.xiaohongshu.account" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="认证类型"><n-input v-model:value="data.xiaohongshu.verifiedType" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="粉丝数"><n-input v-model:value="data.xiaohongshu.followers" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="点赞数"><n-input v-model:value="data.xiaohongshu.likes" /></n-form-item></n-gi>
|
||
</n-grid>
|
||
<n-form-item label="一句话介绍">
|
||
<n-input v-model:value="data.xiaohongshu.tagline" />
|
||
</n-form-item>
|
||
<n-form-item label="描述">
|
||
<n-input v-model:value="data.xiaohongshu.description" type="textarea" :autosize="{ minRows: 2, maxRows: 5 }" />
|
||
</n-form-item>
|
||
<n-form-item label="标签">
|
||
<n-dynamic-input v-model:value="data.xiaohongshu.tags" :min="1" />
|
||
</n-form-item>
|
||
<n-form-item label="荣誉">
|
||
<n-dynamic-input v-model:value="data.xiaohongshu.awards" :min="0" />
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- subsidiaries -->
|
||
<n-collapse-item title="子公司" name="subsidiaries">
|
||
<n-card>
|
||
<div style="width: 100%">
|
||
<n-card v-for="(sub, i) in data.subsidiaries" :key="i" size="small" style="margin-bottom: 12px">
|
||
<template #header>
|
||
<n-space align="center">
|
||
<span style="font-size: 13px; color: #6b7280">{{ i + 1 }}.</span>
|
||
<n-button size="tiny" type="error" quaternary @click="data.subsidiaries.splice(i, 1)">删除</n-button>
|
||
</n-space>
|
||
</template>
|
||
<n-form label-placement="top">
|
||
<n-grid :cols="2" :x-gap="12">
|
||
<n-gi><n-form-item label="名称"><n-input v-model:value="sub.name" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="成立时间"><n-input v-model:value="sub.established" /></n-form-item></n-gi>
|
||
</n-grid>
|
||
<n-form-item label="角色">
|
||
<n-input v-model:value="sub.role" type="textarea" :autosize="{ minRows: 1, maxRows: 3 }" />
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
<n-button size="small" type="primary" dashed @click="data.subsidiaries.push({ name: '', role: '', established: '' })">添加子公司</n-button>
|
||
</div>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- trademark -->
|
||
<n-collapse-item title="商标" name="trademark">
|
||
<n-card>
|
||
<n-form label-placement="top">
|
||
<n-grid :cols="2" :x-gap="12">
|
||
<n-gi><n-form-item label="商标名"><n-input v-model:value="data.trademark.name" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="注册范围"><n-input v-model:value="data.trademark.scope" /></n-form-item></n-gi>
|
||
<n-gi :span="2"><n-form-item label="持有者"><n-input v-model:value="data.trademark.holder" /></n-form-item></n-gi>
|
||
</n-grid>
|
||
<n-form-item label="描述">
|
||
<n-input v-model:value="data.trademark.description" type="textarea" :autosize="{ minRows: 2, maxRows: 5 }" />
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- copyrights -->
|
||
<n-collapse-item title="版权" name="copyrights">
|
||
<n-card>
|
||
<div style="width: 100%">
|
||
<n-card v-for="(c, i) in data.copyrights" :key="i" size="small" style="margin-bottom: 12px">
|
||
<template #header>
|
||
<n-space align="center">
|
||
<span style="font-size: 13px; color: #6b7280">{{ c.name || '新版权' }}</span>
|
||
<n-button size="tiny" type="error" quaternary @click="data.copyrights.splice(i, 1)">删除</n-button>
|
||
</n-space>
|
||
</template>
|
||
<n-form label-placement="top">
|
||
<n-grid :cols="2" :x-gap="12">
|
||
<n-gi><n-form-item label="作品名"><n-input v-model:value="c.name" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="登记号"><n-input v-model:value="c.regNo" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="类别"><n-input v-model:value="c.category" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="登记日期"><n-input v-model:value="c.date" /></n-form-item></n-gi>
|
||
<n-gi :span="2"><n-form-item label="持有者"><n-input v-model:value="c.holder" /></n-form-item></n-gi>
|
||
</n-grid>
|
||
<n-form-item label="描述">
|
||
<n-input v-model:value="c.description" type="textarea" :autosize="{ minRows: 1, maxRows: 3 }" />
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
<n-button size="small" type="primary" dashed @click="data.copyrights.push({ name: '', regNo: '', category: '', holder: '', date: '', description: '' })">添加版权</n-button>
|
||
</div>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- founder -->
|
||
<n-collapse-item title="创始人" name="founder">
|
||
<n-card>
|
||
<n-form label-placement="top">
|
||
<n-grid :cols="2" :x-gap="12">
|
||
<n-gi><n-form-item label="姓名"><n-input v-model:value="data.founder.name" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="头衔"><n-input v-model:value="data.founder.title" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="深耕年限"><n-input v-model:value="data.founder.yearsInHulunbuir" /></n-form-item></n-gi>
|
||
</n-grid>
|
||
<n-form-item label="背景">
|
||
<n-input v-model:value="data.founder.background" type="textarea" :autosize="{ minRows: 1, maxRows: 3 }" />
|
||
</n-form-item>
|
||
<n-form-item label="专长">
|
||
<n-dynamic-input v-model:value="data.founder.expertise" :min="1" />
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- guarantees -->
|
||
<n-collapse-item title="保障承诺" name="guarantees">
|
||
<n-card>
|
||
<div style="width: 100%">
|
||
<div v-for="(g, i) in data.guarantees" :key="i" style="margin-bottom: 12px; padding: 12px; border: 1px solid #e5e7eb; border-radius: 6px">
|
||
<n-space justify="space-between" style="margin-bottom: 8px">
|
||
<span style="font-size: 13px; font-weight: 500; color: #374151">{{ i + 1 }}.</span>
|
||
<n-button size="tiny" type="error" quaternary @click="data.guarantees.splice(i, 1)">删除</n-button>
|
||
</n-space>
|
||
<n-form label-placement="top">
|
||
<n-form-item label="标题"><n-input v-model:value="g.title" /></n-form-item>
|
||
<n-form-item label="详情"><n-input v-model:value="g.detail" type="textarea" :autosize="{ minRows: 1, maxRows: 3 }" /></n-form-item>
|
||
</n-form>
|
||
</div>
|
||
<n-button size="small" type="primary" dashed @click="data.guarantees.push({ title: '', detail: '' })">添加保障</n-button>
|
||
</div>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- licenses -->
|
||
<n-collapse-item title="许可证" name="licenses">
|
||
<n-card>
|
||
<div style="width: 100%">
|
||
<n-card v-for="(l, i) in data.licenses" :key="i" size="small" style="margin-bottom: 12px">
|
||
<template #header>
|
||
<n-space align="center">
|
||
<span style="font-size: 13px; color: #6b7280">{{ l.licenseNo || '新许可证' }}</span>
|
||
<n-button size="tiny" type="error" quaternary @click="data.licenses.splice(i, 1)">删除</n-button>
|
||
</n-space>
|
||
</template>
|
||
<n-form label-placement="top">
|
||
<n-grid :cols="2" :x-gap="12">
|
||
<n-gi><n-form-item label="许可证号"><n-input v-model:value="l.licenseNo" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="持有者"><n-input v-model:value="l.holder" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="发证机关"><n-input v-model:value="l.issuer" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="有效期"><n-input v-model:value="l.validPeriod" /></n-form-item></n-gi>
|
||
</n-grid>
|
||
<n-form-item label="备注">
|
||
<n-input v-model:value="l.note" />
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
<n-button size="small" type="primary" dashed @click="data.licenses.push({ licenseNo: '', holder: '', issuer: '', validPeriod: '', note: '' })">添加许可证</n-button>
|
||
</div>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- insurance -->
|
||
<n-collapse-item title="保险" name="insurance">
|
||
<n-card>
|
||
<n-form label-placement="top">
|
||
<n-grid :cols="2" :x-gap="12">
|
||
<n-gi><n-form-item label="标题"><n-input v-model:value="data.insurance.title" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="承保公司"><n-input v-model:value="data.insurance.insurer" /></n-form-item></n-gi>
|
||
</n-grid>
|
||
<n-form-item label="描述">
|
||
<n-input v-model:value="data.insurance.description" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" />
|
||
</n-form-item>
|
||
<n-form-item label="保障范围">
|
||
<n-input v-model:value="data.insurance.policyScope" />
|
||
</n-form-item>
|
||
<n-form-item label="理赔联系方式">
|
||
<n-input v-model:value="data.insurance.claimContact" />
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
|
||
<!-- awards -->
|
||
<n-collapse-item title="荣誉奖项" name="awards">
|
||
<n-card>
|
||
<div style="width: 100%">
|
||
<n-card v-for="(a, i) in data.awards" :key="i" size="small" style="margin-bottom: 12px">
|
||
<template #header>
|
||
<n-space align="center">
|
||
<span style="font-size: 13px; color: #6b7280">{{ a.year }} {{ a.title || '新奖项' }}</span>
|
||
<n-button size="tiny" type="error" quaternary @click="data.awards.splice(i, 1)">删除</n-button>
|
||
</n-space>
|
||
</template>
|
||
<n-form label-placement="top">
|
||
<n-grid :cols="2" :x-gap="12">
|
||
<n-gi><n-form-item label="年份"><n-input v-model:value="a.year" /></n-form-item></n-gi>
|
||
<n-gi><n-form-item label="颁发机构"><n-input v-model:value="a.issuer" /></n-form-item></n-gi>
|
||
</n-grid>
|
||
<n-form-item label="奖项名称">
|
||
<n-input v-model:value="a.title" />
|
||
</n-form-item>
|
||
<n-form-item label="详情">
|
||
<n-input v-model:value="a.detail" />
|
||
</n-form-item>
|
||
</n-form>
|
||
</n-card>
|
||
<n-button size="small" type="primary" dashed @click="data.awards.push({ year: '', title: '', detail: '', issuer: '' })">添加奖项</n-button>
|
||
</div>
|
||
</n-card>
|
||
</n-collapse-item>
|
||
</n-collapse>
|
||
</n-spin>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { NButton, NCard, NSpace, NInput, NForm, NFormItem, NSpin, NGrid, NGi, NCollapse, NCollapseItem, NDynamicInput, NInputNumber } from 'naive-ui'
|
||
import { useMessage } from 'naive-ui'
|
||
|
||
definePageMeta({ layout: 'admin', middleware: 'admin' })
|
||
const { adminFetch } = useAdmin()
|
||
const message = useMessage()
|
||
|
||
const data = ref({
|
||
story: { title: '', paragraphs: [] },
|
||
serviceJourney: { title: '', subtitle: '', moments: [] },
|
||
team: { summary: '' },
|
||
heritage: { title: '', milestones: [] },
|
||
culture: { core: '', belief: '', values: [], slogan: { lines: [] } },
|
||
xiaohongshu: { account: '', verified: true, verifiedType: '', followers: '', likes: '', tagline: '', description: '', tags: [], awards: [] },
|
||
subsidiaries: [],
|
||
trademark: { name: '', scope: '', holder: '', description: '' },
|
||
copyrights: [],
|
||
founder: { name: '', title: '', background: '', expertise: [], yearsInHulunbuir: '' },
|
||
guarantees: [],
|
||
licenses: [],
|
||
insurance: { title: '', description: '', insurer: '', policyScope: '', claimContact: '' },
|
||
awards: []
|
||
})
|
||
const loaded = ref(false)
|
||
const saving = ref(false)
|
||
|
||
async function load() {
|
||
const res = await adminFetch('/api/admin/site-content?key=about')
|
||
data.value = res.data
|
||
loaded.value = true
|
||
}
|
||
|
||
async function save() {
|
||
saving.value = true
|
||
try {
|
||
await adminFetch('/api/admin/site-content', { method: 'PUT', body: { key: 'about', data: data.value } })
|
||
message.success('已保存')
|
||
} catch (e) {
|
||
message.error('保存失败')
|
||
} finally {
|
||
saving.value = false
|
||
}
|
||
}
|
||
|
||
onMounted(load)
|
||
</script>
|