118 行
4.2 KiB
Vue
118 行
4.2 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">统一管理各页面顶部 Hero 区域的副标题文案</p>
|
||
</div>
|
||
<n-space>
|
||
<n-button type="primary" :loading="saving" @click="save">保存</n-button>
|
||
</n-space>
|
||
</n-space>
|
||
|
||
<n-spin :show="!loaded">
|
||
<n-space vertical :size="12">
|
||
<n-card
|
||
v-for="item in entries"
|
||
:key="item.key"
|
||
size="small"
|
||
>
|
||
<template #header>
|
||
<n-space align="center" :size="12">
|
||
<span style="font-size: 14px; font-weight: 600; color: #111827">{{ item.label }}</span>
|
||
<n-button
|
||
size="tiny"
|
||
tag="a"
|
||
:href="item.preview"
|
||
target="_blank"
|
||
quaternary
|
||
style="color: #6b7280"
|
||
>
|
||
查看前台 ↗
|
||
</n-button>
|
||
</n-space>
|
||
</template>
|
||
<n-input
|
||
v-model:value="data[item.key]"
|
||
type="textarea"
|
||
:autosize="{ minRows: 1, maxRows: 4 }"
|
||
:placeholder="`${item.label} 的副标题`"
|
||
/>
|
||
</n-card>
|
||
</n-space>
|
||
</n-spin>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { NButton, NCard, NSpace, NInput, NSpin } from 'naive-ui'
|
||
import { useMessage } from 'naive-ui'
|
||
|
||
definePageMeta({ layout: 'admin', middleware: 'admin' })
|
||
const { adminFetch } = useAdmin()
|
||
const message = useMessage()
|
||
|
||
// 23 个页面的元信息:key(对应 heroes.json 的字段)、显示名、前台路径
|
||
const entries = [
|
||
{ key: 'about', label: '关于我们', preview: '/about' },
|
||
{ key: 'reviews', label: '客户评价', preview: '/reviews' },
|
||
{ key: 'guides', label: '出行指南', preview: '/guides' },
|
||
{ key: 'guide-custom', label: '定制游怎么选', preview: '/guide-custom' },
|
||
{ key: 'youji', label: '客户游记列表', preview: '/youji' },
|
||
{ key: 'xiaohongshu', label: '小红书游记列表', preview: '/xiaohongshu' },
|
||
{ key: 'stories', label: '客户故事列表', preview: '/stories' },
|
||
{ key: 'winter-camp', label: '冬令营', preview: '/winter-camp' },
|
||
{ key: 'summer-camp', label: '夏令营', preview: '/summer-camp' },
|
||
{ key: 'calendar', label: '旅行日历', preview: '/calendar' },
|
||
{ key: 'customize', label: '定制入口', preview: '/customize' },
|
||
{ key: 'blog', label: '博客列表', preview: '/blog' },
|
||
{ key: 'courses', label: '亲子研学课程', preview: '/courses' },
|
||
{ key: 'partners', label: '合作伙伴', preview: '/partners' },
|
||
{ key: 'contact', label: '联系我们', preview: '/contact' },
|
||
{ key: 'destinations', label: '目的地列表', preview: '/destinations' },
|
||
{ key: 'news', label: '最新动态', preview: '/news' },
|
||
{ key: 'products', label: '产品总览', preview: '/products' },
|
||
{ key: 'products-winter', label: '冬季产品', preview: '/products/winter' },
|
||
{ key: 'qualifications', label: '资质荣誉', preview: '/qualifications' },
|
||
{ key: 'selector', label: '产品选择器', preview: '/selector' },
|
||
{ key: 'pricing', label: '价格公开', preview: '/pricing' },
|
||
{ key: 'gallery', label: '呼籁旅拍', preview: '/gallery' },
|
||
]
|
||
|
||
const data = ref({})
|
||
const loaded = ref(false)
|
||
const saving = ref(false)
|
||
|
||
async function load() {
|
||
try {
|
||
const res = await adminFetch('/api/admin/site-content?key=heroes')
|
||
data.value = res.data || {}
|
||
} catch (e) {
|
||
data.value = {}
|
||
} finally {
|
||
// 保证 23 个 key 都存在,避免 v-model 报错
|
||
for (const item of entries) {
|
||
if (typeof data.value[item.key] !== 'string') data.value[item.key] = ''
|
||
}
|
||
loaded.value = true
|
||
}
|
||
}
|
||
|
||
async function save() {
|
||
saving.value = true
|
||
try {
|
||
await adminFetch('/api/admin/site-content', {
|
||
method: 'PUT',
|
||
body: { key: 'heroes', data: data.value },
|
||
})
|
||
message.success('已保存')
|
||
} catch (e) {
|
||
message.error('保存失败:' + (e.data?.message || ''))
|
||
} finally {
|
||
saving.value = false
|
||
}
|
||
}
|
||
|
||
onMounted(load)
|
||
</script>
|