diff --git a/docs/PROJECT_FUNCTIONS_AND_DB_DICTIONARY.md b/docs/PROJECT_FUNCTIONS_AND_DB_DICTIONARY.md deleted file mode 100644 index e35eddf..0000000 --- a/docs/PROJECT_FUNCTIONS_AND_DB_DICTIONARY.md +++ /dev/null @@ -1,523 +0,0 @@ -# QuyUn 项目功能与数据库字典(用于多租户改造) - -本文档基于当前仓库代码静态分析整理(`frontend/` + `backend/`),目标是帮助你将“单实例/单运营方(单用户后台)”改造为“多租户(多运营方)”。 - ---- - -## 1. 仓库结构与技术栈 - -### 1.1 目录结构 - -- `backend/`:Go 后端(同时包含少量 `package.json` 依赖,但核心为 Go 服务) -- `frontend/admin/`:后台管理端(Vue3 + Vite) -- `frontend/wechat/`:微信 H5 端(Vue3 + Vite) - -### 1.2 关键技术栈 - -**后端** - -- Web 框架:`gofiber/fiber/v3` -- DI / 代码生成:`go.ipao.vip/atom`(路由文件为 `routes.gen.go`) -- 数据库:PostgreSQL(`lib/pq`、`pgx`),迁移:`pressly/goose` -- SQL Builder/ORM:`go-jet/jet`(`backend/database/table/*`、`backend/app/model/*.gen.go`) -- 任务队列:`riverqueue/river`(依赖表:`river_job` 等) -- 对象存储:阿里云 OSS(签名上传、签名下载、删除) -- 微信:网页授权(OAuth)、JS-SDK 签名、支付/退款回调(微信支付 v3) -- 监控链路:OpenTelemetry(存在 provider 但本文档不展开) - -**前端** - -- Vue 3 + Vite -- Admin:PrimeVue + Tailwind(并用少量 DaisyUI) -- WeChat H5:Tailwind + `weixin-js-sdk` + `xgplayer`(视频播放) - ---- - -## 2. 运行时架构与路由总览 - -### 2.1 HTTP 服务入口与静态资源托管 - -后端启动入口:`backend/main.go`,命令为 `serve`(`backend/app/service/http/http.go`)。 - -HTTP 路由前缀固定为 `/v1`: - -- API:统一挂载在 `/v1/*` -- 静态资源: - - 后台管理端:`GET /admin*` → `App.DistAdmin` 指向的 `frontend/admin/dist` - - 微信端:`GET /*` → `App.DistWeChat` 指向的 `frontend/wechat/dist` - -这意味着项目典型部署形态是:**一个后端进程同时提供 API + 两套前端静态文件**。 - -### 2.2 认证/授权(非常影响多租户改造) - -#### 2.2.1 微信端用户认证(Cookie Token + 重定向) - -中间件:`backend/app/middlewares/mid_auth.go` - -- 受保护范围:除以下路径外,均要求登录 - - `/v1/pay/callback/*`(支付回调免登录) - - `/v1/auth/*`(授权流程免登录) - - `/v1/admin/*`(后台 API 走另一套鉴权) -- 机制: - - 读取 Cookie:`token` - - 解析 JWT(`providers/jwt`),拿到 `UserID` - - 查询用户(`users` 表) - - 校验 `users.auth_token.expires_at`(微信 access token 过期则强制重登) - - 未登录时:非 XHR 请求会重定向到 `/v1/auth/wechat?redirect=当前完整URL`;XHR 请求直接返回 401 -- 认证通过后写入:`ctx.Locals("user", *model.Users)` - -> 重要:当前系统的“用户”是**微信 OpenID 用户**,并且 `users.open_id` 在 DB 层为 `UNIQUE`(单租户假设)。 - -#### 2.2.2 后台管理端鉴权(硬编码账号 + 固定 UserID) - -登录接口:`POST /v1/admin/auth`(`backend/app/http/admin/auth.go`) - -- 用户名/密码硬编码在后端:`pl.yang` / `Xixi@0202` -- 登录成功签发 JWT,`UserID` 固定写死为 `-20140202` - -中间件:`backend/app/middlewares/mid_auth_admin.go` - -- 对 `/v1/admin/*` 生效(除 `/v1/admin/auth`) -- 从 Header `Authorization` 或 Query `token` 读取 JWT -- 校验 `jwt.UserID == -20140202`,否则 403 - -> 重要:这套后台体系是“单运营方/单管理员”的实现方式,多租户必须重构为“租户维度的后台账号体系”。 - -### 2.3 API 路由清单(从 `routes.gen.go` 汇总) - -#### 2.3.1 微信端(非 admin)API - -路由文件:`backend/app/http/routes.gen.go` - -- `GET /v1/auth/wechat`:发起微信网页授权(重定向到微信授权页) -- `GET /v1/auth/login`:微信回调,换取 openid + 用户信息,写入 Cookie `token`,再重定向回 `redirect` -- `GET /v1/posts`:曲谱列表(仅已发布),支持 keyword 搜索,返回是否已购买、封面图 OSS 签名 URL -- `GET /v1/posts/:id/show`:曲谱详情(仅已发布),返回购买态、封面图 URL -- `GET /v1/posts/:id/play`:获取可播放视频 URL(未购买返回“短视频/试听”,已购买返回“完整版”) -- `GET /v1/posts/mine`:我的已购曲谱列表 -- `POST /v1/posts/:id/buy`:购买(当前实现主路径为“余额支付”) -- `GET /v1/users/profile`:当前用户资料 + 余额 -- `PUT /v1/users/username`:修改用户名(最多 12 字符) -- `GET /v1/wechats/js-sdk`:获取 JS-SDK 签名配置 -- `POST /v1/pay/callback/:channel`:支付/退款回调入口(免登录),内部将回调入队列异步处理 - -#### 2.3.2 后台管理端(admin)API - -路由文件:`backend/app/http/admin/routes.gen.go` - -- `POST /v1/admin/auth`:后台登录(硬编码账号),返回 Token - -媒体库: - -- `GET /v1/admin/medias`:媒体列表(分页 + keyword) -- `GET /v1/admin/medias/:id`:媒体预览(302 跳转到 OSS 签名 URL) -- `DELETE /v1/admin/medias/:id`:删除 OSS 文件 + DB 记录 - -上传: - -- `GET /v1/admin/uploads/pre-uploaded-check/:md5.:ext?mime=...`:按 md5 判断是否已存在;不存在则返回 OSS 预签名 PUT URL -- `POST /v1/admin/uploads/post-uploaded-action`:上传完成回调:写入 `medias`;若为 `video/mp4` 触发下载/转码类任务 - -曲谱: - -- `GET /v1/admin/posts`:曲谱列表(分页 + keyword),附带销量(购买次数) -- `POST /v1/admin/posts`:创建曲谱(包含封面 head_images 与媒体 assets) -- `PUT /v1/admin/posts/:id`:编辑曲谱 -- `DELETE /v1/admin/posts/:id`:硬删除 -- `GET /v1/admin/posts/:id`:曲谱详情(附带 medias 列表) -- `POST /v1/admin/posts/:id/send-to/:userId`:赠送曲谱(写入 `user_posts`,price=-1) - -用户: - -- `GET /v1/admin/users`:用户列表(分页 + keyword) -- `GET /v1/admin/users/:id`:用户详情 -- `GET /v1/admin/users/:id/articles`:用户已购曲谱(分页) -- `POST /v1/admin/users/:id/balance`:后台给用户充值余额(单位:分) - -订单: - -- `GET /v1/admin/orders`:订单列表(分页 + 按订单号/用户过滤),返回附带 `post_title`、`username` -- `POST /v1/admin/orders/:id/refund`:退款(余额支付直接退余额 + 撤销权限;微信支付走退款 API 并等待回调) - -统计: - -- `GET /v1/admin/statistics`:仪表盘统计(草稿/已发布、媒体数、已完成订单数、用户数、已完成订单金额汇总) - ---- - -## 3. 前端功能梳理 - -### 3.1 Admin(`frontend/admin`) - -**路由(`frontend/admin/src/router.js`,base 为 `/admin/`)** - -- `/`:Dashboard(统计) -- `/medias`:媒体库列表(预览/下载/删除) -- `/medias/uploads`:媒体上传(MD5 去重 + OSS 预签名上传 + 上传后回调) -- `/posts`:曲谱列表(创建/编辑/删除/赠送) -- `/posts/create`:创建曲谱(选择封面图 ≤3、选择媒体资源、设置价格/折扣/状态) -- `/posts/edit/:id`:编辑曲谱 -- `/users`:用户列表(可充值) -- `/users/:id`:用户详情 + 已购列表 -- `/orders`:订单列表 + 退款 -- `/login`:后台登录页 - -**API 调用方式** - -- Axios baseURL:`/v1`(`frontend/admin/src/api/httpClient.js`) -- Token:本地存储 `__token`,请求头写入 `Authorization: Bearer ` - -### 3.2 WeChat H5(`frontend/wechat`) - -**路由(`frontend/wechat/src/router.js`)** - -- `/`:曲谱列表(无限滚动 + 搜索) -- `/posts/:id`:曲谱详情(视频播放 + 购买按钮 + 分享) -- `/purchased`:已购列表(顶部固定播放器 + 列表点播) -- `/profile`:个人信息 + 余额 - -**API 调用方式** - -- Axios baseURL:`/v1`,并且 `withCredentials: true`(携带 Cookie `token`) -- 401 时前端自动跳转 `/v1/auth/wechat?redirect=<当前URL>`(`frontend/wechat/src/api/client.js`) - -**注意:接口路径不一致** - -`frontend/wechat/src/api/userApi.js` 的更新接口调用 `PUT /users/profile`,但后端实际是 `PUT /v1/users/username`。 - ---- - -## 4. 后端业务流程(核心用例) - -### 4.1 微信登录/注册流程 - -1) 前端/中间件发现未登录 → 重定向到 `/v1/auth/wechat?redirect=...` -2) `/v1/auth/wechat` 生成微信授权 URL(回调到 `/v1/auth/login`,并透传 redirect) -3) `/v1/auth/login`: - - `code` 换取 `openid` 与 `access_token` - - 获取“稳定版 token”(stable_token) - - 拉取用户信息(失败则生成随机昵称/头像) - - `users` 表按 `open_id` 查询,不存在则创建;存在则更新用户名/头像/metas/auth_token - - 生成 JWT(claims 里只使用了 `UserID`),写 Cookie:`token` - -### 4.2 曲谱内容模型与呈现 - -曲谱(`posts`)本质是一个“商品/内容条目”,它的媒体资产在 `posts.assets` 里记录(JSON 数组),每个 asset 指向 `medias.id`。 - -- 封面图:`posts.head_images`(媒体 ID 数组)→ 后端转为 OSS 签名 URL 数组返回 -- 播放:`GET /v1/posts/:id/play` - - 若未购买:`preview=true` - - 在 `posts.assets` 中选择 `Type=="video/mp4"` 且 `asset.Metas.Short == preview` 的媒体作为播放源 - - 生成带过期时间的 OSS 签名 URL(预览与正式片过期时间不同) - -### 4.3 购买/订单/权限授予 - -核心表: - -- `orders`:订单记录(支付状态 + 支付/退款回调内容写入 `orders.meta`) -- `user_posts`:用户与曲谱的授予关系(购买、赠送) - -购买接口:`POST /v1/posts/:id/buy` - -- 先检查 `user_posts` 是否已有记录(避免重复购买) -- 创建 `orders`(状态 pending) -- 当前主路径:余额足够则走余额支付,写入 `orders.meta.cost_balance` 并投递 `BalancePayNotify` 任务,随后返回一个特殊响应(`appId: "balance"`) -- 代码中存在“余额不足时走微信 JSAPI 支付”的逻辑,但当前版本在余额不足时提前 `return`,导致后续微信支付逻辑不可达(这属于现状问题,改多租户时建议一并梳理修正)。 - -### 4.4 支付/退款回调(异步任务) - -回调入口:`POST /v1/pay/callback/:channel` - -- `TRANSACTION.SUCCESS` → 入队 `WechatPayNotify` -- `REFUND.*` → 入队 `WechatRefundNotify` - -任务处理(均运行在 River 队列里): - -- `WechatPayNotify`:校验金额、更新订单状态为 completed、扣减余额(若 cost_balance>0)、写入 `user_posts` 授权 -- `BalancePayNotify`:余额支付完成,更新订单状态 completed、扣减余额、写入 `user_posts` -- `WechatRefundNotify`:订单状态按退款状态更新;退款成功则撤销 `user_posts` - ---- - -## 5. 数据库字典(PostgreSQL) - -### 5.1 迁移与生成代码来源 - -- Goose 迁移:`backend/database/migrations/*.sql` -- Jet 表定义(由 DB 反射/生成):`backend/database/table/*.go` -- Model(CRUD + 部分自定义逻辑):`backend/app/model/*` -- 类型映射(jsonb → struct、int2 → enum):`backend/database/transform.yaml` + `backend/database/fields/*` - -### 5.2 业务表一览 - -| 表 | 作用 | -|---|---| -| `users` | 微信用户主体 + 余额 + 微信 token 信息 | -| `posts` | 曲谱/内容商品 | -| `medias` | 媒体资源(OSS 路径、hash 去重、媒体元信息) | -| `user_posts` | 用户-曲谱的授予关系(购买/赠送) | -| `orders` | 订单记录(支付/退款状态与元数据) | - -此外还有 `migrations`(Goose 使用)以及 River 队列表(`river_job` 等,属于基础设施表)。 - -### 5.3 枚举(int2/int)取值定义 - -**posts.status(`fields.PostStatus`)** - -- `0`:draft -- `1`:published - -**users.status(`fields.UserStatus`)** - -- `0`:ok -- `1`:banned -- `2`:blocked - -**orders.status(`fields.OrderStatus`)** - -- `0`:pending -- `1`:paid(当前代码主要使用 `pending/completed/refund_*`,该值可能历史遗留) -- `2`:refund_success -- `3`:refund_closed -- `4`:refund_processing -- `5`:refund_abnormal -- `6`:cancelled -- `7`:completed - -### 5.4 `users` 表 - -迁移:`backend/database/migrations/20250322103119_create_users.sql`、`20250430014015_alter_user.sql`、`20250512113213_alter_user.sql` - -字段(按迁移语义整理): - -- `id int8`:主键(序列起始被设置为 1000) -- `created_at timestamp not null default now()` -- `updated_at timestamp not null default now()` -- `deleted_at timestamp null`:软删除 -- `status int2 not null default 0`:见 `UserStatus` -- `open_id varchar(128) not null unique`:微信 OpenID(单租户假设的关键约束) -- `username varchar(128) not null` -- `avatar text null` -- `metas jsonb not null default '{}'`:用户资料(见下方 JSON 结构) -- `auth_token jsonb not null default '{}'`:微信 token(见下方 JSON 结构) -- `balance int8 not null default 0`:余额(单位:分) - -`users.metas`(`fields.UserMetas`): - -- `city/country/province`:地区 -- `head_image_url`:头像 -- `nickname`:昵称 -- `sex`:性别 -- `privilege[]`:特权列表 - -`users.auth_token`(`fields.UserAuthToken`): - -- `stable_access_token`、`stable_expires_at` -- `access_token`、`expires_at` -- `refresh_token`、`scope`、`is_snapshotuser` - -### 5.5 `medias` 表 - -迁移:`backend/database/migrations/20250321112535_create_medias.sql` - -- `id int8`:主键 -- `created_at timestamp not null default now()` -- `name varchar(255) not null default ''`:原始文件名 -- `mime_type varchar(128) not null default ''` -- `size int8 not null default 0` -- `path varchar(255) not null default ''`:OSS 对象 key(实际业务中通常为 `quyun/.`) -- `metas jsonb not null default '{}'`:媒体元信息 -- `hash varchar(64) not null default ''`:上传 md5(用于去重) - -`medias.metas`(`fields.MediaMetas`): - -- `parent_hash`:关联源文件(例如转码后的视频/封面与原视频关联) -- `short bool`:是否为“试听/预览版” -- `duration int64`:时长(秒) - -### 5.6 `posts` 表 - -迁移:`backend/database/migrations/20250322100215_create_posts.sql` - -- `id int8`:主键 -- `created_at timestamp not null default now()` -- `updated_at timestamp not null default now()` -- `deleted_at timestamp null`:软删除 -- `status int2 not null default 0`:见 `PostStatus` -- `title varchar(128) not null` -- `head_images jsonb not null default '[]'`:封面媒体 ID 数组(int64) -- `description varchar(256) not null`:简介(admin 表单里叫 introduction) -- `content text not null`:正文(微信端详情里展示在 `content`) -- `price int8 not null default 0`:单位:分 -- `discount int2 not null default 100`:折扣百分比(0~100) -- `views int8 not null default 0` -- `likes int8 not null default 0` -- `tags jsonb default '{}'`:标签(代码期望是 `[]string`) -- `assets jsonb default '{}'`:媒体资产(代码期望是 `[]MediaAsset`) - -`posts.assets`(`fields.MediaAsset[]`)元素结构: - -- `type string`:mime_type(例如 `video/mp4`) -- `media int64`:对应 `medias.id` -- `metas MediaMetas`:冗余一份媒体 metas(用于直接判断 short/duration) -- `mark *string`:预留字段(当前业务未见强依赖) - -> 注意:迁移里 `tags/assets` 默认值是 `{}`(object),但代码将其当作数组解析。真实运行依赖于写入时覆盖该字段;多租户改造时建议顺手把默认值修正为 `[]` 并清洗历史数据。 - -### 5.7 `user_posts` 表 - -迁移:`backend/database/migrations/20250322103243_create_user_posts.sql` - -- `id int8`:主键 -- `created_at timestamp not null default now()` -- `updated_at timestamp not null default now()` -- `user_id int8 not null` -- `post_id int8 not null` -- `price int8 not null`:购买/赠送时记录(赠送场景 `price=-1`) - -> 注意:无外键、无唯一约束(同一用户重复 post 的防重由代码实现);多租户/一致性增强时建议加唯一索引与外键。 - -### 5.8 `orders` 表 - -迁移:`backend/database/migrations/20250410130530_create_orders.sql` - -- `id int8`:主键 -- `created_at timestamp not null default now()` -- `updated_at timestamp not null default now()` -- `order_no varchar(64) not null`:系统订单号(当前用时间戳字符串) -- `sub_order_no varchar(64) not null default ''`:子订单号(当前等于 order_no) -- `transaction_id varchar(64) not null default ''`:微信支付交易号 -- `refund_transaction_id varchar(64) not null default ''`:微信退款单号 -- `price int8 not null default 0`:单位:分 -- `discount int2 not null default 100` -- `currency varchar(10) not null default 'CNY'` -- `payment_method varchar(50) not null default 'wechatpay'`:实际可能为 `balance` 或微信回调的 trade_type -- `post_id int8 not null` -- `user_id int8 not null` -- `status int2 not null`:见 `OrderStatus` -- `meta jsonb not null default '{}'` - -`orders.meta`(`fields.OrderMeta`): - -- `pay_notify`:微信支付回调解密内容(结构体较大) -- `refund_resp`:发起退款 API 响应 -- `refund_notify`:微信退款回调解密内容 -- `cost_balance int64`:混合支付时使用余额金额(单位:分) - ---- - -## 6. 单租户假设与“多租户化”切入点清单 - -### 6.1 当前显式/隐式单租户点 - -- DB 约束:`users.open_id UNIQUE`(同一 openid 只能存在一条用户记录) -- Admin 登录:硬编码账号/密码;JWT `UserID` 写死 `-20140202`(没有“租户管理员表”) -- OSS 路径:`providers/ali/oss_client.go` 固定把对象放到 `quyun/` 前缀;上传模块也固定 `UPLOAD_PATH = "quyun"` -- 业务表均缺少租户字段:`posts/medias/orders/user_posts/users` 都没有 `tenant_id` -- 业务查询没有任何“租户过滤”(例如 `admin/posts` 列表直接全表扫描) - -### 6.2 值得注意:JWT Claims 已预留租户字段 - -`backend/providers/jwt/jwt.go` 的 `BaseClaims` 已包含: - -- `tenant string` -- `tenant_id int64` - -但当前业务仅用到 `user_id`,并未实现“解析租户上下文 → 写入 claims → 数据隔离”。 - ---- - -## 7. 多租户改造建议(面向本项目的可落地方案) - -下面给的是“从当前代码出发”的推荐路线,优先保证:改动面可控、隔离强、后续可扩展。 - -### 7.1 明确租户边界与识别方式(建议先定这个) - -对该项目而言,最常见的租户识别方式: - -1) **按域名**(推荐):`tenantA.example.com`、`tenantB.example.com` -2) **按路径前缀**:`/t/:tenant/...`(会影响前端路由与静态文件托管) -3) **按请求头**:`X-Tenant-ID`(适合 B 端 API,但微信 H5 场景常常不便) -4) **按二维码/推广链接参数**:首次进入携带 tenant,再写入 cookie/localStorage(需要防篡改与校验) - -微信场景强依赖“回跳 redirect”,因此建议:**域名识别租户** 或 “二维码/链接识别租户 + 服务端签名 state 防篡改”。 - -### 7.2 数据库层:新增租户表 + 为业务表补 `tenant_id` - -建议新增: - -- `tenants`:`id`、`code`、`name`、`status`、`created_at`、`updated_at`、配置项(可 JSONB) -- `admin_users`(或 `tenant_users`):租户后台账号体系(账号/密码 hash/角色/tenant_id) - -为现有业务表增加: - -- `users.tenant_id` -- `posts.tenant_id` -- `medias.tenant_id` -- `orders.tenant_id` -- `user_posts.tenant_id` - -并建立必要约束/索引(至少): - -- `users`: `UNIQUE(tenant_id, open_id)`(替代当前全局唯一) -- `posts`: `(tenant_id, id)` 常用过滤索引;如有需要加 `(tenant_id, status, deleted_at)` -- `medias`: `UNIQUE(tenant_id, hash)`(上传去重在租户内进行) -- `orders`: `UNIQUE(tenant_id, order_no)`(避免多租户下时间戳订单号冲突) -- `user_posts`: `UNIQUE(tenant_id, user_id, post_id)`(彻底防重) - -### 7.3 应用层:租户上下文注入与强制过滤 - -建议新增一个最先执行的 middleware: - -- 解析租户(host/path/header) -- 写入 `ctx.Locals("tenant", tenant)`(包含 `tenant_id`) -- 后续所有 model 查询都必须带 tenant 条件 - -落点示例(按当前代码组织方式): - -- `middlewares` 新增 `TenantResolve` 并在 `Group("v1").Use(...)` 的最前面插入 -- `Auth` 中间件里查用户时改为:`WHERE tenant_id = ? AND id = ?` -- 微信登录 `GetUserByOpenIDOrCreate` 改为 tenant 维度:`WHERE tenant_id=? AND open_id=?` -- Admin 路由统一加 tenant 过滤(例如 `/admin/posts` 只看当前租户) - -### 7.4 OAuth/支付:租户与微信配置关系 - -必须提前决策:**每个租户是否独立微信 AppID/支付商户**? - -- 若每租户独立:`tenants` 里存微信配置(AppID/AppSecret/MchID/ApiV3Key/...),并按租户动态初始化 client(当前 providers 是“全局单例配置”) -- 若共享同一个公众号/商户:仍然要做数据隔离,但需要在订单号、回调处理里带上 tenant 信息(例如 `order_no` 编码 tenant 前缀,或在 `meta` 中存 tenant_id 并保证可反查) - -### 7.5 OSS:对象 Key 增加租户前缀 - -当前固定前缀 `quyun/`。多租户建议改为: - -- `tenants//quyun/.` 或 `tenants//...` - -并同步改动: - -- 预签名上传:`PreUploadCheck` 返回的 Key -- 上传完成回调写入 `medias.path` -- 后续签名下载/删除统一使用新 path - -### 7.6 队列任务:必须携带 tenant 上下文 - -目前任务参数里不包含 tenant。多租户后建议: - -- Job Args 增加 `TenantID`(或可解析的 `TenantCode`) -- Worker 执行时所有 DB 查询都加 tenant 过滤 -- OSS 操作也按 tenant 前缀 - -否则会出现:回调/任务在多租户环境下“写错库、扣错余额、发错权限”的高危问题。 - ---- - -## 8. 建议你下一步提供的信息(能让多租户设计更准确) - -为了把“多租户模式”落到你期望的形态,建议你确认并告诉我: - -1) 租户识别方式:域名 / 路径前缀 / header / 邀请链接? -2) 微信与支付配置:租户独立还是共享? -3) 租户隔离级别:仅逻辑隔离(tenant_id)还是需要“库/Schema 隔离”? -4) 你期望的后台账号体系:每租户多个管理员?是否需要角色权限? - -确认后我可以按本仓库的结构直接给出:迁移脚本 + middleware + model 查询改造点 + 前端配套改造清单。 diff --git a/docs/design/portal/GLOBAL.md b/docs/design/portal/GLOBAL.md new file mode 100644 index 0000000..4e7c7ac --- /dev/null +++ b/docs/design/portal/GLOBAL.md @@ -0,0 +1,96 @@ +# Portal 全局设计准则 (Global Design Guidelines) + +> **适用范围**: PC 端 Portal 站点 +> **核心风格**: 内容型(浅灰背景 + 白色卡片),强调信息层级与阅读体验。 + +## 1. 全局布局 (Layout & Container) + +采用经典的垂直分布布局,确保内容区域聚焦且具备良好的扩展性。 + +### 1.1 页面结构 (DOM Structure) +- **Root**: `min-h-screen flex flex-col bg-slate-50` (浅灰背景,hex: `#F8FAFC`) + - **Header**: `fixed top-0 w-full z-50` (固定顶部) + - **Main**: `flex-grow pt-16` (内容自适应填充,顶部留出 Header 高度) + - **Footer**: `mt-auto` (页脚沉底) + +### 1.2 核心容器 (Main Container) +所有页面核心内容需包裹在标准容器内,以保证视觉统一。 +- **宽度限制**: `max-w-screen-xl` (Tailwind default: 1280px) +- **对齐方式**: `mx-auto` (水平居中) +- **内边距**: `px-4 sm:px-6 lg:px-8` (响应式呼吸感) +- **示例代码**: + ```html +
+
+ +
+
+ ``` + +### 1.3 基础色彩规范 (Basic Colors) +- **背景色**: `bg-slate-50` (页面底色) +- **卡片色**: `bg-white` (内容承载) +- **边框色**: `border-slate-200` (轻微分割线) +- **文字色**: + - 主要: `text-slate-900` (标题、正文) + - 次要: `text-slate-500` (描述、辅助信息) + - 链接: `text-primary-600 hover:text-primary-700` + +--- + +## 2. 顶部导航栏 (TopNavbar) + +全局常驻入口,承载品牌认知与核心路径导航。 + +### 2.1 外观 (Appearance) +- **尺寸**: 高度 `h-16` (64px) +- **背景**: `bg-white` (纯白背景) +- **质感**: `border-b border-slate-200` 或 `shadow-sm` (轻微投影,提升层级) + +### 2.2 布局 (Flex Grid) +`flex items-center justify-between` + `Container` + +| 区域 | 元素 | 交互/样式 | +| --- | --- | --- | +| **Left** | **Logo** | 图片/SVG + 文字,点击回首页 (`/`)。高度控制在 32-40px。 | +| **Center-Left** | **Nav Links** | 首页、分类、标签等。间距 `space-x-8`。
Default: `text-slate-600 font-medium`
Hover: `text-primary-600`
Active: `text-primary-600` | +| **Center-Right** | **Global Search** | 圆角矩形输入框 `rounded-full` 或 `rounded-lg`。
`bg-slate-100 focus:bg-white focus:ring`。
宽度:默认 `w-64`,聚焦时可伸展。 | +| **Right** | **User Actions** | **未登录**:
- [登录]: Ghost Button (`text-slate-600 hover:bg-slate-50`)
- [注册]: Primary Button (`bg-primary-600 text-white`)
**已登录**:
- [通知]: Bell Icon + Badge
- [头像]: Avatar + Dropdown Menu | + +--- + +## 3. 页脚 (Footer) + +全局底部信息区,采用深色风格以稳定视觉重心。 + +### 3.1 外观 (Appearance) +- **背景**: `bg-slate-900` (深色) +- **文字**: `text-slate-400` (灰色文本,避免纯白刺眼) +- **链接**: Hover 时变亮 `hover:text-white` + +### 3.2 内容结构 +- **Upper Section (Links)**: `py-12` + - Grid 布局 (3-4 列) + - 栏目:关于我们、帮助中心、法律条款、关注我们 (Social Icons) +- **Bottom Section (Copyright)**: `border-t border-slate-800 py-6` + - 版权声明 (© 2025 Quyun) + - ICP 备案号 / 公安网备 + +--- + +## 4. 通用 UI 元素 (Common Components) + +### 4.1 内容卡片 (Content Card) +用于承载列表项、详情块等。 +- **样式**: `bg-white rounded-lg border border-slate-100 shadow-sm transition-shadow hover:shadow-md` +- **内边距**: `p-4` 或 `p-6` + +### 4.2 按钮 (Buttons) +- **Primary**: `bg-primary-600 text-white hover:bg-primary-700 rounded-lg px-4 py-2` +- **Secondary/Outline**: `border border-slate-300 text-slate-700 hover:bg-slate-50 rounded-lg px-4 py-2` +- **Ghost**: `text-slate-600 hover:bg-slate-100 rounded-lg px-4 py-2` + +### 4.3 标题 (Typography) +- **H1 (页面主标题)**: `text-2xl sm:text-3xl font-bold text-slate-900` +- **H2 (区块标题)**: `text-xl font-semibold text-slate-900 mb-4` +- **Section Header**: 标题 + 操作区 (如“查看更多”) 的组合。 diff --git a/docs/design/portal/PAGE_AUTH.md b/docs/design/portal/PAGE_AUTH.md new file mode 100644 index 0000000..d3eaccf --- /dev/null +++ b/docs/design/portal/PAGE_AUTH.md @@ -0,0 +1,62 @@ +# 页面设计: 认证流程 (Auth) + +> **路由**: `/auth/login` (合并登录与注册) +> **布局**: `LayoutAuth` (全屏居中大卡片,内部左右分栏) + +## 1. 布局结构 (Centered Split Layout) + +采用屏幕居中的大尺寸卡片布局,将品牌展示与操作表单完美融合。 + +- **外层容器**: `min-h-screen bg-slate-50 flex items-center justify-center p-4` +- **主卡片**: `w-full max-w-4xl bg-white rounded-2xl shadow-2xl overflow-hidden flex min-h-[550px]` + - **左侧 (品牌区 - Brand Area)**: `w-1/2 hidden md:flex bg-slate-900 relative p-12 flex-col justify-between text-white` + - **顶部**: Logo 展示。 + - **中部**: + - `h1`: "探索内容的无限可能" (text-4xl font-bold leading-tight) + - `p`: "专业的租户管理与内容交付平台,连接创作者与用户。" (text-slate-400 mt-4) + - **底部**: `text-xs text-slate-500`: "© 2025 Quyun. All rights reserved." + - **右侧 (表单区 - Form Area)**: `w-full md:w-1/2 p-8 sm:p-12 flex flex-col justify-center` + +--- + +## 2. 核心交互流程 (手机号 OTP 登录/注册) + +页面不区分登录和注册,统一通过手机号验证码进入。若手机号未注册,验证通过后后台自动创建账号。 + +### 2.1 第一步:输入手机号 +- **标题**: "欢迎回来" (text-2xl font-bold text-slate-900) +- **描述**: "未注册的手机号验证后将自动创建账号" (text-sm text-slate-500 mt-2 mb-8) +- **表单元素**: + - **手机号输入**: `flex` 结构。前置国家代码 `+86` (不可选或下拉),后接 Input。 + - 样式: `border-slate-200 focus:border-primary-500 rounded-lg` + - **协议勾选**: 位于按钮上方。 + - `[ ] 我已阅读并同意《用户协议》和《隐私政策》` + - **交互**: 协议名称点击后 **新窗口打开** (`target="_blank"`)。 + - **提交按钮**: "获取验证码" (Primary Button, Full Width, `h-12`)。 +- **第三方登录 (Footer)**: + - 分隔线: "其他方式登录" (text-xs text-slate-400) + - 图标组: 微信、GitHub 等圆型图标按钮。 + +### 2.2 第二步:输入验证码 (OTP Input) +*用户点击获取验证码成功后,表单区域平滑切换(或水平滑入)至验证码输入界面。* + +- **提示**: "验证码已发送至 +86 138****8888" (含返回修改图标)。 +- **验证码输入框**: + - **连体样式**: 单个 Input,但通过 CSS `letter-spacing` 或背景装饰模拟 6 位网格感,或者使用标准的 6 位连体窄间距输入框。 + - **交互**: 自动聚焦,仅限数字,长度 6 位。 +- **辅助操作**: + - **倒计时**: "59s 后重新获取" (不可点击) -> "重新获取验证码" (可点击)。 +- **确认按钮**: "登录 / 进入 QuYun"。 + +--- + +## 3. 逻辑与细节 + +- **自动注册逻辑**: 验证码校验通过后,由后端判断 `is_new_user`。 +- **跳转逻辑**: 登录/注册成功后,统一跳转回 **首页 (`/`)** 或 **来源页 (redirect_url)**。不强制跳转完善资料页。 +- **校验规则**: + - 手机号: 必须符合中国大陆 11 位手机号格式。 + - 协议: 未勾选时点击“获取验证码”,按钮执行“抖动”动画,并在下方显示红色提示“请先同意用户协议”。 +- **加载状态**: + - 发送验证码时,按钮进入 `loading` 状态,防止重复点击。 + - 登录校验时,全屏或区域遮罩 Loading。 diff --git a/docs/design/portal/PAGE_CONTENT_DETAIL.md b/docs/design/portal/PAGE_CONTENT_DETAIL.md new file mode 100644 index 0000000..134f80d --- /dev/null +++ b/docs/design/portal/PAGE_CONTENT_DETAIL.md @@ -0,0 +1,66 @@ +# 页面设计: 内容详情页 (Content Detail) + +> **路由**: `/contents/:contentId` +> **布局**: `LayoutMain` +> **结构**: 经典阅读布局 (左文右侧栏) + +## 1. 页面结构 (Structure) + +```text +[ Breadcrumb ] +---------------------------------------------------------- +| | | +| Article Body | Sidebar (Sticky) | +| (70%) | (Author / TOC / Related) | +| | (30%) | +| Comments Area | | +| | | +``` + +## 2. 详细设计 (Detailed Specs) + +### 2.1 顶部导航 (Breadcrumb) +- **位置**: 容器顶部,Main 内容之前。 +- **内容**: `首页 > 分类 > 正文标题`。 +- **样式**: `text-sm text-slate-500 py-4`。 + +### 2.2 左侧正文区 (Article Body - Left Column) +- **容器**: `bg-white rounded-lg shadow-sm p-6 sm:p-10 mb-6`。 +- **头部 (Header)**: + - **标题**: H1, `text-3xl font-bold text-slate-900 mb-4`。 + - **元数据**: 作者头像、名称、发布时间、阅读数、Tag 标签。 + - 样式: `flex items-center text-sm text-slate-500 space-x-4`。 +- **正文 (Content)**: + - 富文本渲染区域。 + - 排版规范: `prose prose-slate max-w-none` (使用 Tailwind Typography 插件)。 + - 图片: `rounded-lg my-4 max-w-full`。 +- **底部互动 (Footer Actions)**: + - **点赞/收藏/分享**: 居中或居左的图标按钮组。 + - **上一篇/下一篇**: 简单的文字链接导航。 + +### 2.3 评论区 (Comments Section) +- **位置**: 正文容器下方,独立卡片或同一容器底部。 +- **结构**: + - 输入框: 文本域 + 表情/图片按钮 + "发布"按钮。 + - 列表: 头像 + 名称 + 内容 + 时间 + 回复按钮。 + - 嵌套: 二级回复缩进显示。 + +### 2.4 右侧侧边栏 (Sidebar - Right Column) +- **特性**: `sticky top-20` (随页面滚动固定)。 +- **模块**: + 1. **作者/租户卡片 (Author Profile)**: + - 大头像、名称、简介。 + - 数据: 文章数、粉丝数。 + - 动作: [关注] [私信] 大按钮。 + 2. **目录 (Table of Contents)**: + - 自动生成 H2/H3 锚点链接。 + - 当前阅读章节高亮 (ScrollSpy)。 + - 样式: 左侧竖线指示条。 + 3. **相关推荐 (Related Contents)**: + - 缩略图 + 标题列表。 + +## 3. 移动端适配 (Mobile) +- **侧边栏**: 隐藏。 +- **作者信息**: 移动到文章头部或底部展示。 +- **目录**: 变为浮动按钮 (FAB) 或 放在文章头部折叠面板中。 +- **底部栏**: 固定底部操作栏 (评论输入框、点赞、收藏、分享),类似于原生 App 体验。 diff --git a/docs/design/portal/PAGE_HOME.md b/docs/design/portal/PAGE_HOME.md new file mode 100644 index 0000000..9ba4847 --- /dev/null +++ b/docs/design/portal/PAGE_HOME.md @@ -0,0 +1,60 @@ +# 页面设计: 首页 (Home) + +> **路由**: `/` +> **布局**: `LayoutMain` (Header + Footer) +> **结构**: 混合布局 (Banner + 左右分栏) + +## 1. 页面结构 (Structure) + +首页采用 **"Hero + 左右分栏"** 的经典内容型布局,强调信息密度与浏览效率。 + +```text +[ Hero Banner / Carousel (Full Width or Boxed) ] +---------------------------------------------------------- +[ Filter/Tags Bar ] +---------------------------------------------------------- +| | | +| Main Feed (70%) | Sidebar (30%) | +| (Content List) | (Trending / Recommends) | +| | | +``` + +## 2. 详细设计 (Detailed Specs) + +### 2.1 Hero 区域 (Hero Section) +位于导航栏下方,用于展示平台级置顶、活动或推荐。 +- **容器**: `w-full bg-white border-b border-slate-100 mb-6` (可选) +- **内容**: + - **轮播图/大卡片**: 高度约 `300px - 400px`。 + - **样式**: 圆角矩形,阴影,图文叠加或左右结构。 + +### 2.2 筛选栏 (Filter Bar) +- **位置**: 内容流上方。 +- **元素**: + - **Tabs**: "推荐", "最新", "热门"。 + - **Tag List**: 常用分类标签(如:技术、生活、设计...),支持横向滚动。 +- **样式**: `flex items-center space-x-4 py-4 border-b border-slate-200 mb-4`。 + +### 2.3 左侧内容流 (Main Feed - Left Column) +- **宽度**: `col-span-12 lg:col-span-8 xl:col-span-9` (响应式调整) +- **列表样式**: + - **卡片列表 (List View)**: 左图右文 或 上文下图。适合资讯、博客。 + - *Image*: 缩略图 `w-48 h-32 object-cover rounded`。 + - *Info*: 标题 (H3), 摘要 (Line-clamp-2), Meta (作者/时间/热度)。 + - **网格列表 (Grid View)**: 适合商品、图片为主的内容。 + - `grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6`。 +- **加载状态**: 底部 "加载更多" 按钮 或 无限滚动 (Infinite Scroll)。 + +### 2.4 右侧侧边栏 (Sidebar - Right Column) +- **宽度**: `hidden lg:block lg:col-span-4 xl:col-span-3` (移动端隐藏) +- **组件模块**: + 1. **公告栏 (Announcements)**: 平台公告,纯文本或带图标。 + 2. **热门榜单 (Trending)**: Top 5-10 内容列表,带数字排行 (1, 2, 3...)。 + 3. **推荐租户/作者 (Recommended Creators)**: 头像 + 名称 + 关注按钮列表。 + 4. **广告位 (Ads/Promos)**: 图片 Banner。 +- **样式**: 每个模块为一个 `bg-white rounded-lg p-4 shadow-sm mb-6` 卡片。 + +## 3. 交互与状态 (Interactions) +- **Hover**: 列表卡片 Hover 时出现轻微上浮或阴影加深。 +- **点击**: 点击卡片任意区域跳转至 `内容详情页`。 +- **关注**: 在推荐租户模块直接点击 "关注",需判断登录状态。 diff --git a/docs/design/portal/PAGE_MISC.md b/docs/design/portal/PAGE_MISC.md new file mode 100644 index 0000000..165db40 --- /dev/null +++ b/docs/design/portal/PAGE_MISC.md @@ -0,0 +1,38 @@ +# 页面设计: 帮助与错误页 (Misc) + +> **路由**: +> - 帮助中心: `/help` +> - 条款: `/terms`, `/privacy` +> - 错误: `/404`, `/500` +> +> **布局**: `LayoutMain` (帮助/条款) / `LayoutSimple` (错误页) + +## 1. 帮助中心 (Help Center) + +### 1.1 首页 (/help) +- **搜索大图 (Hero)**: 背景图 + 大搜索框 "遇到什么问题?"。 +- **常见问题 (FAQ)**: Grid 布局,每项为 "Q: 问题... A: 简答..."。 +- **分类导航**: 卡片入口 (如 "账号问题", "支付问题", "内容发布")。 +- **联系客服**: 底部 Banner,展示客服电话或在线咨询按钮。 + +### 1.2 帮助详情页 +- 左侧分类导航树。 +- 右侧富文本文章。 +- 底部反馈: "此文是否有帮助? [是] [否]"。 + +## 2. 条款页面 (Legal) +- **布局**: 居中单栏,适合阅读长文。 +- **样式**: `prose prose-slate mx-auto max-w-3xl py-10`。 +- **导航**: 简单的 Tab 或 侧边 Anchor 导航 (条款、隐私、免责)。 + +## 3. 错误页面 (Error Pages) + +### 3.1 404 Not Found +- **视觉**: 插画 (如迷路的人、空的盒子)。 +- **文案**: "页面走丢了 (Page Not Found)"。 +- **操作**: [返回首页] [返回上一页]。 + +### 3.2 500 Server Error +- **视觉**: 插画 (如正在维修的机器人)。 +- **文案**: "服务器开小差了,请稍后再试"。 +- **操作**: [刷新页面] [联系客服]。 diff --git a/docs/design/portal/PAGE_ORDER.md b/docs/design/portal/PAGE_ORDER.md new file mode 100644 index 0000000..6468e20 --- /dev/null +++ b/docs/design/portal/PAGE_ORDER.md @@ -0,0 +1,39 @@ +# 页面设计: 订单流程 (Order Flow) + +> **路由**: +> - 确认订单: `/checkout` +> - 支付: `/payment` +> - 详情: `/me/orders/:id` + +## 1. 确认订单页 (Checkout) +- **步骤条**: 1.确认订单 -> 2.支付 -> 3.完成。 +- **收货地址 (如需)**: + - 卡片网格展示已有地址。 + - [+ 新增地址] 虚线框按钮。 +- **商品清单**: + - 列表: 图片 | 标题 | 单价 | 数量 | 小计。 +- **优惠/备注**: + - 优惠券选择器。 + - 买家留言输入框。 +- **底部结算栏 (Sticky Bottom)**: + - 只有 "合计金额"。 + - [提交订单] 大按钮。 + +## 2. 收银台/支付页 (Cashier) +- **倒计时**: "请在 15:00 内完成支付,超时将取消"。 +- **订单金额**: 大字体显示。 +- **支付方式**: + - Radio List: 微信支付、支付宝、余额支付。 + - 选中会有高亮边框。 +- **扫码区**: (PC端) 展示二维码,轮询支付状态。 + +## 3. 订单详情页 (Order Detail) +- **状态栏**: + - 顶部大卡片。 + - 当前状态 (如 "待发货") + 进度条 (下单 -> 付款 -> 发货 -> 完成)。 + - 对应操作按钮: [申请退款] [确认收货] [去评价]。 +- **信息模块**: + - **收货信息**: 人名、电话、地址。 + - **订单信息**: 订单号、创建时间、支付时间。 + - **商品列表**: 复用清单组件。 + - **费用明细**: 商品总价 - 优惠 + 运费 = 实付。 diff --git a/docs/design/portal/PAGE_SEARCH.md b/docs/design/portal/PAGE_SEARCH.md new file mode 100644 index 0000000..09e3199 --- /dev/null +++ b/docs/design/portal/PAGE_SEARCH.md @@ -0,0 +1,48 @@ +# 页面设计: 搜索结果 (Search Result) + +> **路由**: `/search?keyword=abc` +> **布局**: `LayoutMain` + +## 1. 页面结构 (Structure) + +```text +[ Search Bar (Persistent in Header) ] +------------------------------------------------ +[ Filter Tabs (All / Article / User / Tag) ] +------------------------------------------------ +| | | +| Result List | Related / Hot | +| (70%) | (30%) | +| | | +``` + +## 2. 详细设计 (Detailed Specs) + +### 2.1 顶部筛选区 (Filter Header) +- **搜索反馈**: "找到约 **120** 个与 'AI' 相关的结果"。 +- **Tab 分类**: + - **综合**: 混合展示内容、用户、标签。 + - **文章/内容**: 纯内容列表。 + - **用户/租户**: 匹配的创作者。 + - **标签**: 匹配的 Tags。 +- **高级筛选 (Dropdown)**: + - **时间**: 不限 / 一天内 / 一周内 / 一月内。 + - **排序**: 相关度 (默认) / 最新发布 / 最多浏览。 + +### 2.2 结果列表区 (Result List - Left) +- **内容卡片**: + - 标题中匹配的 **关键字高亮** (如红色或加粗)。 + - 展示摘要、封面图(如有)、元数据。 +- **用户卡片** (在综合搜索中穿插或独立 Tab): + - 横向布局:头像 | 名称 + 简介 | [关注] 按钮。 +- **无结果状态 (Empty State)**: + - 插画提示 "未找到相关内容"。 + - 建议: "尝试简化关键词" 或 "看看热门推荐"。 + +### 2.3 右侧辅助区 (Right Sidebar) +- **热门搜索 (Hot Search)**: 词云或列表 (1. DeepSeek, 2. AI 绘画...)。 +- **搜索历史 (Search History)**: (仅登录且开启记录时显示) 显示最近 5 条,支持删除。 + +## 3. 交互 +- **输入框联动**: 页面顶部的全局搜索框内容应与 URL `keyword` 参数保持同步。 +- **Enter 键**: 触发搜索跳转。 diff --git a/docs/design/portal/PAGE_TENANT_HOME.md b/docs/design/portal/PAGE_TENANT_HOME.md new file mode 100644 index 0000000..c3ce303 --- /dev/null +++ b/docs/design/portal/PAGE_TENANT_HOME.md @@ -0,0 +1,56 @@ +# 页面设计: 租户/个人主页 (Tenant Profile) + +> **路由**: `/tenants/:tenantId` +> **布局**: `LayoutMain` +> **结构**: 封面式头部 + 内容列表 + +## 1. 页面结构 (Structure) + +类似社交媒体的个人主页设计,强调品牌形象展示。 + +```text +[ Cover Image (Full Width/Boxed) ] +[ Profile Header (Info & Stats) ] +---------------------------------------------------------- +| Tabs (Home/All/Series) | Search in Tenant | +---------------------------------------------------------- +[ Content Grid / List ] +``` + +## 2. 详细设计 (Detailed Specs) + +### 2.1 头部区域 (Profile Header) +- **封面图 (Cover)**: + - 高度 `h-48 sm:h-64`。 + - `object-cover w-full rounded-t-lg` (如果在大容器内) 或 `w-full` (全宽)。 +- **信息栏 (Info Bar)**: + - **布局**: 相对定位,头像部分重叠在封面图上。 + - **头像**: 大尺寸 `w-24 h-24 sm:w-32 sm:h-32 rounded-full border-4 border-white shadow-md -mt-12 ml-6`。 + - **文本**: 名称 (H1)、认证标识、一句话简介。 + - **数据**: 关注者数量、内容数量、获赞数。 + - **操作**: 右侧 [关注] [私信] 按钮。 + +### 2.2 导航与筛选 (Navigation) +- **位置**: 信息栏下方。 +- **Tabs**: + - "主页" (精选/置顶) + - "全部内容" + - "专栏/系列" + - "简介/关于" +- **样式**: `border-b border-slate-200`,选中项底部高亮 `border-primary-600`。 + +### 2.3 内容展示区 (Content Area) +- **布局**: + - **默认**: 网格布局 `grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6`。 + - **列表模式**: 可选切换。 +- **卡片样式**: + - 复用首页的卡片组件,但可能移除作者信息(因为已经在作者主页了),强调封面和标题。 + +### 2.4 "关于" Tab (About Tab) +- 如果切换到 "简介/关于" Tab: + - 展示详细的企业/个人介绍。 + - 联系方式、地图、资质证书等。 + +## 3. 特殊状态 +- **未发布内容**: 展示空状态插画 "作者很懒,还没有发布内容"。 +- **私密/封禁**: 展示 403/404 提示。 diff --git a/docs/design/portal/PAGE_USER_CENTER.md b/docs/design/portal/PAGE_USER_CENTER.md new file mode 100644 index 0000000..35198af --- /dev/null +++ b/docs/design/portal/PAGE_USER_CENTER.md @@ -0,0 +1,52 @@ +# 页面设计: 个人中心 (User Center) + +> **路由**: `/me/*` +> **布局**: `LayoutMain` +> **结构**: 侧边栏导航布局 (Sidebar Layout) + +## 1. 布局结构 (Structure) + +```text +[ Container ] +| [ Sidebar Nav ] | [ Main Content Area ] | +| (250px Fixed) | (Flex Grow, White Card) | +| | | +| - Dashboard | H2 Page Title | +| - Orders | ------------------------------- | +| - Wallet | [ Content Form / List ] | +| - Settings | | +``` + +## 2. 侧边栏导航 (Sidebar Nav) +- **用户信息**: 顶部展示小头像 + 昵称。 +- **菜单组**: + - **交易中心**: 我的订单、我的钱包、售后记录。 + - **内容管理**: 我的收藏、浏览历史。 + - **账号设置**: 个人资料、账号安全(密码/绑定)、收货地址。 + - **租户入口**: (如果是租户管理员) [切换到创作者中心] 按钮。 +- **样式**: 选中项背景高亮 `bg-primary-50 text-primary-600 border-r-2 border-primary-600`。 + +## 3. 核心子页面设计 + +### 3.1 个人资料 (Profile) +- **表单**: + - 头像上传 (点击更换)。 + - 昵称、简介 (Textarea)。 + - 性别、生日 (Datepicker)。 +- **操作**: [保存修改] 按钮。 + +### 3.2 账号安全 (Security) +- **列表项**: + - **登录密码**: 已设置 [修改]。 + - **手机绑定**: 138****8888 [更换]。 + - **邮箱绑定**: 未绑定 [绑定]。 + - **注销账号**: 红色链接,需二次确认。 + +### 3.3 我的收藏 (Favorites) +- **布局**: 网格或列表展示收藏的内容。 +- **操作**: [取消收藏] 按钮,支持批量管理。 + +### 3.4 浏览历史 (History) +- **时间轴**: 按日期分组 (今天 / 昨天 / 更早)。 +- **列表**: 简单的标题 + 时间列表。 +- **操作**: [清空历史]。 diff --git a/frontend/portal/pages.md b/docs/portal.md similarity index 100% rename from frontend/portal/pages.md rename to docs/portal.md