170 行
6.4 KiB
Vue

<template>
<div style="max-width: 900px;">
<n-breadcrumb style="margin-bottom: 16px;">
<n-breadcrumb-item>
<NuxtLink to="/admin/content/destinations-detail">景点详情</NuxtLink>
</n-breadcrumb-item>
<n-breadcrumb-item>{{ form.name || '编辑' }}</n-breadcrumb-item>
</n-breadcrumb>
<n-space justify="space-between" align="center" style="margin-bottom: 24px;">
<div style="font-size: 20px; font-weight: 600; color: #111827;">{{ form.name || '编辑景点' }}</div>
<n-space>
<NuxtLink to="/admin/content/destinations-detail" style="text-decoration: none;">
<n-button>返回列表</n-button>
</NuxtLink>
<n-button type="primary" :loading="saving" :disabled="saving" @click="handleSave">
{{ saving ? '保存中...' : '保存' }}
</n-button>
</n-space>
</n-space>
<n-spin v-if="pageLoading" style="display:block; padding: 60px; text-align: center;" />
<n-card v-else-if="loadError" style="color: #ef4444; text-align: center; padding: 40px;">{{ loadError }}</n-card>
<template v-else>
<n-card title="基本信息" style="margin-bottom: 16px;">
<n-grid :cols="2" :x-gap="12">
<n-gi>
<n-form-item label="ID">
<n-input v-model:value="form.id" />
</n-form-item>
</n-gi>
<n-gi>
<n-form-item label="名称">
<n-input v-model:value="form.name" />
</n-form-item>
</n-gi>
<n-gi>
<n-form-item label="副标题">
<n-input v-model:value="form.subtitle" />
</n-form-item>
</n-gi>
<n-gi>
<n-form-item label="标签">
<n-input v-model:value="form.tag" />
</n-form-item>
</n-gi>
</n-grid>
<n-form-item label="描述">
<n-input v-model:value="form.description" type="textarea" :rows="5" />
</n-form-item>
</n-card>
<n-card title="亮点与建议" style="margin-bottom: 16px;">
<n-form-item label="亮点(每行一个)">
<n-input
type="textarea"
:rows="4"
:value="(form.highlights||[]).join('\n')"
@input="form.highlights = $event.split('\n').filter(Boolean)"
/>
</n-form-item>
<n-form-item label="旅行建议(每行一个)">
<n-input
type="textarea"
:rows="4"
:value="(form.tips||[]).join('\n')"
@input="form.tips = $event.split('\n').filter(Boolean)"
/>
</n-form-item>
<n-form-item v-if="form.seasons" label="可去季节">
<n-checkbox-group
:value="form.seasons || []"
@update:value="form.seasons = $event"
>
<n-space>
<n-checkbox value="spring">春季</n-checkbox>
<n-checkbox value="summer">夏季</n-checkbox>
<n-checkbox value="autumn">秋季</n-checkbox>
<n-checkbox value="winter">冬季</n-checkbox>
</n-space>
</n-checkbox-group>
</n-form-item>
</n-card>
<n-card v-if="form.bestSeason" title="最佳季节" style="margin-bottom: 16px;">
<n-grid :cols="2" :x-gap="12">
<n-gi>
<n-form-item label="季节">
<n-input v-model:value="form.bestSeason.season" />
</n-form-item>
</n-gi>
<n-gi>
<n-form-item label="原因">
<n-input v-model:value="form.bestSeason.reason" />
</n-form-item>
</n-gi>
</n-grid>
</n-card>
<n-card v-if="form.seo" title="SEO" style="margin-bottom: 16px;">
<n-form-item label="SEO 标题">
<n-input v-model:value="form.seo.title" />
</n-form-item>
<n-form-item label="SEO 描述">
<n-input v-model:value="form.seo.description" type="textarea" :rows="2" />
</n-form-item>
<n-form-item v-if="form.seo.keywords" label="关键词">
<n-input v-model:value="form.seo.keywords" />
</n-form-item>
</n-card>
<p v-if="formError" style="color: #ef4444; font-size: 13px; margin: 12px 0 0;">{{ formError }}</p>
<div class="save-bar">
<NuxtLink to="/admin/content/destinations-detail" style="text-decoration: none;">
<n-button>返回列表</n-button>
</NuxtLink>
<n-button type="primary" :loading="saving" :disabled="saving" @click="handleSave">
{{ saving ? '保存中...' : '保存' }}
</n-button>
</div>
</template>
</div>
</template>
<script setup>
import { NButton, NCard, NSpace, NInput, NForm, NFormItem, NSpin, NGrid, NGi, NBreadcrumb, NBreadcrumbItem, NCheckbox, NCheckboxGroup } from 'naive-ui'
import { useMessage } from 'naive-ui'
definePageMeta({ layout: 'admin', middleware: 'admin', name: 'admin-content-destinations-edit' })
const route = useRoute()
const { adminFetch } = useAdmin()
const message = useMessage()
const idx = computed(() => Number(route.params.idx))
const form = ref({})
const pageLoading = ref(true); const loadError = ref(''); const saving = ref(false); const formError = ref('')
async function loadItem() {
try {
const res = await adminFetch('/api/admin/site-content?key=destinations-detail')
const data = res.data
const arr = Array.isArray(data) ? data : (data.destinations || [])
if (idx.value >= arr.length) { loadError.value = '索引超出范围'; return }
form.value = JSON.parse(JSON.stringify(arr[idx.value]))
} catch (e) { loadError.value = e.data?.message || '加载失败' }
finally { pageLoading.value = false }
}
async function handleSave() {
saving.value = true; formError.value = ''
try {
const res = await adminFetch('/api/admin/site-content?key=destinations-detail')
const data = res.data
if (Array.isArray(data)) {
data[idx.value] = form.value
} else {
data.destinations[idx.value] = form.value
}
await adminFetch('/api/admin/site-content', { method: 'PUT', body: { key: 'destinations-detail', data } })
message.success('保存成功')
} catch (e) { formError.value = e.data?.message || '保存失败' }
finally { saving.value = false }
}
onMounted(loadItem)
</script>
<style scoped>
.save-bar { position: sticky; bottom: 0; display: flex; justify-content: flex-end; gap: 8px; padding: 16px 0; margin-top: 24px; background: #f0f2f5; border-top: 1px solid #e5e7eb; }
</style>