feat: 官网代码全量更新 (2026-04-18)
这个提交包含在:
@@ -0,0 +1,40 @@
|
||||
// 通用排序工具:在分页列表内与相邻行交换 sortOrder
|
||||
// 用法:
|
||||
// const { swapAdjacent } = useAdminSort('/api/admin/faqs')
|
||||
// await swapAdjacent(currentRow, neighborRow) // 把两条的 sortOrder 字段互换并落库
|
||||
export function useAdminSort(apiBase) {
|
||||
const { adminFetch } = useAdmin()
|
||||
|
||||
async function swapAdjacent(a, b) {
|
||||
if (!a || !b) return
|
||||
const orderA = a.sortOrder ?? 0
|
||||
const orderB = b.sortOrder ?? 0
|
||||
// 同时发两次 PUT(并行)
|
||||
await Promise.all([
|
||||
adminFetch(`${apiBase}/${a.id}`, { method: 'PUT', body: { sortOrder: orderB } }),
|
||||
adminFetch(`${apiBase}/${b.id}`, { method: 'PUT', body: { sortOrder: orderA } }),
|
||||
])
|
||||
a.sortOrder = orderB
|
||||
b.sortOrder = orderA
|
||||
}
|
||||
|
||||
// 在列表内按 UI 位置上移/下移(仅与相邻行交换)
|
||||
async function moveUp(list, index) {
|
||||
if (index <= 0) return
|
||||
const a = list[index]
|
||||
const b = list[index - 1]
|
||||
await swapAdjacent(a, b)
|
||||
// UI 顺序也要立即交换,让人看见结果
|
||||
;[list[index - 1], list[index]] = [list[index], list[index - 1]]
|
||||
}
|
||||
|
||||
async function moveDown(list, index) {
|
||||
if (index >= list.length - 1) return
|
||||
const a = list[index]
|
||||
const b = list[index + 1]
|
||||
await swapAdjacent(a, b)
|
||||
;[list[index], list[index + 1]] = [list[index + 1], list[index]]
|
||||
}
|
||||
|
||||
return { swapAdjacent, moveUp, moveDown }
|
||||
}
|
||||
+1
-1
@@ -300,7 +300,7 @@ export function useHowToSchema(sections) {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'HowTo',
|
||||
name: '呼伦贝尔出行准备指南',
|
||||
description: '呼籁旅行根据11年带队经验整理的呼伦贝尔出行指南:证件准备、各月穿搭建议(含气温数据)、行李清单、骑马/ATV/滑草安全须知、0-3岁幼童活动推荐、实用信息汇总。',
|
||||
description: '呼籁旅行根据11年带队经验整理的呼伦贝尔出行指南:证件准备、各月穿搭建议(含气温数据)、行李清单、骑马/全地形越野车/滑草安全须知、0-3岁幼童活动推荐、实用信息汇总。',
|
||||
step: steps,
|
||||
totalTime: 'PT30M',
|
||||
estimatedCost: { '@type': 'MonetaryAmount', currency: 'CNY', value: '0' },
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 栏目标题(CommonSectionTitle)统一读取器
|
||||
*
|
||||
* 用法:
|
||||
* const st = await useSectionTitles()
|
||||
* <CommonSectionTitle
|
||||
* :title="st('summerCamp.principles', '行程设计四大原则')"
|
||||
* :subtitle="st('summerCamp.principles', '每一处安排都经过精心考量', 'subtitle')"
|
||||
* />
|
||||
*
|
||||
* st(path, fallback, field = 'title') 三个参数都是字符串。
|
||||
* field 可选 'title' | 'subtitle' | 'label'。
|
||||
*/
|
||||
|
||||
export async function useSectionTitles() {
|
||||
const { data } = await usePublicApi('section-titles')
|
||||
|
||||
return (path, fallback = '', field = 'title') => {
|
||||
const parts = (path || '').split('.')
|
||||
let cursor = data.value
|
||||
for (const p of parts) {
|
||||
if (cursor && typeof cursor === 'object' && p in cursor) {
|
||||
cursor = cursor[p]
|
||||
} else {
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
if (cursor && typeof cursor === 'object') {
|
||||
return cursor[field] || fallback
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户