feat(admin): 分区增删、资料设置、操作条与交互收尾

- POST/DELETE /api/categories(需登录,级联,local 保护)
- profile 设置(头像/名称/角色/简介/位置)应用到资料卡
- 详情操作条:浏览 +1、点赞切换、分享复制、跳转外链
- 编辑按钮垂直居中且留 12px;设置页按钮上间距、保存 Toast、Tabs 去滚动条
- 登录密码可见切换
- 测试 48 passed
This commit is contained in:
stash
2026-10-29 20:15:56 +08:00
parent 3c6c20e534
commit 427338b329
15 changed files with 851 additions and 54 deletions
@@ -1,28 +1,34 @@
import { Avatar, Card, Icon, Text, Title } from '@stash/ui'
import { PLACEHOLDER_PROFILE } from '../../profile'
import type { ProfileSettings } from '../../settings'
/**
* 用户资料卡:头像、认证徽标、昵称、角色、简介、IP 属地。
* 数据来自 profile.ts 的占位对象,后端接入鉴权后替换。
* 内容来自站点设置(/admin → 资料),未设置时用默认文案。
*/
export function ProfileCard() {
const { name, role, bio, location } = PLACEHOLDER_PROFILE
type ProfileCardProps = {
profile: ProfileSettings
}
export function ProfileCard({ profile }: ProfileCardProps) {
return (
<Card padding="none" className="profile-card">
<Avatar size="xl" tone="primary" icon={<Icon name="user_3_line" />} verified />
{profile.avatar !== '' ? (
<Avatar size="xl" src={profile.avatar} alt={profile.name} verified />
) : (
<Avatar size="xl" tone="primary" icon={<Icon name="user_3_line" />} verified />
)}
<Title level={4} weight="semibold" className="profile-name">
{name}
{profile.name}
</Title>
<span className="profile-role">{role}</span>
<span className="profile-role">{profile.role}</span>
<Text size="xs" tone="sub" className="profile-bio">
{bio}
{profile.description}
</Text>
<span className="profile-location">IP属地:{location}</span>
<span className="profile-location">{profile.location}</span>
</Card>
)
}
+4 -1
View File
@@ -1,6 +1,7 @@
import type { Project } from '../../api'
import type { NavTarget } from '../../data/nav'
import type { Category } from '../../data/types'
import type { ProfileSettings } from '../../settings'
import { CategoryNav } from './CategoryNav'
import { ProfileCard } from './ProfileCard'
import { ServicePanel } from './ServicePanel'
@@ -13,6 +14,7 @@ type SidebarProps = {
projects: Project[]
/** 内容目录;侧栏直接由它派生 */
categories: Category[]
profile: ProfileSettings
live: boolean
refreshing: boolean
onRefresh: () => void
@@ -28,6 +30,7 @@ type SidebarProps = {
export function Sidebar({
projects,
categories,
profile,
live,
refreshing,
onRefresh,
@@ -39,7 +42,7 @@ export function Sidebar({
}: SidebarProps) {
return (
<div className="sidebar">
<ProfileCard />
<ProfileCard profile={profile} />
<ServicePanel
projects={projects}