feat: 官网代码全量更新 (2026-04-18)

这个提交包含在:
wx
2026-04-18 15:13:28 +08:00
父节点 fcac8f88db
当前提交 f91ecbb093
共修改 135 个文件,包含 10307 行新增和 3349 行删除
@@ -67,13 +67,18 @@
@input="form.tips = $event.split('\n').filter(Boolean)"
/>
</n-form-item>
<n-form-item v-if="form.seasons" label="可去季节(每行一个)">
<n-input
type="textarea"
:rows="2"
:value="(form.seasons||[]).join('\n')"
@input="form.seasons = $event.split('\n').filter(Boolean)"
/>
<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>
@@ -118,7 +123,7 @@
</template>
<script setup>
import { NButton, NCard, NSpace, NInput, NForm, NFormItem, NSpin, NGrid, NGi, NBreadcrumb, NBreadcrumbItem } from 'naive-ui'
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' })
+105 -19
查看文件
@@ -5,53 +5,139 @@
<div style="font-size: 20px; font-weight: 600; color: #111827; margin-bottom: 4px;">景点详情</div>
<div style="font-size: 13px; color: #9ca3af;">管理各景点的详细介绍 · 对应官网「目的地」详情页</div>
</div>
<a href="/destinations" target="_blank">
<n-button>查看前台</n-button>
</a>
<n-space>
<n-button v-if="!sortMode" @click="enterSortMode">调整排序</n-button>
<template v-else>
<n-button @click="cancelSort">取消</n-button>
<n-button type="primary" :loading="saving" @click="saveSort">保存排序</n-button>
</template>
<a href="/destinations" target="_blank">
<n-button>查看前台</n-button>
</a>
</n-space>
</n-space>
<n-alert v-if="sortMode" type="info" :show-icon="false" style="margin-bottom: 12px;">
排序模式:使用 ↑ ↓ 按钮调整顺序,完成后点「保存排序」。取消则不保存。
</n-alert>
<n-spin v-if="!loaded" style="display:block; padding: 60px; text-align: center;" />
<template v-else>
<n-space vertical :size="8">
<NuxtLink
v-for="item in items"
:key="item._index"
:to="`/admin/content/destinations-detail/${item._index}`"
style="text-decoration: none;"
>
<template v-for="(item, idx) in items" :key="item._originalIndex">
<n-card hoverable>
<n-space justify="space-between" align="center">
<div>
<div style="font-size: 15px; font-weight: 600; color: #111827; margin-bottom: 4px;">{{ item.name || '未命名' }}</div>
<div style="flex: 1; min-width: 0;">
<div style="font-size: 15px; font-weight: 600; color: #111827; margin-bottom: 4px;">
<span v-if="sortMode" style="color: #9ca3af; margin-right: 8px; font-weight: 400;">#{{ idx + 1 }}</span>
{{ item.name || '未命名' }}
</div>
<div v-if="item.subtitle" style="font-size: 13px; color: #6b7280;">{{ item.subtitle }}</div>
</div>
<n-space align="center">
<n-tag v-if="item.tag" type="success" size="small">{{ item.tag }}</n-tag>
<span style="font-size: 20px; color: #d1d5db;">&rsaquo;</span>
<!-- 排序模式:显示上下移动按钮 -->
<template v-if="sortMode">
<n-button size="tiny" :disabled="idx === 0" @click="moveUp(idx)">↑</n-button>
<n-button size="tiny" :disabled="idx === items.length - 1" @click="moveDown(idx)">↓</n-button>
</template>
<!-- 编辑模式:显示跳转箭头 -->
<NuxtLink
v-else
:to="`/admin/content/destinations-detail/${item._originalIndex}`"
style="text-decoration: none; color: #d1d5db; font-size: 20px; padding: 0 8px;"
>&rsaquo;</NuxtLink>
</n-space>
</n-space>
</n-card>
</NuxtLink>
</template>
</n-space>
</template>
</div>
</template>
<script setup>
import { NButton, NCard, NSpace, NTag, NSpin } from 'naive-ui'
import { NButton, NCard, NSpace, NTag, NSpin, NAlert } from 'naive-ui'
import { useMessage } from 'naive-ui'
definePageMeta({ layout: 'admin', middleware: 'admin' })
const { adminFetch } = useAdmin()
const items = ref([]); const loaded = ref(false)
onMounted(async () => {
const message = useMessage()
const items = ref([])
const loaded = ref(false)
const sortMode = ref(false)
const saving = ref(false)
// 原始完整数据(用于保存时回写完整结构)
const rawDestinations = ref([])
const originalOrder = ref([])
async function loadData() {
loaded.value = false
try {
const res = await adminFetch('/api/admin/site-content?key=destinations-detail')
const data = res.data
const arr = Array.isArray(data) ? data : (data.destinations || [])
rawDestinations.value = arr
items.value = arr.map((d, i) => ({
_index: i, name: d.name || '', subtitle: d.subtitle || '', tag: d.tag || '', id: d.id || '',
_originalIndex: i,
name: d.name || '',
subtitle: d.subtitle || '',
tag: d.tag || '',
id: d.id || '',
}))
} catch {}
} catch (e) {
message.error('加载失败')
}
loaded.value = true
})
}
function enterSortMode() {
originalOrder.value = items.value.map(i => i._originalIndex)
sortMode.value = true
}
function cancelSort() {
// 按原始顺序还原
const restored = originalOrder.value.map(origIdx => {
return items.value.find(i => i._originalIndex === origIdx)
})
items.value = restored
sortMode.value = false
}
function moveUp(idx) {
if (idx === 0) return
const arr = [...items.value]
;[arr[idx - 1], arr[idx]] = [arr[idx], arr[idx - 1]]
items.value = arr
}
function moveDown(idx) {
if (idx >= items.value.length - 1) return
const arr = [...items.value]
;[arr[idx], arr[idx + 1]] = [arr[idx + 1], arr[idx]]
items.value = arr
}
async function saveSort() {
saving.value = true
try {
// 按当前显示顺序重组完整数据
const reordered = items.value.map(i => rawDestinations.value[i._originalIndex])
await adminFetch('/api/admin/site-content', {
method: 'PUT',
body: { key: 'destinations-detail', data: { destinations: reordered } },
})
message.success('排序已保存')
sortMode.value = false
await loadData()
} catch (e) {
message.error('保存失败:' + (e.data?.message || e.message))
}
saving.value = false
}
onMounted(loadData)
</script>