feat(admin): 分区增删、资料设置、操作条与交互收尾
- POST/DELETE /api/categories(需登录,级联,local 保护) - profile 设置(头像/名称/角色/简介/位置)应用到资料卡 - 详情操作条:浏览 +1、点赞切换、分享复制、跳转外链 - 编辑按钮垂直居中且留 12px;设置页按钮上间距、保存 Toast、Tabs 去滚动条 - 登录密码可见切换 - 测试 48 passed
This commit is contained in:
@@ -19,6 +19,16 @@ export type SectionSettings = {
|
||||
limits: Record<string, number>
|
||||
}
|
||||
|
||||
export type ProfileSettings = {
|
||||
name: string
|
||||
role: string
|
||||
description: string
|
||||
/** 完整文案,如「IP属地:局域网」 */
|
||||
location: string
|
||||
/** 头像地址;空表示用图标占位 */
|
||||
avatar: string
|
||||
}
|
||||
|
||||
export type SiteSettings = {
|
||||
header: {
|
||||
enabled: boolean
|
||||
@@ -31,6 +41,7 @@ export type SiteSettings = {
|
||||
order: string[]
|
||||
}
|
||||
sections: SectionSettings
|
||||
profile: ProfileSettings
|
||||
}
|
||||
|
||||
export type SettingsDocument = {
|
||||
@@ -62,6 +73,13 @@ export function defaultSettings(): SiteSettings {
|
||||
},
|
||||
sidebar: { hidden: [], order: [] },
|
||||
sections: { hidden: [], order: [], titles: {}, limits: {} },
|
||||
profile: {
|
||||
name: '本地管理员',
|
||||
role: '管理员',
|
||||
description: '本地静态站点托管与预览,所有项目都运行在这台机器上。',
|
||||
location: 'IP属地:局域网',
|
||||
avatar: '',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,8 +107,7 @@ export async function fetchSettings(): Promise<SettingsDocument> {
|
||||
export async function saveSettings(
|
||||
version: number,
|
||||
settings: SiteSettings,
|
||||
): Promise<SettingsResult> {
|
||||
try {
|
||||
): Promise<SettingsResult> { try {
|
||||
const response = await fetch('/api/settings', {
|
||||
method: 'PATCH',
|
||||
credentials: 'same-origin',
|
||||
@@ -119,8 +136,7 @@ export async function saveSettings(
|
||||
|
||||
/**
|
||||
* 按设置重排侧栏:先按 `order`,未列出的保持原顺序,`hidden` 过滤掉。
|
||||
*/
|
||||
export function applySidebarSettings<T extends { id: string }>(
|
||||
*/export function applySidebarSettings<T extends { id: string }>(
|
||||
categories: T[],
|
||||
sidebar: SiteSettings['sidebar'],
|
||||
): T[] {
|
||||
@@ -155,8 +171,7 @@ export function sectionLimit(
|
||||
return typeof override === 'number' && override > 0 ? override : fallback
|
||||
}
|
||||
|
||||
function orderAndFilter<T extends { id: string }>(
|
||||
categories: T[],
|
||||
function orderAndFilter<T extends { id: string }>( categories: T[],
|
||||
config: { hidden: string[]; order: string[] },
|
||||
): T[] {
|
||||
const hidden = new Set(config.hidden)
|
||||
@@ -172,3 +187,39 @@ function orderAndFilter<T extends { id: string }>(
|
||||
.sort((a, b) => (a.rank === b.rank ? a.index - b.index : a.rank - b.rank))
|
||||
.map((entry) => entry.category)
|
||||
}
|
||||
|
||||
// ---------- 分区(首页卡片 / 侧栏项)的新增与删除 ----------
|
||||
|
||||
export type NewCategory = { id: string; title: string; kind: 'apps' | 'posters' | 'articles' }
|
||||
|
||||
export type SimpleResult = { ok: true } | { ok: false; error: string }
|
||||
|
||||
export async function createCategory(input: NewCategory): Promise<SimpleResult> {
|
||||
try {
|
||||
const response = await fetch('/api/categories', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
})
|
||||
if (response.ok) return { ok: true }
|
||||
const payload = (await response.json().catch(() => null)) as { error?: string } | null
|
||||
return { ok: false, error: payload?.error ?? `创建失败(${response.status})` }
|
||||
} catch (error) {
|
||||
return { ok: false, error: error instanceof Error ? error.message : '网络错误' }
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteCategory(id: string): Promise<SimpleResult> {
|
||||
try {
|
||||
const response = await fetch(`/api/categories/${encodeURIComponent(id)}`, {
|
||||
method: 'DELETE',
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
if (response.ok) return { ok: true }
|
||||
const payload = (await response.json().catch(() => null)) as { error?: string } | null
|
||||
return { ok: false, error: payload?.error ?? `删除失败(${response.status})` }
|
||||
} catch (error) {
|
||||
return { ok: false, error: error instanceof Error ? error.message : '网络错误' }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user