commit 1da4d8cf1f2ee0a1b8e4873c8c16845274067b00
Author: rain <982766701@qq.com>
Date: Mon Jul 13 00:23:04 2026 +0800
家谱APP首页样式设计完成,落地80%
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..82682d7
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,115 @@
+# AGENTS.md
+Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
+
+Tradeoff: These guidelines bias toward caution over speed. For trivial tasks, use judgment.
+
+1. Think Before Coding
+Don't assume. Don't hide confusion. Surface tradeoffs.
+
+Before implementing:
+
+State your assumptions explicitly. If uncertain, ask.
+If multiple interpretations exist, present them instead of picking silently.
+If a simpler approach exists, say so. Push back when warranted.
+If something is unclear, stop. Name what is confusing. Ask.
+
+2. Simplicity First
+Minimum code that solves the problem. Nothing speculative.
+
+No features beyond what was asked.
+No abstractions for single-use code.
+No "flexibility" or "configurability" that was not requested.
+No error handling for impossible scenarios.
+If you write 200 lines and it could be 50, rewrite it.
+Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
+
+3. Surgical Changes
+Touch only what you must. Clean up only your own mess.
+
+When editing existing code:
+
+Do not "improve" adjacent code, comments, or formatting.
+Do not refactor things that are not broken.
+Match existing style, even if you would do it differently.
+If you notice unrelated dead code, mention it instead of deleting it.
+
+When your changes create orphans:
+
+Remove imports, variables, functions, files, docs, and references that YOUR changes made unused.
+Do not remove pre-existing dead code unless asked.
+The test: Every changed line should trace directly to the user's request.
+
+4. Contract Discipline
+When a new contract lands, make it the only contract in the same change.
+
+A contract means any source-of-truth rule that code depends on: data shape, API boundary, schema, field name, config key, prompt format, file layout, runtime entrypoint, ownership boundary, or workflow.
+
+When introducing or changing a contract, do all three in the same round:
+
+Specify the single owner.
+
+Identify the one module, function, schema, document, or service that owns the contract.
+Do not leave the same rule duplicated across frontend, backend, scripts, prompts, docs, or tests.
+If multiple places need the value, they should consume it from the owner rather than redefine it.
+
+Delete old fields and old entrypoints.
+
+Remove obsolete fields, fallback reads, compatibility branches, legacy routes, stale config keys, old scripts, and old docs.
+Do not keep the old path "just in case" unless the user explicitly asks for a compatibility period.
+If compatibility is required, name it as temporary, define the removal condition, and keep it narrow.
+
+Tighten validators and runtime together.
+
+Update schemas, validators, tests, fixtures, seed data, docs, and runtime code in the same change.
+Make invalid old inputs fail early and loudly.
+Do not allow runtime behavior to accept shapes that validators reject, or validators to allow shapes that runtime no longer supports.
+The rule: New owner, old path removed, validator and runtime tightened. If one is missing, the contract migration is not complete.
+
+5. Goal-Driven Execution
+Define success criteria. Loop until verified.
+
+Transform tasks into verifiable goals:
+
+"Add validation" -> "Write tests for invalid inputs, then make them pass."
+"Fix the bug" -> "Write a test that reproduces it, then make it pass."
+"Refactor X" -> "Ensure tests pass before and after."
+"Change a contract" -> "Identify owner, remove old paths, tighten validators and runtime, then verify old inputs fail."
+
+For multi-step tasks, state a brief plan:
+
+1. [Step] -> verify: [check]
+2. [Step] -> verify: [check]
+3. [Step] -> verify: [check]
+
+Strong success criteria let you loop independently. Weak criteria like "make it work" require clarification.
+
+6. Verification Before Closure
+A change is not finished until the relevant behavior is checked.
+
+Before reporting completion:
+
+Run the smallest relevant test, script, typecheck, build, or manual verification.
+Prefer focused verification over broad, noisy suites when the task is narrow.
+If verification cannot be run, say exactly why.
+If verification is only by inspection, say that clearly.
+If the task changed a contract, verify both the new valid path and the old invalid path.
+Do not claim success from intent. Claim success from evidence.
+
+7. Reporting Results
+Be concise, concrete, and honest.
+
+When summarizing work:
+
+Say what changed.
+Say how it was verified.
+Mention any remaining risk or skipped verification.
+Do not bury important caveats in vague language.
+Do not over-explain routine edits.
+
+Good final answer shape:
+
+Changed: [short summary]
+Verified: [command/check]
+Notes: [only if needed]
+
+These guidelines are working if: fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, fewer hidden compatibility paths, clearer ownership of contracts, and clarifying questions happen before implementation rather than after mistakes.
diff --git a/APP.openapi.yaml b/APP.openapi.yaml
new file mode 100644
index 0000000..7a55ab6
--- /dev/null
+++ b/APP.openapi.yaml
@@ -0,0 +1,5287 @@
+openapi: 3.0.1
+info:
+ title: APP
+ description: |
+ 家谱业务 APP/PC 用户侧接口文档,用于 Apifox 导入。
+
+ 导入方式:Apifox -> 导入 API 数据 -> OpenAPI/Swagger -> 选择本文件。
+
+ 约定:
+ - APP 路径前缀为 `/genealogy/app`
+ - PC/H5 路径前缀为 `/genealogy/pc`
+ - 后台管理接口不包含在本文件内
+ - 登录后接口需要携带 token,且请求 Header 需携带 `clientid`
+ version: 1.0.0
+tags:
+ - name: 验证中心
+ - name: 认证登录
+ - name: 文件上传
+ - name: 行政区划
+ - name: 家谱
+ - name: 家谱成员
+ - name: 字辈谱
+ - name: 世系人物
+ - name: 家族圈
+ - name: 内容文章
+ - name: 相册
+ - name: 祭祀
+ - name: 族务记录
+ - name: 消息通知
+ - name: 意见反馈
+ - name: VIP
+paths:
+ /captcha/require:
+ get:
+ summary: 查询当前场景是否需要验证
+ deprecated: false
+ description: 用于登录、注册、换绑手机等场景。按租户、客户端和场景编码匹配后台验证策略。
+ tags:
+ - 验证中心
+ parameters:
+ - name: tenantId
+ in: query
+ description: 租户ID,用于区分不同租户的数据和验证策略
+ required: false
+ example: '000000'
+ schema:
+ type: string
+ - name: clientId
+ in: query
+ description: 客户端ID,对应后台客户端管理中的APP/PC/小程序等终端
+ required: false
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ - name: sceneCode
+ in: query
+ description: 验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER
+ required: true
+ example: APP_LOGIN
+ schema:
+ type: string
+ - name: subject
+ in: query
+ description: 验证主体,通常为手机号、用户ID或登录账号
+ required: false
+ example: '13800000000'
+ schema:
+ type: string
+ - name: clientid
+ in: header
+ description: ''
+ required: false
+ example: ced7e5f0498645c6ec642dcf450b036f
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VerificationRequireResult'
+ description: 验证需求查询结果
+ security:
+ - SaToken: []
+ /captcha/challenge:
+ post:
+ summary: 生成验证挑战
+ deprecated: false
+ description: 统一生成验证码。策略为天爱时返回行为验证数据;策略为系统图形时返回 uuid 和 img。
+ tags:
+ - 验证中心
+ parameters:
+ - name: clientid
+ in: header
+ description: 客户端ID
+ example: ced7e5f0498645c6ec642dcf450b036f
+ schema:
+ type: string
+ default: ced7e5f0498645c6ec642dcf450b036f
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VerificationChallengeBody'
+ example:
+ tenantId: '000000'
+ clientId: 428a8310cd442757ae699df5d894f051
+ sceneCode: APP_LOGIN
+ subject: '13800000000'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VerificationChallengeResult'
+ description: 验证挑战生成结果
+ security:
+ - SaToken: []
+ /captcha/verify:
+ post:
+ summary: 校验验证结果并换取 validToken
+ deprecated: false
+ description: 验证通过后返回 validToken,登录、注册、发短信等业务接口将 validToken 随请求体一起提交。
+ tags:
+ - 验证中心
+ parameters:
+ - name: clientid
+ in: header
+ description: 客户端ID
+ example: ced7e5f0498645c6ec642dcf450b036f
+ schema:
+ type: string
+ default: ced7e5f0498645c6ec642dcf450b036f
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VerificationCheckBody'
+ examples:
+ tianai_behavior:
+ value:
+ tenantId: '000000'
+ clientId: 428a8310cd442757ae699df5d894f051
+ sceneCode: APP_LOGIN
+ subject: '13800000000'
+ challengeId: CAPTCHA_CHALLENGE_ID
+ providerCode: tianai
+ captchaType: SLIDER
+ payload:
+ id: tianai-captcha-id
+ data:
+ bgImageWidth: 340
+ bgImageHeight: 180
+ startSlidingTime: 1720000000000
+ endSlidingTime: 1720000001500
+ trackList: []
+ summary: 天爱滑块/旋转/滑动还原/文字点选验证
+ system_image:
+ value:
+ tenantId: '000000'
+ clientId: 428a8310cd442757ae699df5d894f051
+ sceneCode: APP_LOGIN
+ subject: '13800000000'
+ challengeId: CAPTCHA_CHALLENGE_ID
+ providerCode: ruoyi_image
+ captchaType: math
+ payload:
+ uuid: captcha-uuid
+ code: '6'
+ summary: 系统图形验证码
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VerificationCheckResult'
+ description: 验证校验结果
+ security:
+ - SaToken: []
+ /auth/code:
+ get:
+ summary: 兼容旧系统图形验证码
+ deprecated: false
+ description: >-
+ 老版后台登录图形验证码入口。新的 APP/PC 业务优先使用 /captcha/require + /captcha/challenge +
+ /captcha/verify。
+ tags:
+ - 验证中心
+ parameters:
+ - name: clientid
+ in: header
+ description: 客户端ID
+ example: ced7e5f0498645c6ec642dcf450b036f
+ schema:
+ type: string
+ default: ced7e5f0498645c6ec642dcf450b036f
+ responses:
+ '200':
+ $ref: '#/components/responses/LegacyCaptchaResult'
+ description: 兼容旧图形验证码结果
+ security:
+ - SaToken: []
+ /genealogy/app/auth/register:
+ post:
+ summary: APP 用户注册
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PasswordRegisterBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/LoginResult'
+ description: 登录结果
+ security:
+ - SaToken: []
+ /genealogy/app/auth/login:
+ post:
+ summary: APP 密码登录
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PasswordLoginBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/LoginResult'
+ description: 登录结果
+ security:
+ - SaToken: []
+ /genealogy/app/auth/login/sms:
+ post:
+ summary: APP 短信登录
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SmsLoginBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/LoginResult'
+ description: 登录结果
+ security:
+ - SaToken: []
+ /genealogy/app/auth/sms/code:
+ post:
+ summary: APP 发送短信验证码
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SmsCodeBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/auth/profile:
+ get:
+ summary: APP 当前用户资料
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ put:
+ summary: APP 修改用户资料
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ProfileUpdateBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/auth/password:
+ put:
+ summary: APP 修改密码
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PasswordChangeBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/auth/password/reset:
+ put:
+ summary: APP 找回密码
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PasswordResetBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/auth/phone:
+ put:
+ summary: APP 换绑手机号
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PhoneChangeBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/auth/account/deactivate:
+ post:
+ summary: APP 注销账号
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/AccountDeactivateBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/auth/logout:
+ delete:
+ summary: APP 退出登录
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/pc/auth/register:
+ post:
+ summary: PC 用户注册
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PasswordRegisterBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/LoginResult'
+ description: 登录结果
+ security:
+ - SaToken: []
+ /genealogy/pc/auth/login:
+ post:
+ summary: PC 密码登录
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PasswordLoginBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/LoginResult'
+ description: 登录结果
+ security:
+ - SaToken: []
+ /genealogy/pc/auth/login/sms:
+ post:
+ summary: PC 短信登录
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SmsLoginBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/LoginResult'
+ description: 登录结果
+ security:
+ - SaToken: []
+ /genealogy/pc/auth/sms/code:
+ post:
+ summary: PC 发送短信验证码
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SmsCodeBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/pc/auth/profile:
+ get:
+ summary: PC 当前用户资料
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ put:
+ summary: PC 修改用户资料
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ProfileUpdateBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/pc/auth/password:
+ put:
+ summary: PC 修改密码
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PasswordChangeBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/pc/auth/password/reset:
+ put:
+ summary: PC 找回密码
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PasswordResetBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/pc/auth/phone:
+ put:
+ summary: PC 换绑手机号
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PhoneChangeBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/pc/auth/account/deactivate:
+ post:
+ summary: PC 注销账号
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/AccountDeactivateBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/pc/auth/logout:
+ delete:
+ summary: PC 退出登录
+ deprecated: false
+ description: ''
+ tags:
+ - 认证登录
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/files/upload:
+ post:
+ summary: APP 单文件上传
+ deprecated: false
+ description: ''
+ tags:
+ - 文件上传
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ file:
+ type: string
+ format: binary
+ example: ''
+ required:
+ - file
+ example:
+ file: ''
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/FileUploadResult'
+ description: 文件上传结果
+ security:
+ - SaToken: []
+ /genealogy/app/files/resumable/init:
+ post:
+ summary: APP 分片上传初始化
+ deprecated: false
+ description: ''
+ tags:
+ - 文件上传
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ResumableInitBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/files/resumable/chunk:
+ post:
+ summary: APP 上传分片
+ deprecated: false
+ description: ''
+ tags:
+ - 文件上传
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ uploadId:
+ type: string
+ example: ''
+ chunkIndex:
+ type: integer
+ example: 0
+ chunkMd5:
+ type: string
+ example: d41d8cd98f00b204e9800998ecf8427e
+ file:
+ type: string
+ format: binary
+ example: ''
+ required:
+ - uploadId
+ - chunkIndex
+ - chunkMd5
+ - file
+ example:
+ uploadId: ''
+ chunkIndex: 0
+ chunkMd5: d41d8cd98f00b204e9800998ecf8427e
+ file: ''
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/files/resumable/complete:
+ post:
+ summary: APP 完成分片上传
+ deprecated: false
+ description: ''
+ tags:
+ - 文件上传
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ResumableCompleteBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/FileUploadResult'
+ description: 文件上传结果
+ security:
+ - SaToken: []
+ /genealogy/app/files/reference:
+ post:
+ summary: APP 绑定文件业务引用
+ deprecated: false
+ description: ''
+ tags:
+ - 文件上传
+ parameters:
+ - name: clientid
+ in: header
+ description: 客户端ID,请求Header携带,用于区分APP/PC/小程序等终端
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FileReferenceBody'
+ example:
+ bizType: family_feed
+ bizName: family feed image
+ bizTable: gen_family_feed
+ bizId: 900013001
+ bizField: media_oss_ids
+ ossId: '2060000000000000000'
+ usageScene: feed_image
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ delete:
+ summary: APP 释放文件业务引用
+ deprecated: false
+ description: ''
+ tags:
+ - 文件上传
+ parameters:
+ - name: bizTable
+ in: query
+ description: 业务表名,用于文件引用绑定
+ required: true
+ example: gen_family_feed
+ schema:
+ type: string
+ - name: bizId
+ in: query
+ description: 业务数据ID,用于文件引用绑定
+ required: true
+ example: 900013001
+ schema:
+ type: integer
+ format: int64
+ - name: bizField
+ in: query
+ description: 业务字段名,例如avatar_oss_id、cover_oss_id
+ required: true
+ example: media_ids
+ schema:
+ type: string
+ - name: clientid
+ in: header
+ description: 客户端ID,请求Header携带,用于区分APP/PC/小程序等终端
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/pc/files/upload:
+ post:
+ summary: PC 单文件上传
+ deprecated: false
+ description: ''
+ tags:
+ - 文件上传
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ file:
+ type: string
+ format: binary
+ example: ''
+ required:
+ - file
+ example:
+ file: ''
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/FileUploadResult'
+ description: 文件上传结果
+ security:
+ - SaToken: []
+ /genealogy/pc/files/resumable/init:
+ post:
+ summary: PC 分片上传初始化
+ deprecated: false
+ description: ''
+ tags:
+ - 文件上传
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ResumableInitBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/pc/files/resumable/chunk:
+ post:
+ summary: PC 上传分片
+ deprecated: false
+ description: ''
+ tags:
+ - 文件上传
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ uploadId:
+ type: string
+ example: ''
+ chunkIndex:
+ type: integer
+ example: 0
+ chunkMd5:
+ type: string
+ example: d41d8cd98f00b204e9800998ecf8427e
+ file:
+ type: string
+ format: binary
+ example: ''
+ required:
+ - uploadId
+ - chunkIndex
+ - chunkMd5
+ - file
+ example:
+ uploadId: ''
+ chunkIndex: 0
+ chunkMd5: d41d8cd98f00b204e9800998ecf8427e
+ file: ''
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/pc/files/resumable/complete:
+ post:
+ summary: PC 完成分片上传
+ deprecated: false
+ description: ''
+ tags:
+ - 文件上传
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ResumableCompleteBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/FileUploadResult'
+ description: 文件上传结果
+ security:
+ - SaToken: []
+ /genealogy/region/children:
+ get:
+ summary: 查询下级行政区划
+ deprecated: false
+ description: ''
+ tags:
+ - 行政区划
+ parameters:
+ - name: parentCode
+ in: query
+ description: ''
+ required: false
+ example: '51'
+ schema:
+ type: string
+ - name: clientid
+ in: header
+ description: 客户端ID
+ example: ced7e5f0498645c6ec642dcf450b036f
+ schema:
+ type: string
+ default: ced7e5f0498645c6ec642dcf450b036f
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/region/path/{regionCode}:
+ get:
+ summary: 查询行政区划路径
+ deprecated: false
+ description: ''
+ tags:
+ - 行政区划
+ parameters:
+ - name: regionCode
+ in: path
+ description: ''
+ required: true
+ example: '510000'
+ schema:
+ type: string
+ - name: clientid
+ in: header
+ description: 客户端ID
+ example: ced7e5f0498645c6ec642dcf450b036f
+ schema:
+ type: string
+ default: ced7e5f0498645c6ec642dcf450b036f
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/region/search:
+ get:
+ summary: 搜索行政区划
+ deprecated: false
+ description: ''
+ tags:
+ - 行政区划
+ parameters:
+ - name: keyword
+ in: query
+ description: ''
+ required: true
+ example: 北京
+ schema:
+ type: string
+ - name: level
+ in: query
+ description: ''
+ required: false
+ example: 3
+ schema:
+ type: integer
+ - name: limit
+ in: query
+ description: ''
+ required: false
+ example: 20
+ schema:
+ type: integer
+ - name: clientid
+ in: header
+ description: 客户端ID
+ example: ced7e5f0498645c6ec642dcf450b036f
+ schema:
+ type: string
+ default: ced7e5f0498645c6ec642dcf450b036f
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/region/{regionCode}:
+ get:
+ summary: 查询行政区划详情
+ deprecated: false
+ description: ''
+ tags:
+ - 行政区划
+ parameters:
+ - name: regionCode
+ in: path
+ description: ''
+ required: true
+ example: '510000'
+ schema:
+ type: string
+ - name: clientid
+ in: header
+ description: 客户端ID
+ example: ced7e5f0498645c6ec642dcf450b036f
+ schema:
+ type: string
+ default: ced7e5f0498645c6ec642dcf450b036f
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies:
+ post:
+ summary: APP 创建家谱
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/mine:
+ get:
+ summary: APP 我的家谱
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/public:
+ get:
+ summary: APP 公开家谱
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/options:
+ get:
+ summary: APP 家谱下拉选项
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}:
+ get:
+ summary: APP 家谱详情
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ put:
+ summary: APP 修改家谱
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/overview:
+ get:
+ summary: APP 家谱概览
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/join-applies:
+ post:
+ summary: APP 申请加入家谱
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/join-applies/mine:
+ get:
+ summary: APP 我的加入申请
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/join-applies/pending:
+ get:
+ summary: APP 待审核加入申请
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/join-applies/{applyId}/audit:
+ put:
+ summary: APP 审核加入申请
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: applyId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/join-applies/{applyId}:
+ delete:
+ summary: APP 撤销加入申请
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱
+ parameters:
+ - name: applyId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/members:
+ get:
+ summary: APP 家谱成员列表
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱成员
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/members/options:
+ get:
+ summary: APP 家谱成员选项
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱成员
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/members/{memberId}:
+ put:
+ summary: APP 修改成员
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱成员
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: memberId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ delete:
+ summary: APP 移除成员
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱成员
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: memberId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/members/me:
+ delete:
+ summary: APP 退出家谱
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱成员
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/members/owner-transfer:
+ put:
+ summary: APP 转让家谱所有者
+ deprecated: false
+ description: ''
+ tags:
+ - 家谱成员
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/generation-poems:
+ get:
+ summary: APP 查询字辈谱
+ deprecated: false
+ description: ''
+ tags:
+ - 字辈谱
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 新增字辈
+ deprecated: false
+ description: ''
+ tags:
+ - 字辈谱
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/generation-poems/batch/preview:
+ post:
+ summary: APP 批量字辈预览
+ deprecated: false
+ description: ''
+ tags:
+ - 字辈谱
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/GenerationPoemBatchBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/generation-poems/batch/save:
+ post:
+ summary: APP 批量保存字辈
+ deprecated: false
+ description: ''
+ tags:
+ - 字辈谱
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/GenerationPoemBatchBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/generation-poems/{poemId}:
+ put:
+ summary: APP 修改字辈
+ deprecated: false
+ description: ''
+ tags:
+ - 字辈谱
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: poemId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/lineage/persons:
+ get:
+ summary: APP 世系人物列表
+ deprecated: false
+ description: ''
+ tags:
+ - 世系人物
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 新增世系人物
+ deprecated: false
+ description: ''
+ tags:
+ - 世系人物
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LineagePersonBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/lineage/persons/page:
+ get:
+ summary: APP 世系人物分页
+ deprecated: false
+ description: ''
+ tags:
+ - 世系人物
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: pageNum
+ in: query
+ description: ''
+ required: false
+ schema:
+ type: integer
+ default: 1
+ - name: pageSize
+ in: query
+ description: ''
+ required: false
+ schema:
+ type: integer
+ default: 10
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/PageResult'
+ description: 通用分页响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/lineage/persons/options:
+ get:
+ summary: APP 世系人物选项
+ deprecated: false
+ description: ''
+ tags:
+ - 世系人物
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/lineage/tree:
+ get:
+ summary: APP 世系树
+ deprecated: false
+ description: ''
+ tags:
+ - 世系人物
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}:
+ get:
+ summary: APP 世系人物详情
+ deprecated: false
+ description: ''
+ tags:
+ - 世系人物
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: personId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ put:
+ summary: APP 修改世系人物
+ deprecated: false
+ description: ''
+ tags:
+ - 世系人物
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: personId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LineagePersonBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ delete:
+ summary: APP 删除世系人物
+ deprecated: false
+ description: ''
+ tags:
+ - 世系人物
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: personId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}/children:
+ post:
+ summary: APP 给人物添加子女
+ deprecated: false
+ description: ''
+ tags:
+ - 世系人物
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: personId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LineagePersonBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}/parents:
+ post:
+ summary: APP 给人物添加父母
+ deprecated: false
+ description: ''
+ tags:
+ - 世系人物
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: personId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LineagePersonBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}/siblings:
+ post:
+ summary: APP 给人物添加兄弟姐妹
+ deprecated: false
+ description: ''
+ tags:
+ - 世系人物
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: personId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LineagePersonBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/lineage/persons/{personId}/spouses:
+ post:
+ summary: APP 给人物添加配偶
+ deprecated: false
+ description: ''
+ tags:
+ - 世系人物
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: personId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LineagePersonBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/pc/genealogies/{genealogyId}/feeds:
+ get:
+ summary: PC 家族圈动态列表
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: PC 发布家族圈动态
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FamilyFeedBody'
+ example:
+ feedType: text
+ feedContent: 今天上传一张老照片。
+ mediaOssIds: 2060000000000000001,2060000000000000002
+ sortOrder: 0
+ status: '0'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/feeds:
+ get:
+ summary: APP 家族圈动态列表
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 发布家族圈动态
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FamilyFeedBody'
+ example:
+ feedType: text
+ feedContent: 今天上传一张老照片。
+ mediaOssIds: 2060000000000000001,2060000000000000002
+ sortOrder: 0
+ status: '0'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/pc/genealogies/{genealogyId}/feeds/page:
+ get:
+ summary: PC 家族圈动态分页
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: pageNum
+ in: query
+ description: ''
+ required: false
+ schema:
+ type: integer
+ default: 1
+ - name: pageSize
+ in: query
+ description: ''
+ required: false
+ schema:
+ type: integer
+ default: 10
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/PageResult'
+ description: 通用分页响应
+ security:
+ - SaToken: []
+ /genealogy/pc/genealogies/{genealogyId}/feeds/{feedId}:
+ get:
+ summary: PC 家族圈动态详情
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ put:
+ summary: PC 修改家族圈动态
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FamilyFeedBody'
+ example:
+ feedType: text
+ feedContent: 今天上传一张老照片。
+ mediaOssIds: 2060000000000000001,2060000000000000002
+ sortOrder: 0
+ status: '0'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ delete:
+ summary: PC 删除家族圈动态
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/feeds/page:
+ get:
+ summary: APP 家族圈动态分页
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: pageNum
+ in: query
+ description: ''
+ required: false
+ schema:
+ type: integer
+ default: 1
+ - name: pageSize
+ in: query
+ description: ''
+ required: false
+ schema:
+ type: integer
+ default: 10
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/PageResult'
+ description: 通用分页响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/feeds/{feedId}:
+ get:
+ summary: APP 家族圈动态详情
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ put:
+ summary: APP 修改家族圈动态
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FamilyFeedBody'
+ example:
+ feedType: text
+ feedContent: 今天上传一张老照片。
+ mediaOssIds: 2060000000000000001,2060000000000000002
+ sortOrder: 0
+ status: '0'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ delete:
+ summary: APP 删除家族圈动态
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/pc/genealogies/{genealogyId}/feeds/{feedId}/likes:
+ post:
+ summary: PC 点赞动态
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ delete:
+ summary: PC 取消点赞动态
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/feeds/{feedId}/likes:
+ post:
+ summary: APP 点赞动态
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ delete:
+ summary: APP 取消点赞动态
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/pc/genealogies/{genealogyId}/feeds/{feedId}/comments:
+ get:
+ summary: PC 动态评论列表
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: PC 评论动态
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FamilyFeedCommentBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/feeds/{feedId}/comments:
+ get:
+ summary: APP 动态评论列表
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 评论动态
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FamilyFeedCommentBody'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/pc/genealogies/{genealogyId}/feeds/{feedId}/comments/page:
+ get:
+ summary: PC 动态评论分页
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: pageNum
+ in: query
+ description: ''
+ required: false
+ schema:
+ type: integer
+ default: 1
+ - name: pageSize
+ in: query
+ description: ''
+ required: false
+ schema:
+ type: integer
+ default: 10
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/PageResult'
+ description: 通用分页响应
+ security:
+ - SaToken: []
+ /genealogy/pc/genealogies/{genealogyId}/feeds/{feedId}/comments/{commentId}:
+ delete:
+ summary: PC 删除动态评论
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: commentId
+ in: path
+ description: 评论ID
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/feeds/{feedId}/comments/page:
+ get:
+ summary: APP 动态评论分页
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: pageNum
+ in: query
+ description: ''
+ required: false
+ schema:
+ type: integer
+ default: 1
+ - name: pageSize
+ in: query
+ description: ''
+ required: false
+ schema:
+ type: integer
+ default: 10
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/PageResult'
+ description: 通用分页响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/feeds/{feedId}/comments/{commentId}:
+ delete:
+ summary: APP 删除动态评论
+ deprecated: false
+ description: ''
+ tags:
+ - 家族圈
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: feedId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: commentId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/article-categories:
+ get:
+ summary: APP 谱文分类
+ deprecated: false
+ description: ''
+ tags:
+ - 内容文章
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/articles:
+ get:
+ summary: APP 谱文列表
+ deprecated: false
+ description: ''
+ tags:
+ - 内容文章
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 新增谱文
+ deprecated: false
+ description: ''
+ tags:
+ - 内容文章
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/articles/{articleId}:
+ get:
+ summary: APP 谱文详情
+ deprecated: false
+ description: ''
+ tags:
+ - 内容文章
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: articleId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ put:
+ summary: APP 修改谱文
+ deprecated: false
+ description: ''
+ tags:
+ - 内容文章
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: articleId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/help-articles:
+ get:
+ summary: APP 帮助文章列表
+ deprecated: false
+ description: ''
+ tags:
+ - 内容文章
+ parameters:
+ - name: clientid
+ in: header
+ description: 客户端ID
+ example: ced7e5f0498645c6ec642dcf450b036f
+ schema:
+ type: string
+ default: ced7e5f0498645c6ec642dcf450b036f
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/help-articles/{helpId}:
+ get:
+ summary: APP 帮助文章详情
+ deprecated: false
+ description: ''
+ tags:
+ - 内容文章
+ parameters:
+ - name: helpId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: 客户端ID
+ example: ced7e5f0498645c6ec642dcf450b036f
+ schema:
+ type: string
+ default: ced7e5f0498645c6ec642dcf450b036f
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/promotions:
+ get:
+ summary: APP 应用推广列表
+ deprecated: false
+ description: ''
+ tags:
+ - 内容文章
+ parameters:
+ - name: clientid
+ in: header
+ description: 客户端ID
+ example: ced7e5f0498645c6ec642dcf450b036f
+ schema:
+ type: string
+ default: ced7e5f0498645c6ec642dcf450b036f
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/albums:
+ get:
+ summary: APP 相册列表
+ deprecated: false
+ description: ''
+ tags:
+ - 相册
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 创建相册
+ deprecated: false
+ description: ''
+ tags:
+ - 相册
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/albums/{albumId}:
+ put:
+ summary: APP 修改相册
+ deprecated: false
+ description: ''
+ tags:
+ - 相册
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: albumId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/albums/{albumId}/photos:
+ get:
+ summary: APP 相册图片列表
+ deprecated: false
+ description: ''
+ tags:
+ - 相册
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: albumId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 上传相册图片记录
+ deprecated: false
+ description: ''
+ tags:
+ - 相册
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: albumId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/ceremonies:
+ get:
+ summary: APP 祭祀列表
+ deprecated: false
+ description: ''
+ tags:
+ - 祭祀
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 创建祭祀
+ deprecated: false
+ description: ''
+ tags:
+ - 祭祀
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/ceremonies/{ceremonyId}:
+ get:
+ summary: APP 祭祀详情
+ deprecated: false
+ description: ''
+ tags:
+ - 祭祀
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: ceremonyId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ put:
+ summary: APP 修改祭祀
+ deprecated: false
+ description: ''
+ tags:
+ - 祭祀
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: ceremonyId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/ceremonies/{ceremonyId}/gifts:
+ get:
+ summary: APP 祭祀献礼列表
+ deprecated: false
+ description: ''
+ tags:
+ - 祭祀
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: ceremonyId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 新增祭祀献礼
+ deprecated: false
+ description: ''
+ tags:
+ - 祭祀
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: ceremonyId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/growth-records:
+ get:
+ summary: APP 成长记录列表
+ deprecated: false
+ description: ''
+ tags:
+ - 族务记录
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 新增成长记录
+ deprecated: false
+ description: ''
+ tags:
+ - 族务记录
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/growth-records/{recordId}:
+ get:
+ summary: APP 成长记录详情
+ deprecated: false
+ description: ''
+ tags:
+ - 族务记录
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: recordId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ put:
+ summary: APP 修改成长记录
+ deprecated: false
+ description: ''
+ tags:
+ - 族务记录
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: recordId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/memos:
+ get:
+ summary: APP 备忘录列表
+ deprecated: false
+ description: ''
+ tags:
+ - 族务记录
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 新增备忘录
+ deprecated: false
+ description: ''
+ tags:
+ - 族务记录
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/memos/{memoId}:
+ get:
+ summary: APP 备忘录详情
+ deprecated: false
+ description: ''
+ tags:
+ - 族务记录
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: memoId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ put:
+ summary: APP 修改备忘录
+ deprecated: false
+ description: ''
+ tags:
+ - 族务记录
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: memoId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/genealogies/{genealogyId}/merit-records:
+ get:
+ summary: APP 功德记录列表
+ deprecated: false
+ description: ''
+ tags:
+ - 族务记录
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 新增功德记录
+ deprecated: false
+ description: ''
+ tags:
+ - 族务记录
+ parameters:
+ - name: genealogyId
+ in: path
+ description: ''
+ required: true
+ example: 900001001
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/notifications:
+ get:
+ summary: APP 消息通知列表
+ deprecated: false
+ description: ''
+ tags:
+ - 消息通知
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/notifications/{notificationId}/read:
+ post:
+ summary: APP 标记通知已读
+ deprecated: false
+ description: ''
+ tags:
+ - 消息通知
+ parameters:
+ - name: notificationId
+ in: path
+ description: ''
+ required: true
+ example: ''
+ schema:
+ type: integer
+ format: int64
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/notifications/read-all:
+ post:
+ summary: APP 全部标记已读
+ deprecated: false
+ description: ''
+ tags:
+ - 消息通知
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/VoidResult'
+ description: 通用成功响应
+ security:
+ - SaToken: []
+ /genealogy/app/feedback:
+ get:
+ summary: APP 我的反馈列表
+ deprecated: false
+ description: ''
+ tags:
+ - 意见反馈
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 提交反馈
+ deprecated: false
+ description: ''
+ tags:
+ - 意见反馈
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+ /genealogy/app/vip/packages:
+ get:
+ summary: APP VIP 套餐列表
+ deprecated: false
+ description: ''
+ tags:
+ - VIP
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ /genealogy/app/vip/orders:
+ get:
+ summary: APP VIP 订单列表
+ deprecated: false
+ description: ''
+ tags:
+ - VIP
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ responses:
+ '200':
+ $ref: '#/components/responses/ListResult'
+ description: 通用列表响应
+ security:
+ - SaToken: []
+ post:
+ summary: APP 创建 VIP 订单
+ deprecated: false
+ description: ''
+ tags:
+ - VIP
+ parameters:
+ - name: clientid
+ in: header
+ description: ''
+ required: true
+ example: 428a8310cd442757ae699df5d894f051
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
+ required: true
+ responses:
+ '200':
+ $ref: '#/components/responses/ObjectResult'
+ description: 通用对象响应
+ security:
+ - SaToken: []
+components:
+ schemas:
+ JsonObject:
+ type: object
+ additionalProperties: true
+ properties: {}
+ PasswordRegisterBody:
+ type: object
+ required:
+ - grantType
+ - tenantId
+ - phone
+ - password
+ properties:
+ clientId:
+ type: string
+ grantType:
+ type: string
+ example: password
+ tenantId:
+ type: string
+ example: '000000'
+ phone:
+ type: string
+ example: '13800000000'
+ password:
+ type: string
+ description: 32 位 MD5
+ example: e10adc3949ba59abbe56e057f20f883e
+ nickName:
+ type: string
+ registerSource:
+ type: string
+ validToken:
+ type: string
+ PasswordLoginBody:
+ type: object
+ required:
+ - grantType
+ - tenantId
+ - phone
+ - password
+ properties:
+ clientId:
+ type: string
+ grantType:
+ type: string
+ example: password
+ tenantId:
+ type: string
+ example: '000000'
+ phone:
+ type: string
+ example: '13800000000'
+ password:
+ type: string
+ description: 32 位 MD5
+ example: e10adc3949ba59abbe56e057f20f883e
+ validToken:
+ type: string
+ SmsLoginBody:
+ type: object
+ required:
+ - grantType
+ - tenantId
+ - phone
+ - smsCode
+ properties:
+ clientId:
+ type: string
+ grantType:
+ type: string
+ example: sms
+ tenantId:
+ type: string
+ example: '000000'
+ phone:
+ type: string
+ example: '13800000000'
+ smsCode:
+ type: string
+ example: '1234'
+ SmsCodeBody:
+ type: object
+ required:
+ - grantType
+ - tenantId
+ - sceneCode
+ - phone
+ - validToken
+ properties:
+ clientId:
+ type: string
+ grantType:
+ type: string
+ example: sms
+ tenantId:
+ type: string
+ example: '000000'
+ sceneCode:
+ type: string
+ example: APP_LOGIN
+ phone:
+ type: string
+ example: '13800000000'
+ validToken:
+ type: string
+ ProfileUpdateBody:
+ type: object
+ properties:
+ nickName:
+ type: string
+ avatarOssId:
+ type: integer
+ format: int64
+ sex:
+ type: string
+ birthday:
+ type: string
+ format: date
+ provinceCode:
+ type: string
+ cityCode:
+ type: string
+ districtCode:
+ type: string
+ PasswordChangeBody:
+ type: object
+ required:
+ - oldPassword
+ - newPassword
+ properties:
+ oldPassword:
+ type: string
+ description: 32 位 MD5
+ newPassword:
+ type: string
+ description: 32 位 MD5
+ PasswordResetBody:
+ type: object
+ required:
+ - tenantId
+ - phone
+ - smsCode
+ - newPassword
+ properties:
+ tenantId:
+ type: string
+ example: '000000'
+ phone:
+ type: string
+ smsCode:
+ type: string
+ newPassword:
+ type: string
+ description: 32 位 MD5
+ validToken:
+ type: string
+ PhoneChangeBody:
+ type: object
+ required:
+ - newPhone
+ - smsCode
+ properties:
+ newPhone:
+ type: string
+ smsCode:
+ type: string
+ validToken:
+ type: string
+ AccountDeactivateBody:
+ type: object
+ required:
+ - password
+ properties:
+ password:
+ type: string
+ description: 32 位 MD5
+ reason:
+ type: string
+ ResumableInitBody:
+ type: object
+ required:
+ - fileName
+ - fileSize
+ - fileMd5
+ - chunkSize
+ - totalChunks
+ properties:
+ fileName:
+ type: string
+ fileSize:
+ type: integer
+ format: int64
+ fileMd5:
+ type: string
+ contentType:
+ type: string
+ chunkSize:
+ type: integer
+ example: 4194304
+ totalChunks:
+ type: integer
+ bizType:
+ type: string
+ usageScene:
+ type: string
+ ResumableCompleteBody:
+ type: object
+ required:
+ - uploadId
+ - fileMd5
+ - fileSize
+ properties:
+ uploadId:
+ type: string
+ fileMd5:
+ type: string
+ fileSize:
+ type: integer
+ format: int64
+ FileReferenceBody:
+ type: object
+ required:
+ - bizType
+ - bizTable
+ - bizId
+ - bizField
+ properties:
+ bizType:
+ description: 业务类型,例如avatar、cover、feed_image
+ type: string
+ example: family_feed
+ bizName:
+ description: 业务名称,用于后台展示文件引用来源
+ type: string
+ example: 家族圈动态
+ bizTable:
+ description: 业务表名,用于文件引用绑定
+ type: string
+ example: gen_family_feed
+ bizId:
+ description: 业务数据ID,用于文件引用绑定
+ type: integer
+ format: int64
+ bizField:
+ description: 业务字段名,例如avatar_oss_id、cover_oss_id
+ type: string
+ example: media_ids
+ ossId:
+ description: OSS文件ID,表单内部使用,用户侧应通过上传组件获取
+ type: integer
+ format: int64
+ ossIds:
+ type: string
+ example: 2060001,2060002
+ usageScene:
+ description: 文件使用场景,便于后续引用统计和清理
+ type: string
+ usageName:
+ type: string
+ GenerationPoemBatchBody:
+ type: object
+ properties:
+ content:
+ type: string
+ example: 德承家亦
+ stopMissingOldGeneration:
+ type: boolean
+ example: false
+ LineagePersonBody:
+ type: object
+ properties:
+ appUserId:
+ type: integer
+ format: int64
+ personNo:
+ type: string
+ personName:
+ type: string
+ aliasName:
+ type: string
+ sex:
+ type: string
+ example: '0'
+ generationNo:
+ type: integer
+ generationName:
+ type: string
+ avatarOssId:
+ type: integer
+ format: int64
+ birthDate:
+ type: string
+ deathDate:
+ type: string
+ introduction:
+ type: string
+ FamilyFeedBody:
+ type: object
+ required:
+ - feedContent
+ properties:
+ feedType:
+ description: 动态类型,未传时默认为 text
+ type: string
+ example: text
+ feedContent:
+ description: 动态内容,不能为空
+ type: string
+ example: 今天上传一张老照片。
+ mediaOssIds:
+ description: 媒体文件 OSS ID 列表,多个 ID 使用英文逗号分隔
+ type: string
+ example: 2060000000000000001,2060000000000000002
+ sortOrder:
+ description: 排序值,未传时默认为 0
+ type: integer
+ format: int64
+ example: 0
+ status:
+ description: 动态状态,未传时默认为正常状态
+ type: string
+ example: '0'
+ FamilyFeedCommentBody:
+ type: object
+ required:
+ - content
+ properties:
+ parentCommentId:
+ type: integer
+ format: int64
+ nullable: true
+ replyUserId:
+ type: integer
+ format: int64
+ nullable: true
+ content:
+ type: string
+ VerificationChallengeBody:
+ type: object
+ required:
+ - sceneCode
+ properties:
+ tenantId:
+ type: string
+ description: 租户 ID
+ clientId:
+ type: string
+ description: 客户端 ID
+ sceneCode:
+ type: string
+ description: 验证场景编码,如 ADMIN_LOGIN/APP_LOGIN/APP_REGISTER
+ subject:
+ type: string
+ description: 验证主体,如手机号/用户名/IP
+ example:
+ tenantId: '000000'
+ clientId: 428a8310cd442757ae699df5d894f051
+ sceneCode: APP_LOGIN
+ subject: '13800000000'
+ VerificationCheckBody:
+ type: object
+ required:
+ - sceneCode
+ properties:
+ tenantId:
+ description: 租户ID,用于区分不同租户的数据和验证策略
+ type: string
+ clientId:
+ description: 客户端ID,对应后台客户端管理中的APP/PC/小程序等终端
+ type: string
+ sceneCode:
+ description: 验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER
+ type: string
+ subject:
+ description: 验证主体,通常为手机号、用户ID或登录账号
+ type: string
+ challengeId:
+ description: 验证挑战ID,由验证挑战接口返回
+ type: string
+ providerCode:
+ type: string
+ description: 验证服务,tianai 或 ruoyi_image
+ captchaType:
+ type: string
+ description: SLIDER/ROTATE/CONCAT/WORD_IMAGE_CLICK/math/char
+ payload:
+ type: object
+ additionalProperties: true
+ description: 前端验证控件返回的轨迹/点击/图形验证码数据
+ properties: {}
+ RVoid:
+ type: object
+ properties:
+ code:
+ type: integer
+ example: 200
+ msg:
+ type: string
+ example: 操作成功
+ data:
+ type: 'null'
+ RObject:
+ type: object
+ properties:
+ code:
+ type: integer
+ example: 200
+ msg:
+ type: string
+ example: 操作成功
+ data:
+ type: object
+ additionalProperties: true
+ properties: {}
+ RList:
+ type: object
+ properties:
+ code:
+ type: integer
+ example: 200
+ msg:
+ type: string
+ example: 操作成功
+ data:
+ type: array
+ items:
+ type: object
+ additionalProperties: true
+ properties: {}
+ PageResult:
+ type: object
+ properties:
+ code:
+ type: integer
+ example: 200
+ msg:
+ type: string
+ example: 操作成功
+ rows:
+ type: array
+ items:
+ type: object
+ additionalProperties: true
+ properties: {}
+ total:
+ type: integer
+ example: 0
+ RLogin:
+ type: object
+ properties:
+ code:
+ type: integer
+ example: 200
+ msg:
+ type: string
+ example: 操作成功
+ data:
+ $ref: '#/components/schemas/LoginVo'
+ LoginVo:
+ type: object
+ properties:
+ token:
+ type: string
+ accessToken:
+ type: string
+ tokenValue:
+ type: string
+ userId:
+ type: integer
+ format: int64
+ tenantId:
+ type: string
+ clientId:
+ type: string
+ clientKey:
+ type: string
+ RFileUpload:
+ type: object
+ properties:
+ code:
+ type: integer
+ example: 200
+ msg:
+ type: string
+ example: 操作成功
+ data:
+ $ref: '#/components/schemas/FileUploadVo'
+ FileUploadVo:
+ type: object
+ properties:
+ ossId:
+ type: integer
+ format: int64
+ url:
+ type: string
+ thumbnailUrl:
+ type: string
+ nullable: true
+ fileName:
+ type: string
+ originalName:
+ type: string
+ RVerificationRequire:
+ type: object
+ properties:
+ code:
+ description: 系统图形验证码或响应状态码
+ type: integer
+ example: 200
+ msg:
+ description: 响应消息
+ type: string
+ example: 操作成功
+ data:
+ description: 响应数据主体
+ $ref: '#/components/schemas/VerificationRequireVo'
+ VerificationRequireVo:
+ type: object
+ properties:
+ required:
+ description: 当前场景是否需要验证
+ type: boolean
+ providerCode:
+ description: 第三方服务商编码
+ type: string
+ captchaType:
+ description: 验证码类型,例如SLIDER、ROTATE、CONCAT、WORD_IMAGE_CLICK
+ type: string
+ sceneCode:
+ description: 验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER
+ type: string
+ ttlSeconds:
+ description: 验证或票据有效秒数
+ type: integer
+ example:
+ required: true
+ providerCode: tianai
+ captchaType: SLIDER
+ sceneCode: APP_LOGIN
+ ttlSeconds: 300
+ RVerificationChallenge:
+ type: object
+ properties:
+ code:
+ description: 系统图形验证码或响应状态码
+ type: integer
+ example: 200
+ msg:
+ description: 响应消息
+ type: string
+ example: 操作成功
+ data:
+ description: 响应数据主体
+ $ref: '#/components/schemas/VerificationChallengeVo'
+ VerificationChallengeVo:
+ type: object
+ properties:
+ required:
+ description: 当前场景是否需要验证
+ type: boolean
+ providerCode:
+ description: 第三方服务商编码
+ type: string
+ captchaType:
+ description: 验证码类型,例如SLIDER、ROTATE、CONCAT、WORD_IMAGE_CLICK
+ type: string
+ challengeId:
+ description: 验证挑战ID,由验证挑战接口返回
+ type: string
+ uuid:
+ type: string
+ description: 系统图形验证码 uuid
+ img:
+ type: string
+ description: 系统图形验证码 base64 图片
+ payload:
+ type: object
+ additionalProperties: true
+ description: 天爱验证码数据,包括背景图、模板图、尺寸等
+ properties: {}
+ expireSeconds:
+ description: 验证挑战过期秒数
+ type: integer
+ example:
+ required: true
+ providerCode: tianai
+ captchaType: SLIDER
+ challengeId: CAPTCHA_CHALLENGE_ID
+ payload:
+ backgroundImage: data:image/png;base64,...
+ templateImage: data:image/png;base64,...
+ backgroundImageWidth: 340
+ backgroundImageHeight: 180
+ expireSeconds: 300
+ RVerificationCheck:
+ type: object
+ properties:
+ code:
+ description: 系统图形验证码或响应状态码
+ type: integer
+ example: 200
+ msg:
+ description: 响应消息
+ type: string
+ example: 操作成功
+ data:
+ description: 响应数据主体
+ $ref: '#/components/schemas/VerificationCheckVo'
+ VerificationCheckVo:
+ type: object
+ properties:
+ passed:
+ description: 验证是否通过
+ type: boolean
+ validToken:
+ type: string
+ description: 验证通过后交给业务接口的票据
+ expireSeconds:
+ description: 验证挑战过期秒数
+ type: integer
+ message:
+ description: 返回消息
+ type: string
+ example:
+ passed: true
+ validToken: captcha-ticket
+ expireSeconds: 300
+ message: 验证通过
+ RLegacyCaptcha:
+ type: object
+ properties:
+ code:
+ description: 系统图形验证码或响应状态码
+ type: integer
+ example: 200
+ msg:
+ description: 响应消息
+ type: string
+ example: 操作成功
+ data:
+ description: 响应数据主体
+ $ref: '#/components/schemas/LegacyCaptchaVo'
+ LegacyCaptchaVo:
+ type: object
+ properties:
+ captchaEnabled:
+ type: boolean
+ uuid:
+ description: 系统图形验证码UUID
+ type: string
+ img:
+ type: string
+ description: base64 图形验证码
+ example:
+ captchaEnabled: true
+ uuid: captcha-uuid
+ img: data:image/png;base64,...
+ responses:
+ VoidResult:
+ description: 通用成功响应
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RVoid'
+ ObjectResult:
+ description: 通用对象响应
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RObject'
+ ListResult:
+ description: 通用列表响应
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RList'
+ PageResult:
+ description: 通用分页响应
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PageResult'
+ LoginResult:
+ description: 登录结果
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RLogin'
+ FileUploadResult:
+ description: 文件上传结果
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RFileUpload'
+ VerificationRequireResult:
+ description: 验证需求查询结果
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RVerificationRequire'
+ VerificationChallengeResult:
+ description: 验证挑战生成结果
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RVerificationChallenge'
+ VerificationCheckResult:
+ description: 验证校验结果
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RVerificationCheck'
+ LegacyCaptchaResult:
+ description: 兼容旧图形验证码结果
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RLegacyCaptcha'
+ securitySchemes:
+ SaToken:
+ type: apiKey
+ in: header
+ name: Authorization
+ description: 登录后返回的 token。若本地配置使用 sa-token 名称,请按项目实际 token-name 调整。
+servers: []
+security:
+ - SaToken: []
diff --git a/App.vue b/App.vue
new file mode 100644
index 0000000..91156d5
--- /dev/null
+++ b/App.vue
@@ -0,0 +1,11 @@
+
+
+
diff --git a/components/AppTabbar.vue b/components/AppTabbar.vue
new file mode 100644
index 0000000..2bc77e7
--- /dev/null
+++ b/components/AppTabbar.vue
@@ -0,0 +1,64 @@
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
diff --git a/components/GenealogyCard.vue b/components/GenealogyCard.vue
new file mode 100644
index 0000000..63a1eee
--- /dev/null
+++ b/components/GenealogyCard.vue
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+ 家谱
+
+
+
+
+ {{ genealogy.name }}
+
+
+
+
+ {{ genealogy.location }}
+
+
+
+ {{ genealogy.memberCount }} 位成员
+
+
+
+ 更新于 {{ genealogy.updatedAt }}
+
+
+
+
+
+ {{ role }}
+
+
+
+
+
+
+
+
diff --git a/components/PageHeader.vue b/components/PageHeader.vue
new file mode 100644
index 0000000..f1c3212
--- /dev/null
+++ b/components/PageHeader.vue
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
diff --git a/data/mock.js b/data/mock.js
new file mode 100644
index 0000000..8249dd2
--- /dev/null
+++ b/data/mock.js
@@ -0,0 +1,68 @@
+export const currentUser = {
+ id: 1,
+ name: '汤文远',
+ phone: '138****1024',
+ role: '创建者'
+}
+
+export const genealogies = [
+ {
+ id: 1001,
+ surname: '汤',
+ name: '汤氏家谱',
+ hall: '敦睦堂',
+ location: '河南·洛阳',
+ memberCount: 158,
+ updatedAt: '2024-05-12',
+ activeCount: 8,
+ visibility: '仅成员可见',
+ motto: '敦亲睦族,敬祖传家。'
+ },
+ {
+ id: 1002,
+ surname: '汤',
+ name: '汤氏宗谱',
+ hall: '承志堂',
+ location: '山东·济宁',
+ memberCount: 286,
+ updatedAt: '2024-04-28',
+ activeCount: 15,
+ visibility: '公开可申请',
+ motto: '继往开来,世守家风。'
+ }
+]
+
+export const treeMembers = [
+ { id: 1, name: '汤文远', relation: '始祖', generation: 1, years: '1940—2012', branch: '主支', x: 50, y: 7 },
+ { id: 2, name: '汤正国', relation: '长子', generation: 2, years: '1965—', branch: '主支', x: 20, y: 38 },
+ { id: 3, name: '汤正华', relation: '次子', generation: 2, years: '1968—', branch: '主支', x: 50, y: 38 },
+ { id: 4, name: '汤正强', relation: '三子', generation: 2, years: '1972—', branch: '支系', x: 80, y: 38 },
+ { id: 5, name: '汤凯', relation: '长孙', generation: 3, years: '1992—', branch: '主支', x: 16, y: 70 },
+ { id: 6, name: '汤悦', relation: '长女', generation: 3, years: '1995—', branch: '主支', x: 35, y: 70 },
+ { id: 7, name: '汤晨', relation: '次子', generation: 3, years: '1998—', branch: '主支', x: 54, y: 70 },
+ { id: 8, name: '汤昊', relation: '三子', generation: 3, years: '2001—', branch: '支系', x: 73, y: 70 },
+ { id: 9, name: '汤宁', relation: '四女', generation: 3, years: '2005—', branch: '支系', x: 90, y: 70 }
+]
+
+export const familyFeeds = [
+ { id: 1, author: '汤正华', title: '清明祭祖通知', content: '本周六上午举行清明祭祖,敬请各支系族亲相互转告。', date: '今天 09:30', type: '族务' },
+ { id: 2, author: '汤悦', title: '老宅修缮旧影', content: '整理出一组敦睦堂老宅照片,已收入家族相册。', date: '昨天 20:18', type: '相册' }
+]
+
+export const familyContent = {
+ article: [{ id: 1, title: '敦睦堂家训摘录', summary: '孝友传家,勤俭立业,敬祖睦族。', date: '2025年4月' }],
+ album: [{ id: 1, title: '敦睦堂老宅旧影', summary: '收录修缮前后的珍贵照片,共 6 张。', date: '2025年3月' }],
+ ceremony: [{ id: 1, title: '清明祭祖', summary: '本周六上午举行祭祖仪式,请族人相互转告。', date: '3 天后' }],
+ record: [{ id: 1, title: '老宅修缮备忘', summary: '屋脊木构加固方案已确认,等待施工。', date: '今天' }]
+}
+
+export const notifications = [
+ { id: 1, title: '有新的入谱申请', content: '陈先生申请加入四川武胜汤氏族谱,等待你的审核。', time: '10:24', unread: true },
+ { id: 2, title: '祭祀活动提醒', content: '清明祭祖将在 3 天后开始。', time: '昨天', unread: true },
+ { id: 3, title: '相册有新照片', content: '汤悦上传了 6 张老宅修缮照片。', time: '4月9日', unread: false }
+]
+
+export const joinApplications = [
+ { id: 1, name: '汤志成', phone: '139****6421', relation: '自述为汤正华堂侄', appliedAt: '今天 10:24', status: 'PENDING' },
+ { id: 2, name: '汤雨薇', phone: '136****2798', relation: '自述为汤正国之女', appliedAt: '昨天 18:02', status: 'PENDING' }
+]
diff --git a/docs/PROJECT_PLAN.md b/docs/PROJECT_PLAN.md
new file mode 100644
index 0000000..642b71d
--- /dev/null
+++ b/docs/PROJECT_PLAN.md
@@ -0,0 +1,34 @@
+# 家谱 App 项目计划
+
+## 已确定的产品规则
+
+- 端:uni-app 用户端,面向 Android 与 iOS 的 HBuilderX 打包。
+- 导航:家谱、家族、我的;通知入口只做提醒与跳转。
+- 主流程:管理员创建家谱并录入首代关系,族人申请加入后由管理员审核。
+- 访问规则:家谱默认仅成员可见;管理员可开放为可搜索、可申请加入。
+- 视觉:家祠卷轴——朱砂红、宣纸暖白、黛青、鎏金细线、竖式人物牌位。
+- 素材:自制图标均为透明背景 PNG;必要时采用可商用在线 PNG 图标并统一风格。
+- 接口:`APP.openapi.yaml` 是 App 端接口契约;地址、`clientid`、`tenantId` 与测试账号通过本地配置注入。
+
+## 进度
+
+- [x] 阅读思维导图、现有界面稿、参考 uni-app 工程与 OpenAPI 文档。
+- [x] 确定三 Tab 信息架构、核心创建流程、加入审核规则和视觉方向。
+- [x] 创建 uni-app 工程骨架、设计令牌与透明 PNG 图标资产。
+- [x] 完成登录、我的家谱、创建家谱、家谱总览、世系树、成员档案和申请审核的首版界面与本地交互闭环。
+- [x] 实现登录、我的家谱、创建家谱与公开家谱申请加入。
+- [x] 修复 Vue 3 工程入口:补齐根目录 `index.html`,并加入配置与入口自检。
+- [x] 完成当前已实现页面的 HBuilderX Web 编译、全量语言诊断与缓存排查;本地 H5 发行构建仍受 HBuilderX 登录状态限制。
+- [x] 参照完整 uni-app 工程修复 Sass 注入:令牌直接置于 `uni.scss`,重启 Vite 后登录页样式请求已验证为 200。
+- [x] 完成显式 Mock/Remote 模式、密码登录链路、token 保存、接口 `data` 解包与当前家谱 ID 上下文。
+- [x] 完成创建家谱后录入首代,并按当前家谱加载详情、世系树、成员档案、申请审核与通知已读。
+- [x] 完成家族动态发布、谱文/相册/祭祀/族务内容入口,以及动态个人资料读取。
+- [ ] 实现家谱总览、世系树、成员资料和亲属关系编辑。
+- [ ] 实现家族圈、谱文、相册、祭祀、贺礼、成长记录、备忘和功德。
+- [ ] 实现通知、个人中心、帮助、反馈、VIP 与接口配置。
+- [ ] 在 HBuilderX 验证运行、Android/iOS 打包配置和关键流程。
+
+## 当前接口限制
+
+- 已支持:认证、家谱、成员、字辈、世系树、家族圈、文章、相册、祭祀、族务记录、通知、反馈与 VIP。
+- 暂不实现为可用功能:邀请码、视频与多管理员分级权限;接口文档尚未提供对应端点。
diff --git a/docs/REFERENCE_PROJECT_AUDIT.md b/docs/REFERENCE_PROJECT_AUDIT.md
new file mode 100644
index 0000000..49c30b1
--- /dev/null
+++ b/docs/REFERENCE_PROJECT_AUDIT.md
@@ -0,0 +1,28 @@
+# 参考 uni-app 工程结构核对
+
+参考工程:`C:\Users\Rain\Desktop\软件\JOB\3dyjsapp`
+
+## 已确认并采纳
+
+- 参考工程是完整的 HBuilderX uni-app 工程,根目录使用 `App.vue`、`main.js`、`pages.json`、`manifest.json`、`index.html` 与 `uni.scss`。
+- 参考工程的 `manifest.json` 已声明 Vue 3;`main.js` 同时保留 Vue 2 与 Vue 3 的条件入口。家谱项目固定使用 Vue 3,因此保留精简的 Vue 3 入口即可。
+- 参考工程的 `uni.scss` 直接定义 Sass 变量,而不是在其中使用相对路径导入。家谱项目已按此方式调整:`uni.scss` 是唯一的设计令牌来源。
+- 参考工程把页面清单集中在 `pages.json`,家谱项目已用自动审计校验每条路由都有对应页面文件。
+
+## 本项目的对应实现
+
+- `uni.scss`:朱砂红、宣纸暖白、墨色、鎏金等 Sass 令牌。
+- `styles/global.scss`:只由 `App.vue` 从根目录引入的全局通用样式。
+- `tests/compile-audit.ps1`:校验路由、入口、Sass 引用与模块引用。
+- `tests/uni-scss-injection.ps1`:防止在 `uni.scss` 中再次写入会被页面按错误目录解析的相对 Sass 导入。
+
+## 不照搬的部分
+
+- 彩票业务页面、接口、数据模型与大量业务组件。
+- 参考工程中的第三方 UI 模块、OAuth 配置和过宽设备权限;家谱 App 按实际功能逐项添加,避免无关权限与凭据进入工程。
+
+## 后续仍需补齐
+
+- 家谱 App 的 Android/iOS 应用图标尺寸与启动图资源。
+- 登录后的真实接口配置与测试账号联调。
+- HBuilderX 登录后的本地 H5 发行构建及 Android/iOS 云打包验证。
diff --git a/docs/design/D1_安卓视觉规范与页面壳.md b/docs/design/D1_安卓视觉规范与页面壳.md
new file mode 100644
index 0000000..093944a
--- /dev/null
+++ b/docs/design/D1_安卓视觉规范与页面壳.md
@@ -0,0 +1,265 @@
+# D1:安卓视觉规范与页面壳
+
+> 对应《规划》:P-02、P-03、P-04
+> 适用范围:Android 手机端全部页面
+> 设计锚点:用户提供的“汤氏家谱世系图”参考图
+
+## 1. 设计锚点与不可偏离项
+
+参考图不是只借用“红色”,而是整套视觉语言的依据。后续每一页必须具备以下共性:
+
+1. **家祠红头图**:核心家谱页面使用朱砂红顶部,左侧为低对比古建筑线描,标题使用金色或暖白色。
+2. **宣纸正文区**:主内容始终落在暖白纸面上,背景只允许极浅的山峦、竹影、亭台或纸纤维纹理,不能影响文字阅读。
+3. **墨字与古金线**:正文使用墨褐色,分隔、牌位、题签和次级结构使用古金细线。
+4. **礼制化状态**:当前选中、重要操作与未读提醒使用朱砂;不可用荧光色、大面积渐变或通用蓝色按钮替代。
+5. **牌位不是卡片**:世系树、人物录核心信息使用竖式牌位;其他列表保留纸签和金线的秩序感,不能变成白底大圆角信息卡。
+
+### 1.1 一比一还原的验收定义
+
+“一比一”指以参考图为唯一视觉锚点,不能只借用红色后另行发挥。以 `360 × 800dp` 设计画布验收时,必须同时还原:朱砂红头的高度与留白、祠堂线描的位置和低对比度、金色标题比例、宣纸与山水底纹、古金牌位线框、朱砂选中描边、纸面底部抽屉与底部导航的层级效果。
+
+不同业务页的内容结构可以不同,但不能把世系图页面原样复制到每个页面;它们必须在同一套材质、颜色、字体、线框和动效逻辑中变化。为适配 `320 × 568` 等小屏,元素可流式换行或缩放,**不得**为追求固定像素而造成重叠或不可点击。
+
+每张后续设计稿都要和参考图并排核对上述八项;没有通过核对,不能标为设计完成。
+
+## 2. Android 基础尺寸
+
+设计坐标以 `750rpx` 宽度为基准。实现时允许流式收缩,但不得缩小关键点击区。
+
+| 元素 | 设计值 | 小屏规则 |
+| --- | ---: | --- |
+| 页面左右安全边距 | `32rpx` | `320px` 小屏可缩至 `24rpx`。 |
+| 页面标题 | `34rpx` | 不换成两行;长标题使用省略号。 |
+| 主标题 | `48rpx` | 家谱名最大两行,超出截断。 |
+| 正文 | `28rpx` | 不低于 `24rpx`。 |
+| 辅助文字 | `24rpx` | 仅用于时间、状态、说明。 |
+| 主按钮高度 | `88rpx` | 最小点击高度,不因小屏减小。 |
+| 图标触控框 | `88rpx × 88rpx` | 图标视觉大小可为 `40–48rpx`。 |
+| 列表行高度 | `104rpx` 起 | 多行信息自动增高。 |
+| 底部操作栏 | `128rpx` 起 | 加系统导航保底内边距。 |
+
+## 3. 色彩、字体与图标
+
+### 3.1 色彩
+
+| Token | 色值 | 使用位置 |
+| --- | --- | --- |
+| `paper-page` | `#F6F0E5` | 页面整体纸底。 |
+| `paper-surface` | `#FFF9EE` | 抽屉、表单、列表承载面。 |
+| `cinnabar` | `#A33B2B` | 主操作、选中描边、徽章。 |
+| `cinnabar-deep` | `#7E251C` | 顶部红底的层次和按下态。 |
+| `ink` | `#342A24` | 标题、主正文。 |
+| `ink-muted` | `#7A6958` | 次级正文、时间。 |
+| `antique-gold` | `#B58A4B` | 牌位描边、细分割线、装饰。 |
+| `gold-soft` | `#E4D4B8` | 弱分割线、未选中轮廓。 |
+| `danger` | `#B63A2C` | 删除、审核拒绝;不额外引入鲜红。 |
+
+绿色不进入页面的背景、导航、主按钮或状态色。若用户照片中含绿色,保持原图,不再叠加绿色 UI 元素。
+
+### 3.2 字体
+
+- 页面标题、家谱名、人物姓名:系统衬线回退链 `serif`;仅用于短标题和牌位姓名,体现家谱的书卷感。
+- 正文、表单、数字、按钮:Android 系统无衬线字体 `sans-serif`;优先保证低版本设备的字形和行高稳定。
+- 不下载网络字体,不依赖单一厂商字体,不用细于常规字重的中文字体。
+- 姓名为两至四字时居中竖排;超过四字改为横排或两行,禁止压缩字距造成难读。
+
+### 3.3 透明 PNG 图标规范
+
+- 图标文件一律是 RGBA PNG,四角透明;不把乳白或绿色底色烘焙进图标。
+- 功能图标以官方 `24dp × 24dp` 视口为唯一留白标准,导出为 `96 × 96px`(4 倍)透明 PNG;不得再从大幅生成图中裁出图标、不得人为叠加 `25%` 以上的额外空白。图标的边距只保留官方图形本身的标准边距。
+- 正常态使用墨褐或古金单色;选中态使用朱砂;不使用渐变、投影或多色填充。
+- 图标命名采用 `功能-状态.png`,例如 `tree-active.png`、`notice-default.png`、`profile-active.png`。
+- 首批需要的图标:返回、搜索、更多、通知、家谱、家族、我的、世系图、列表、编辑、新增、相册、礼仪、消息、设置、关闭。
+
+### 3.4 资产优先规则
+
+- 所有具备明确动作或状态的元素必须使用透明 PNG 图标:导航、返回、搜索、通知、更多、关闭、编辑、新增、上传、相册、成员、世系、礼仪、设置、删除、分享、点赞、评论。
+- 禁止使用 Emoji、Unicode 符号、临时文字(例如“返回”“更多”)替代功能图标;功能图标旁的文字仅用于解释,不承担唯一识别职责。
+- 文字只保留在用户必须阅读或选择的内容上:页面标题、家谱名称、成员资料、表单标签、状态说明与按钮动作。
+- 每一个图标必须有默认态与需要时的选中态;选中态仅改变为朱砂色,不改变图形轮廓,保证低端 Android 设备上也能一眼识别。
+- **后续强制规则**:任何后续页面设计中新增加的图标,必须同步生成对应的 RGBA 透明 PNG,保存到项目 `static/assets/icons/` 后才能将该页面标记为“设计完成”。设计图中的图形、文字占位、Emoji 或系统默认图标均不能替代实际资产。
+- 每个新增图标都要在资产清单登记:用途、文件路径、默认 / 选中状态、设计页编号和透明背景检查结果;页面实现只能引用清单中的已验收资产。
+
+### 3.4.1 功能图标的标准骨架与视觉风格
+
+- Android / Material 的在线规范用于锁定**图标语义、24dp 视口、触控区、默认 / 选中状态和可识别性**,不是要求本项目的每个图标都长成默认黑色 Material 图标。
+- 项目 Logo、祠堂纹样、宣纸、山水竹影属于品牌与背景插画,可以定制生成;顶部和通用操作图标允许采用本项目的“古金双线 + 克制云纹收口”风格,风格锚点见 [公共操作图标风格锚点](references/公共操作图标-风格锚点.png)。
+- 自定义图标仍须保留标准语义:返回一眼识别为左箭头、搜索为放大镜、通知为铃铛、更多为三点、关闭为 X;不得因装饰而改变动作含义。
+- 底部导航固定为三项且每项同时显示图标与短标签:`家谱`、`家族`、`我的`。现有 Tab 图标不通过验收,必须重设计为等视觉重量的简洁轮廓;不能出现云纹、枝叶和双层复杂装饰。选中项使用同一图形的朱砂色与填充 / 加粗状态,非选中项用墨褐或古金描边。
+- 顶部与通用操作采用标准语义:返回、搜索、通知、更多、关闭、新增、编辑、上传、删除、分享、设置。图标可定制风格,但不以文字、Emoji 或难以识别的自造图形替代。
+
+### 3.4.2 按钮图标风格分级
+
+- **大部分按钮与顶部操作**:返回、搜索、通知、更多、关闭、新增、编辑、上传、删除、分享、设置,以及家谱 / 家族模块中的快捷入口,采用“古金双线 + 轻云纹收口”风格。
+- **主操作按钮**:图标可置于“新建家谱、保存、发布、提交”等文字前;图标为古金双线简化版,按钮本身仍以朱砂描边或朱砂实底表达主次,不用纯文字按钮。
+- **底部 Tab**:不得直接使用复杂云纹版本;使用更简洁的同色系轮廓,优先保证 24dp 识别度和三个目的地的差异。
+- **状态与危险操作**:成功、失败、删除、无权限等不使用花纹干扰信息;保留标准清晰的符号,颜色按状态区分。
+
+> 来源依据:Google Material Symbols 官方库提供适用于 Android 的 PNG / SVG 图标、`FILL` 状态轴和 20–48dp 光学尺寸;Android 官方将导航栏用于 3–5 个同等级目的地。项目以这些规则做底线,并在其上保留家祠卷轴的视觉个性。
+
+### 3.5 图标资产清单与路径
+
+所有最终资产均保存在 `static/assets/icons/`,由页面直接引用;不使用在线 URL。
+
+| 资产组 | 文件 | 状态 |
+| --- | --- | --- |
+| 项目标识 | `brand/jiapu-seal-logo.png` | 自定义生成,待缩小尺寸验收 |
+| 顶部操作 | `common/back.png`、`common/search.png`、`common/notice.png`、`common/more.png`、`common/close.png` | 保留已确认的古金双线风格,待逐个紧凑导出 |
+| 底部导航 | `tab/genealogy.png`、`tab/family.png`、`tab/profile.png` 及各自 `-active.png` | 待按标准 24dp 语义重绘为简洁同风格版本 |
+| 家谱操作 | `genealogy/tree.png`、`members.png`、`generation-poem.png`、`application.png`、`create.png`、`edit.png` | 页面设计时按 Material Symbols 官方源导出 |
+| 家族内容 | `family/feed.png`、`article.png`、`album.png`、`ceremony.png`、`gift.png`、`archive.png` | 页面设计时按 Material Symbols 官方源导出 |
+| 通用操作 | `action/add.png`、`upload.png`、`delete.png`、`share.png`、`like.png`、`comment.png`、`setting.png` | 待按古金双线风格逐个紧凑导出 |
+
+### 3.5 已否决的资产(不得引用)
+
+`static/assets/icons/common/`、`static/assets/icons/tab/`、`static/assets/icons/action/` 当前是从大幅绿色底图裁切的原始试验文件,仍不得直接引用。其问题是**画布和缩放方式**,不是用户已确认的顶部操作图标美术方向。
+
+**根因记录**:这些文件来自带大块绿色空白的 3 × 2 AI 图稿,再按格子裁切;生成图没有稳定的标准视口,导致图标周围出现过多空白、主体大小不一致。顶部操作图标的古金双线与轻云纹风格保留;Tab 图标的复杂枝叶、过多装饰和不统一轮廓不保留。
+
+**新生产方式**:每个图标单独制作并紧凑导出到 `96px` PNG,实际图形占官方 24dp 视口的正常范围;不得再批量盲裁。顶部 / 操作图标以风格锚点为准,Tab 按简洁语义重新制作。每个文件在 `46rpx` 实际显示尺寸检查后才可引用。
+
+### 3.6 功能图标验收
+
+功能图标只有同时满足以下条件,才可标记为“已生成”:
+
+1. 语义、视口与状态符合 Google Material Symbols / Android 规范;若为本项目定制图标,记录其对应的标准语义与风格锚点。
+2. 默认 / 选中态为同一语义符号,保持 `24dp` 标准画布与固定留白;不得改变动作含义来冒充选中态。
+3. 项目内文件为 RGBA 透明 PNG,四角透明;默认墨褐、选中朱砂;在纸白底和红头上均满足清晰可见。
+4. 在 `46rpx` Tab 显示尺寸及 `320 × 568` 小屏中检查可识别性、标签不换行、点击区不小于 `88rpx`。
+
+### 3.7 背景资产规则
+
+- 所有装饰性背景都必须生成并保存在项目 `static/assets/backgrounds/`;禁止依赖网络背景图,也不能只用临时 CSS 渐变假装纸纹。
+- 背景分层使用:`header-hall-lineart.png` 用于朱砂顶部;`paper-rice-texture.jpg` 用于宣纸底;`footer-mountain-bamboo.png` 用于页面底部的淡山水竹影。页面按需组合,不把它们烘焙成一张全屏超大图。
+- 祠堂线描、山水、竹影等叠加背景应使用透明 PNG;宣纸纸纹使用压缩后的 JPG。所有背景必须无文字、无 Logo、无人物、无绿色调。
+- 为兼容老款 Android:顶部纹样显示尺寸不超过 `750 × 288rpx`,底部山水显示尺寸不超过 `750 × 360rpx`;背景只在首屏和固定区域加载,不在长列表中重复解码。
+- 背景完成验收需检查:`320 × 568` 下不遮挡标题或操作、纸纹不影响文字对比度、图片已压缩到适合移动端的尺寸。
+- **页面背景完成条件**:每一个页面必须在其设计记录中写清“红头 / 纸纹 / 山水竹影 / 侧边竹影 / 无”的组合与对应资产路径;没有背景组合的页面不得标记为“设计完成”。背景可以复用基础资产,但不能是未设计的纯色空白页面或临时 CSS 渐变。
+
+| 页面类型 | 必须使用的背景组合 |
+| --- | --- |
+| 家谱总览、世系树、成员档案 | 朱砂红头 + 祠堂线描 + 宣纸纸纹;底部或抽屉使用淡山水竹影。 |
+| 家族动态、谱文、相册、礼仪、消息 | 紧凑朱砂题签 + 宣纸纸纹;内容尾部按需叠加竹影或山水。 |
+| 创建、编辑、申请、设置、反馈 | 紧凑题签 + 宣纸纸纹;表单区域保持干净,不让山水压住输入内容。 |
+| 登录、注册、找回密码 | 独立生成的红头 / 纸纹欢迎背景;不能直接复用列表页大山水。 |
+| 无数据、失败、审核中 | 宣纸纸纹 + 对应低对比卷轴 / 印章背景资产;不能只显示文字。 |
+
+| 背景资产 | 路径 | 用途 | 状态 |
+| --- | --- | --- | --- |
+| 祠堂线描 | `backgrounds/header-hall-lineart.png` | 家谱红头左侧或底部纹样 | 生成中 |
+| 宣纸纸纹 | `backgrounds/paper-rice-texture.jpg` | 全局纸面底色 | 未生成 |
+| 山水竹影 | `backgrounds/footer-mountain-bamboo.png` | 列表页、抽屉页底部装饰 | 未生成 |
+
+## 4. 公共页面壳
+
+### 4.1 核心家谱页面壳
+
+适用于:家谱总览、世系图、成员目录、人物档案。
+
+- 顶部 `256–288rpx`:朱砂红,左下放置低对比祠堂屋檐线描,禁止使用满屏建筑照片。
+- 根页面左上角放项目 Logo(祠堂屋檐 + “谱”字印章,透明 PNG,视觉 `40rpx`、触控区 `88rpx`);详情页该位置优先保留返回按钮,Logo 收入建筑纹样中的小印记。
+- 搜索、通知、更多均为 `88rpx` 触控框;图标为暖白或金色线稿 PNG。通知铃铛仅在未读消息数大于 0 时显示朱砂小圆点;未读为 0 时不显示圆点和数字。
+- 家谱名称位于视觉中心,最大两行;名称下可放地域或代系提示,但不能与图标重叠。
+- 红头结束后立即进入纸面 Tab/分段导航,使用古金底线标记当前项。
+
+### 4.2 普通列表与详情页面壳
+
+适用于:家族动态、谱文、相册、人物录、礼仪、消息、我的。
+
+- 顶部使用 `144–176rpx` 紧凑朱砂题签,保留建筑线描的局部纹样。
+- 内容区采用纸白承载面与古金细分割线,列表项不使用夸张圆角;圆角最大 `16rpx`。
+- 每页只保留一个视觉主操作:新增、发布、保存或提交。它固定在底部操作栏或右下角,不能同时出现两个醒目的红按钮。
+
+### 4.3 表单与编辑页面壳
+
+适用于:创建家谱、录入始祖、申请加入、发布内容、编辑成员、反馈、设置。
+
+- 顶部为紧凑题签,正文使用逐行纸签式表单,标签在左、输入值在右或下一行。
+- 必填项用朱砂小印记表示;校验说明用暖灰褐,不使用大面积红色报错框。
+- 键盘出现时页面可滚动,底部“保存 / 提交”保持可操作。
+
+### 4.4 底部导航与抽屉
+
+- 三 Tab:家谱、家族、我的。纸白底、顶部古金细线;当前图标和文字用朱砂,未选中用墨褐。
+- 无底部 Tab 的详情页,底部保留返回区域或单主操作栏,不能出现空白黑条。
+- 成员、相册、礼仪的详情采用半屏至全屏底部抽屉:纸白底、顶部金色短握条、右上关闭按钮、淡竹影或山影作为低对比装饰。
+
+## 5. 核心组件规则
+
+### 5.1 世系人物牌位
+
+- 牌位为暖白底、古金双线边框、上下折角;不使用普通矩形卡片。
+- 始祖牌位比同层成员高一级;当前选中人物外框换成朱砂双线,并使用小圆点连接标识。
+- 牌内信息顺序:姓名 → 字 / 号 → 生卒或年份;信息不足时显示“生卒不详”,不留大段空白。
+- 代际标签固定在树左侧,不随横向滚动丢失;横向树区域与底部档案抽屉互不遮挡。
+
+### 5.2 纸签列表
+
+- 左侧为功能图标或缩略图,中间为标题和两行信息,右侧是状态或箭头。
+- 每行以古金细线分隔;红色仅用于“待审核”“未读”“进行中”等需要注意的状态。
+- 长标题最多两行;日期、人数、地域等元信息单独一行,不与操作按钮抢空间。
+
+### 5.3 主操作与次操作
+
+- 主操作:朱砂描边或朱砂实底,圆角 `44rpx` 的胶囊形;文字白色或纸白。
+- 次操作:纸白底、古金描边、墨褐文字。
+- 删除和退出:先显示纸面确认抽屉,再允许使用 `danger` 色;不可直接用红色文字替代确认流程。
+
+### 5.4 空状态与异常
+
+- 空状态使用纸纹中的淡印章、卷轴、山影或未展开牌位,不使用卡通人物。
+- 网络失败使用“纸页未送达”图标、明确重试按钮;不把错误码直接展示给用户。
+- 审核中使用朱砂印章和预计提示;无权限使用合拢卷轴与申请/返回入口。
+
+## 6. 首屏结构:家谱首页(G-01)
+
+G-01 是所有设计页的公共还原样板,不等同于最终代码。
+
+1. 顶部:朱砂祠堂红头,左侧项目 Logo,中央为“我的家谱”,右侧通知 PNG 图标;只有未读消息时才带朱砂小圆点。
+2. 主区:当前家谱以横向家谱题签展示名称、地域、成员数和身份;这个题签的朱砂印章代表“当前选中的家谱”,不是另一种家谱颜色。没有家谱时替换为卷轴空状态。
+3. 快捷区:世系图、成员、字辈诗、申请审核四项,使用透明 PNG 图标与古金分隔。
+4. 家谱列表:明确分为“我创建的”和“我加入的”两个标题区;每项使用同一套纸签和古金线,不用红色 / 金色印章暗示未说明的身份差异。当前选中项可用朱砂细边标记。
+5. 底部:三 Tab;主操作“创建家谱”在内容末尾或空状态中出现,避免遮挡 Tab。
+
+### 6.1 G-01 数据文案规则
+
+- 家谱名称后不得出现无意义横线、破折号、模拟字符或“待补充”占位。
+- 有数据时按真实字段显示,例如“河南省 · 洛阳市”“158 位成员”“管理员”;无数据时整行隐藏,或显示明确的“地区待完善 / 成员待录入”。
+- “当前家谱”与下方列表不得无说明地重复同一名称;当前题签显示选中的家谱,下方列表用于切换,选中项需要清晰标记。
+
+### 6.2 G-01 固定设计栅格(360 × 800dp)
+
+这是后续页面还原的硬尺寸基准,不再交给整页 AI 图自行猜测:
+
+| 区域 | 尺寸 / 间距 | 约束 |
+| --- | --- | --- |
+| 系统状态栏 | `24dp` | 只预留系统空间,不放页面内容。 |
+| 家祠红头 | `112dp`(含标题区) | Logo 左 `16dp`,标题居中,铃铛右 `16dp`;红头结束后不得出现大块空白。 |
+| 当前家谱题签 | 顶距 `16dp`,高 `136dp`,左右 `16dp` | 只展示当前家谱一次;名称、成员数、地域、身份均为真实字段。 |
+| 快捷入口 | 题签后 `16dp`,行高 `84dp` | 四项等宽;图标实际 `24dp`,标签 `14sp`,不使用大号装饰图。 |
+| 分隔与列表标题 | 上距 `16dp`,标题区 `36dp` | 细古金线 + “我创建的 / 我加入的”,不能留无内容的整屏空白。 |
+| 家谱列表项 | 每项 `72dp`,项间 `8dp` | 一项只显示名称、必要元信息、右箭头;不重复当前题签内容。 |
+| 新建家谱 | 列表后 `16dp`,高 `48dp` | 朱砂描边胶囊按钮,不用巨大的上下留白烘托。 |
+| 底部导航 | `64dp` + 系统导航保底 | 纸白底、三项等宽,图标 `24dp`、标签 `12sp`、每项触控区至少 `48dp`。 |
+
+上述内容总高度在 `360 × 800dp` 首屏内保持紧凑;数据较多时由内容区滚动,而不是拉大首屏留白。`320 × 568` 下题签和快捷入口仍在首屏可见,列表向下滚动。
+
+### 6.3 已否决的 G-01 生成整页图
+
+此前 AI 生成的“我的家谱”整页图**保留为美术锚点**:[G01 我的家谱美术锚点](references/G01-我的家谱-美术锚点.png)。它明确了本项目应该保留的整体效果:朱砂祠堂红头、暖金标题、宣纸纸纹、金线题签、淡山水竹影、纸白底部导航和庄重的色彩层级。
+
+该图**不直接作为尺寸与功能验收稿**,因此不计入 G-01 的“页面设计完成”。需要按 6.2 的固定栅格修正:红头后与题签之间的过大留白、快捷入口与列表之间的过大留白、缺失的左上 Logo、当前家谱与列表的重复、无意义横线占位、以及非标准来源图标。
+
+从此以后,页面设计采用“双锚点”:本图锁定美术氛围和视觉效果;6.2 固定栅格锁定尺寸、内容密度与 Android 小屏可用性。AI 只可生成 Logo、背景等独立视觉资产,不能决定页面间距、文字排版、列表高度或交互层级。
+
+### 6.4 标准来源验收备注
+
+- Material Symbols 官方指南:
+- Android Navigation Bar 官方指南:
+
+## 7. 设计状态记录
+
+- P-02:完成。颜色、字体、尺寸、透明 PNG 图标规范已锁定。
+- P-03:完成。核心、普通、表单三类页面壳及底部导航、抽屉已锁定。
+- P-04:完成。加载、空数据、失败、无权限、审核中与确认操作规则已锁定。
+- 下一阶段:D2 账户与首次使用页面;全部页面均按本规范出高保真稿后,才进入 uni-app 还原。
diff --git a/docs/design/G01_我的家谱_设计记录.md b/docs/design/G01_我的家谱_设计记录.md
new file mode 100644
index 0000000..d25b760
--- /dev/null
+++ b/docs/design/G01_我的家谱_设计记录.md
@@ -0,0 +1,106 @@
+# G-01:我的家谱设计记录
+
+> 状态:资产准备中
+> 美术锚点:[G01 我的家谱美术锚点](references/G01-我的家谱-美术锚点.png)
+> 布局锚点:[D1 安卓视觉规范与页面壳](D1_安卓视觉规范与页面壳.md#62-g-01-固定设计栅格360--800dp)
+
+## 页面目标
+
+让已登录用户一眼查看当前家谱、进入四个高频入口、切换已创建 / 已加入家谱,并在无家谱时创建或搜索加入。
+
+## 背景组合
+
+- 红头:`static/assets/backgrounds/header-hall-lineart.png` 叠加朱砂底色。
+- 内容纸面:`static/assets/backgrounds/paper-rice-texture.jpg`。
+- 页面底部:`static/assets/backgrounds/footer-mountain-bamboo.png`,只用于列表尾部与新建按钮附近。
+
+## 图标资产清单
+
+所有项目完成后均为独立、紧凑的 RGBA 透明 PNG;不能从大图裁切后直接使用。
+
+| 区域 | 语义 | 目标文件 | 风格 | 状态 |
+| --- | --- | --- | --- | --- |
+| 顶部左侧 | 项目 Logo | `brand/jiapu-seal-logo.png` | 用户确认的家祠 Logo 原图 | 已确认使用;待页面小尺寸摆放验收 |
+| 顶部右侧 | 通知 | `common/notice-v3.png` | 古金双线 + 轻云纹 | 已生成;`70 × 94px` 主体通过紧凑尺寸检查 |
+| 列表入口 | 右箭头 | `common/chevron-right-v2.png` | 古金简洁单线 | 已生成;`96 × 96px`、RGBA 四角透明、48px 检查通过 |
+| 快捷入口 | 世系图 | `genealogy/tree-v2.png` | 古金双线简化版 | 已生成;`96 × 96px`、RGBA 四角透明、48px 检查通过 |
+| 快捷入口 | 家族成员 | `genealogy/members-v2.png` | 古金双线简化版 | 已生成;`96 × 96px`、RGBA 四角透明、48px 检查通过 |
+| 快捷入口 | 字辈诗 | `genealogy/generation-poem-v2.png` | 古金双线简化版 | 已生成;`96 × 96px`、RGBA 四角透明、48px 检查通过 |
+| 快捷入口 | 申请审核 | `genealogy/application-v2.png` | 古金双线简化版 | 已生成;`96 × 96px`、RGBA 四角透明、48px 检查通过 |
+| 主操作 | 新建家谱 | `action/add-v2.png` | 古金双线 + 轻云纹 | 已生成;`96 × 96px`、RGBA 四角透明、48px 检查通过 |
+| 底部 Tab | 家谱 | `tab/genealogy-v4.png`、`genealogy-active-v4.png` | 简洁谱册 / 世系语义 | 已生成;同轮廓墨褐 / 朱砂成对导出,`96 × 96px`、48px 检查通过 |
+| 底部 Tab | 家族 | `tab/family-v4.png`、`family-active-v4.png` | 简洁双人群组语义 | 已生成;同轮廓墨褐 / 朱砂成对导出,`96 × 96px`、48px 检查通过 |
+| 底部 Tab | 我的 | `tab/profile-v4.png`、`profile-active-v4.png` | 简洁人物档案语义 | 已生成;同轮廓墨褐 / 朱砂成对导出,`96 × 96px`、48px 检查通过 |
+
+## 页面完成条件
+
+- [ ] 图标资产清单全部生成并在 `46rpx` / `24dp` 下逐个检查。
+- [ ] 背景组合在 `320 × 568` 下不遮挡标题、按钮和列表。
+- [ ] 按固定栅格完成正常态、无家谱态与加载态设计。
+- [ ] 不存在横线占位、重复家谱层级或无业务含义的颜色差异。
+
+## Tabbar 重设计规则
+
+- 当前 `static/assets/icons/tab/` 和 `static/icons/tab-*.png` 的图标不通过验收,不得使用。
+- 三个 Tab 重新采用等视觉重量的简洁符号:家谱为“世系关系 / 谱册”语义,家族为“双人群组”语义,我的为“人物档案”语义。
+- 三个图标同为 `24dp` 标准视口、无文字嵌入、无枝叶和云纹等细碎装饰;默认态为墨褐 / 古金描边,选中态为朱砂色的同一图形加粗或填充。
+- Logo 是品牌标识,不能拿来代替家谱 Tab 图标;Tab 图标只服务于快速识别目的地。
+
+## 2026-07-11 当日确认记录
+
+- 当前交接视觉稿:[G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png](screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png)。它是明日施工时的美术与布局锚点,不替代真实页面和资产验收。
+- 顶部品牌使用项目原有 `static/assets/icons/brand/jiapu-seal-logo.png`;缩小后固定在左上安全区,避免与居中标题及殿宇线稿争夺视觉焦点。
+- 底部 Tabbar 仍然贴合屏幕底部;三个图标和文字整体上移至 Android 手势/三键导航安全区内,标签“家谱 / 家族 / 我的”加大,任何屏幕尺寸下不得被底边裁切。
+- 上述确认只锁定 G-01 的方向和版式。真实透明 PNG 图标、背景组合、320 × 568 小屏校验、空状态与加载状态尚未完成,因此 **G-01 仍不勾选为设计完成**。
+- 明日从 G-01 的可施工项开始:先按 D1 规范落实真实图标与背景资产、再还原页面布局;全量页面设计完成前,不恢复接口联调或 Android 打包工作。
+
+## 2026-07-12 施工记录
+
+- 已完成 G-01 独立图标资产:四个快捷入口、主操作、列表右箭头,以及三组 Tab 默认 / 选中态;全部为 `96 × 96px` RGBA PNG,四角透明,并按实际 `48px` 显示尺寸检查。
+- 已完成 G-01 首轮页面还原:朱砂祠堂红头、左上原 Logo、条件式消息圆点、宣纸与山水背景、当前家谱题签、快捷入口、已创建 / 已加入列表、空状态、加载状态和 Android 安全区 Tabbar。
+- 本页暂时使用 `data/mock.js` 的展示数据;已移除 G-01 的远程接口调用,接口联调仍按总规划暂停。
+- 已通过:`g01-visual-contract.ps1`、`compile-audit.ps1`、`uni-scss-injection.ps1`、`vue3-entry.ps1`。
+- 待完成:在已打开的 HBuilderX 中按 `360 × 800`、`320 × 568` 预览并记录无重叠、无底部裁切的证据。完成前不勾选本页“设计完成”条件。
+
+## 2026-07-12 视觉校正记录
+
+- 用户预览反馈首轮还原与确认稿差异过大。已定位为:祠堂线稿直接铺在红头导致视觉过满,且题签 / 列表使用普通圆角边框而不是确认稿的纸签资产。
+- 红头已改为纯朱砂底 + 独立 `header-hall-lineart.png` 低对比背景层(不透明度 `0.22`);Logo 放大并上移,标题与通知保持独立层级。
+- 已新增并接入 `backgrounds/genealogy-current-slip-frame.png` 与 `backgrounds/genealogy-list-slip-frame.png`;两者为透明 PNG 双金线纸签框,透明角检查通过。
+- 此次只修正可见还原偏差,不改变 G-01 的数据、路由和状态范围;仍待用户重新运行后的两组小屏预览确认。
+
+## 2026-07-12 尺寸校正记录
+
+- 原始品牌 Logo 的透明留白导致顶部显示偏小,已从同一原图无损裁切为 `brand/jiapu-seal-logo-header.png` 后接入;不改变 Logo 的图形、配色或品牌含义。
+- 快捷入口图标由 `52rpx` 调整为 `68rpx`;Tab 图标由 `48rpx` 调整为 `56rpx`,标签由 `24rpx` 调整为 `28rpx`,使实际显示尺寸靠近确认稿。
+- 校正后已重新通过 G-01 资产/页面契约、编译审计和 SCSS 注入审计;仍需 HBuilderX 截图作最终视觉依据。
+
+## 2026-07-12 第三轮背景与栅格校正
+
+- 红头高度由 `216rpx` 收至 `184rpx`,改用 `backgrounds/header-cinnabar-texture-v2.jpg`;祠堂线稿仅作为 `0.16` 不透明度的独立底纹层。
+- 内容左右边距由 `32rpx` 调整为 `48rpx`,当前家谱题签高度收至 `250rpx`,并在快捷入口和列表之间恢复 `backgrounds/genealogy-section-divider-v2.png` 金线云纹分隔。
+- 页面底色改用浅宣纸 `backgrounds/paper-rice-texture-v2.jpg`;山水竹影改用透明 `backgrounds/footer-mountain-bamboo-v2.png` 作为整页底部淡层,不再出现矩形背景块;已检查不含绿色像素。
+- 标题、分区标题与家谱名称增加宋/楷书本机回退链;题签框统一压为古金 `#B58A4B`。
+- 已通过:G-01 页面/资产契约、编译审计、SCSS 注入审计及页脚透明区域检查。仍待新截图比对,页面不标记为完成。
+
+## 2026-07-12 第四轮底图与题签边角校正
+
+- 根因核验:`genealogy-current-slip-frame.png` 与 `genealogy-list-slip-frame.png` 的四个角均为 Alpha 0;白色直角边并非 PNG 资产本身,而是题签容器在透明镂空角下铺了接近白色的矩形底色。
+- 已移除当前家谱题签和两条列表题签的矩形底色,让透明镂空角直接透出宣纸底纹;文字、边框和点击区域不变。
+- 已撤回错误的 `footer-mountain-bamboo-v2.png`(该图是连续实心灰山,无法还原设计稿的淡墨层次),恢复使用带亭、远山和竹影的 `footer-mountain-bamboo.png`,以 `0.48` 透明度贴在 Tabbar 上沿,不形成独立灰色矩形带。
+- `tests/g01-visual-contract.ps1` 先新增回归断言并确认旧实现失败,再完成最小改动后通过;仍需在 HBuilderX 的同一设备尺寸下由新截图确认最终观感,G-01 不标记为完成。
+
+## 2026-07-12 第五轮信息层级与谱印施工
+
+- 施工依据固定为 `screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png`,对应用户箭头指出的谱印、元信息、快捷入口、两条列表和新建按钮;未改接口、路由、页头、背景或 Tabbar。
+- 新增并接入透明 PNG:`common/location-v1.png`、`common/member-meta-v1.png`、`common/admin-v1.png`、`genealogy/seal-current-frame-v1.png`、`genealogy/seal-row-frame-v1.png`、`action/create-cloud-v1.png`。谱印框只承载朱砂和金白边框,谱印固定显示“家谱”,家谱名称和数值仍为动态文本。
+- 当前家谱改为地点、成员数、管理员三组图标化元信息;快捷入口显示框提升至 `82rpx`;新建按钮加入两侧云纹 PNG。
+- 家谱列表改为谱印框、标题、地点/成员元信息和“更新于”日期的两层结构;`320px` 小屏隐藏更新时间,优先保证标题、元信息、角色和箭头不重叠。
+- 已新增资产透明角/有效图形审计。验证通过:`g01-visual-contract.ps1`、`g01-asset-alpha-audit.ps1`、`compile-audit.ps1`、`uni-scss-injection.ps1`。待 HBuilderX 新截图与参考稿并排确认后,G-01 才能标记设计完成。
+
+## 2026-07-12 第六轮参考稿字段与右侧基线校正
+
+- 预览字段已切换为参考稿:当前家谱“汤氏家谱 / 河南·洛阳 / 158 位成员 / 管理员”,已加入家谱“汤氏宗谱 / 山东·济宁 / 286 位成员 / 成员”;不再使用四川、达州、刘氏等旧演示字段。
+- 当前题签恢复标题与元信息之间的细金线;短字段确保 430px 验收宽度内三组元信息同一行显示。
+- 列表谱印固定为竖排“家谱”;右侧角色与箭头改为同一水平基线;“更新于”保留为文本,移除参考稿中不存在的日历图标和对应闲置资产。
+- 新增字段/基线契约先失败后通过。完整验证再次通过:G-01 视觉契约、PNG 透明角审计、编译审计、SCSS 注入审计、Vue 3 入口审计。仍待用户提供 HBuilderX 新截图完成视觉验收。
diff --git a/docs/design/references/G01-我的家谱-美术锚点.png b/docs/design/references/G01-我的家谱-美术锚点.png
new file mode 100644
index 0000000..eeb72df
Binary files /dev/null and b/docs/design/references/G01-我的家谱-美术锚点.png differ
diff --git a/docs/design/references/公共操作图标-风格锚点.png b/docs/design/references/公共操作图标-风格锚点.png
new file mode 100644
index 0000000..9983776
Binary files /dev/null and b/docs/design/references/公共操作图标-风格锚点.png differ
diff --git a/docs/design/screens/G01-我的家谱-紧凑版设计稿-v1.png b/docs/design/screens/G01-我的家谱-紧凑版设计稿-v1.png
new file mode 100644
index 0000000..bc7f846
Binary files /dev/null and b/docs/design/screens/G01-我的家谱-紧凑版设计稿-v1.png differ
diff --git a/docs/design/screens/G01-我的家谱-紧凑版设计稿-v2-logo.png b/docs/design/screens/G01-我的家谱-紧凑版设计稿-v2-logo.png
new file mode 100644
index 0000000..ad01dff
Binary files /dev/null and b/docs/design/screens/G01-我的家谱-紧凑版设计稿-v2-logo.png differ
diff --git a/docs/design/screens/G01-我的家谱-紧凑版设计稿-v3-小Logo.png b/docs/design/screens/G01-我的家谱-紧凑版设计稿-v3-小Logo.png
new file mode 100644
index 0000000..661b808
Binary files /dev/null and b/docs/design/screens/G01-我的家谱-紧凑版设计稿-v3-小Logo.png differ
diff --git a/docs/design/screens/G01-我的家谱-紧凑版设计稿-v4-左上Logo.png b/docs/design/screens/G01-我的家谱-紧凑版设计稿-v4-左上Logo.png
new file mode 100644
index 0000000..0a8fe42
Binary files /dev/null and b/docs/design/screens/G01-我的家谱-紧凑版设计稿-v4-左上Logo.png differ
diff --git a/docs/design/screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png b/docs/design/screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png
new file mode 100644
index 0000000..e25f14c
Binary files /dev/null and b/docs/design/screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png differ
diff --git a/docs/superpowers/plans/2026-07-11-real-api-core-flow.md b/docs/superpowers/plans/2026-07-11-real-api-core-flow.md
new file mode 100644
index 0000000..741d462
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-11-real-api-core-flow.md
@@ -0,0 +1,62 @@
+# 真实接口核心流程 Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 让家谱 App 的认证、当前家谱上下文、创建首代和世系浏览形成可验证的 Mock/远端双模式核心闭环。
+
+**Architecture:** `utils/config.js` 显式决定数据模式;`utils/api.js` 是唯一远端响应解包和错误转换位置;`utils/session.js` 与新增的上下文模块持久化 token 和当前家谱 ID。页面只调用 `appApi` 并从路由读取对象 ID。
+
+**Tech Stack:** uni-app Vue 3、HBuilderX、uni.request、PowerShell 合同测试。
+
+## Global Constraints
+
+- 不将测试账号、密码或 token 写入源码、测试或文档。
+- 远端模式失败必须显示错误,不能自动返回 Mock 数据。
+- 所有新增页面必须登记在 `pages.json` 并纳入编译审计。
+
+---
+
+### Task 1: API 会话与响应契约
+
+**Files:**
+- Modify: `utils/config.js`, `utils/session.js`, `utils/api.js`
+- Create: `utils/genealogy-context.js`, `tests/core-flow-contract.ps1`
+
+- [x] 写失败测试,要求存在显式模式、登录方法、`data` 解包、token 与当前家谱 ID 持久化。
+- [x] 运行 PowerShell 合同测试并确认因方法缺失失败。
+- [x] 实现最小 API 适配层和上下文模块。
+- [x] 重跑合同测试,确认通过。
+
+### Task 2: 认证与我的家谱
+
+**Files:**
+- Modify: `pages/auth/login.vue`, `pages/genealogy/index.vue`, `components/GenealogyCard.vue`
+- Test: `tests/core-flow-contract.ps1`
+
+- [x] 写失败测试,要求登录页调用 API、保存 token,家谱卡片和列表使用真实 ID。
+- [x] 运行测试确认失败。
+- [x] 实现密码登录、错误状态与列表加载状态。
+- [ ] 重跑测试并在 Mock 模式下手动走通登录至列表。
+
+### Task 3: 当前家谱与首代录入
+
+**Files:**
+- Modify: `pages.json`, `pages/genealogy/create.vue`, `pages/genealogy/detail.vue`, `pages/tree/index.vue`, `pages/member/detail.vue`
+- Create: `pages/lineage/first-person.vue`
+- Test: `tests/core-flow-contract.ps1`
+
+- [x] 写失败测试,要求新页面、路由参数和 API 方法存在,且不得写死 `1001`/`3`。
+- [x] 运行测试确认失败。
+- [x] 实现创建家谱后录入首代、按路由 ID 加载详情/树/成员。
+- [x] 重跑合同测试与 HBuilderX Sass/语言诊断。
+
+### Task 4: 加入审核与通知的上下文修复
+
+**Files:**
+- Modify: `pages/genealogy/search.vue`, `pages/genealogy/applications.vue`, `pages/notification/index.vue`, `utils/api.js`
+- Test: `tests/core-flow-contract.ps1`
+
+- [x] 写失败测试,要求申请和审核使用当前家谱 ID,通知读取操作有对应 API 方法。
+- [x] 运行测试确认失败。
+- [x] 实现上下文传递与明确的本地状态更新。
+- [x] 重跑所有项目测试及 HBuilderX 实际样式/模块请求验证。
diff --git a/docs/superpowers/plans/2026-07-12-g01-information-hierarchy.md b/docs/superpowers/plans/2026-07-12-g01-information-hierarchy.md
new file mode 100644
index 0000000..8c33dba
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-12-g01-information-hierarchy.md
@@ -0,0 +1,230 @@
+# G01 信息层级与谱印校正 Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 让“我的家谱”内容区按已确认的 v5 参考稿呈现谱印、元信息、快捷入口、列表和新建按钮的视觉层级。
+
+**Architecture:** 视觉资产只放在 `static/assets/icons/`,页面和卡片组件只引用资产与渲染动态数据。`pages/genealogy/index.vue` 负责当前家谱、快捷入口和新建按钮;`components/GenealogyCard.vue` 负责每条列表的谱印、两层元信息和右侧操作列。
+
+**Tech Stack:** uni-app Vue 3、SCSS、项目本地 RGBA PNG、PowerShell 契约审计。
+
+## Global Constraints
+
+- 视觉基准:`docs/design/screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png`。
+- Android only;继续使用 rpx、flex、小屏 `max-width: 340px` 规则,不使用 `clip-path` 或 CSS 绘图。
+- 所有新增可见图标、谱印框、云纹均为项目内透明 PNG;动态姓名/数字使用 Vue 文本,不嵌死在图片中。
+- 不改接口、路由、页头、背景、Tabbar 或其他页面;仅为现有展示用 `data/mock.js` 家谱条目补充 `updatedAt` 日期字段。
+- 工作目录不是 Git 仓库,本轮不初始化仓库、不创建提交。
+
+---
+
+### Task 1: 谱印与元信息 PNG 资产
+
+**Files:**
+- Create: `static/assets/icons/common/location-v1.png`
+- Create: `static/assets/icons/common/member-meta-v1.png`
+- Create: `static/assets/icons/common/admin-v1.png`
+- Create: `static/assets/icons/genealogy/seal-current-frame-v1.png`
+- Create: `static/assets/icons/genealogy/seal-row-frame-v1.png`
+- Create: `static/assets/icons/action/create-cloud-v1.png`
+- Modify: `tests/g01-visual-contract.ps1`
+
+**Interfaces:**
+- Produces 96×96 RGBA metadata icons, separate transparent current/list seal-frame PNGs, and a transparent cloud ornament consumed by Tasks 2–3.
+- Each asset corner alpha must be 0; the current seal frame must have taller visible bounds than the row seal frame.
+
+- [x] **Step 1: Write the failing asset contract**
+
+Add the six paths to `$requiredAssets`, then add these assertions to `tests/g01-visual-contract.ps1`:
+
+```powershell
+foreach ($asset in @('location-v1.png', 'member-meta-v1.png', 'admin-v1.png', 'seal-current-frame-v1.png', 'seal-row-frame-v1.png', 'create-cloud-v1.png')) {
+ if ($page -notmatch [regex]::Escape($asset) -and $card -notmatch [regex]::Escape($asset)) {
+ throw "G-01 does not consume $asset"
+ }
+}
+```
+
+- [x] **Step 2: Run the contract and verify it fails**
+
+Run: `powershell -ExecutionPolicy Bypass -File tests\g01-visual-contract.ps1`
+Expected: `FAIL` because the asset files and component references do not exist.
+
+- [x] **Step 3: Generate and inspect the assets**
+
+Generate one asset at a time on a flat chroma-key background, remove the chroma key to alpha, and inspect the selected PNG before adding it. Use this visual language: antique-gold single/double outline, Chinese genealogy motif, no text, no shadow, transparent canvas. For the seal frames, generate only the red-and-gold frame; do not include surname or “家谱” text.
+
+- [x] **Step 4: Verify transparent corners**
+
+Run a PowerShell `System.Drawing.Bitmap` alpha audit that checks the four canvas corners of every new PNG are 0.
+
+Expected: all six assets report `PASS ... transparent corners`.
+
+### Task 2: 当前家谱题签、快捷入口与新建按钮
+
+**Files:**
+- Modify: `pages/genealogy/index.vue`
+- Modify: `tests/g01-visual-contract.ps1`
+
+**Interfaces:**
+- Consumes the Task 1 metadata icons, `seal-current-frame-v1.png`, and `create-cloud-v1.png`.
+- Keeps `currentGenealogy` as the data owner; does not add API calls.
+
+- [x] **Step 1: Write the failing page-layout contract**
+
+Require the current title block to render three `.current-meta-item` entries and the current seal to include a `.current-seal-frame` image. Require the shortcut display size and create cloud image:
+
+```powershell
+foreach ($token in @('current-meta-item', 'current-seal-frame', 'location-v1.png', 'member-meta-v1.png', 'admin-v1.png', 'create-cloud-v1.png')) {
+ if ($page -notmatch $token) { throw "G-01 current panel is missing $token" }
+}
+if ($page -notmatch 'width:\s*82rpx') { throw 'G-01 shortcut icons remain below the reference visual size.' }
+```
+
+- [x] **Step 2: Run the contract and verify it fails**
+
+Run: `powershell -ExecutionPolicy Bypass -File tests\g01-visual-contract.ps1`
+Expected: `FAIL` because current content is still a pure text metadata row and has no seal/cloud image references.
+
+- [x] **Step 3: Implement the minimum current-panel markup**
+
+Replace the pure red `current-seal` block with an image layer and dynamic text overlay:
+
+```vue
+
+
+ 家谱
+
+```
+
+Render three icon/text metadata groups using the created `location-v1.png`, `member-meta-v1.png`, and `admin-v1.png`; retain `currentGenealogy.location` and `currentGenealogy.memberCount` as the text values.
+
+- [x] **Step 4: Implement the spacing-only CSS changes**
+
+Set shortcut image box to `82rpx`; increase label size and give the shortcut grid the reference-like vertical space. Use flex wrapping for current metadata and keep the existing `@media screen and (max-width: 340px)` behavior so no item overlaps.
+
+- [x] **Step 5: Implement the create-button ornament**
+
+Place two instances of `create-cloud-v1.png` inside `.create-action`; mirror only the right PNG with `transform: scaleX(-1)`. Keep the existing add PNG and navigation handler.
+
+- [x] **Step 6: Run the contract and verify it passes**
+
+Run: `powershell -ExecutionPolicy Bypass -File tests\g01-visual-contract.ps1`
+Expected: `PASS G-01 visual contract`.
+
+### Task 3: 家谱列表谱印与信息层级
+
+**Files:**
+- Modify: `components/GenealogyCard.vue`
+- Modify: `data/mock.js`
+- Modify: `tests/g01-visual-contract.ps1`
+
+**Interfaces:**
+- Consumes `genealogy` (`surname`, `name`, `location`, `memberCount`, `updatedAt`), `role`, and the Task 1 row seal asset.
+- Emits the unchanged `select` event.
+
+- [x] **Step 1: Write the failing card-layout contract**
+
+Require `seal-row-frame-v1.png` and an explicit `.card-detail-row`:
+
+```powershell
+foreach ($token in @('seal-row-frame-v1.png', 'card-detail-row', 'card-updated')) {
+ if ($card -notmatch $token) { throw "G-01 list card is missing $token" }
+}
+if ($card -match 'background:\s*#b93b2e') { throw 'G-01 list seal is still a plain CSS red block.' }
+```
+
+- [x] **Step 2: Run the contract and verify it fails**
+
+Run: `powershell -ExecutionPolicy Bypass -File tests\g01-visual-contract.ps1`
+Expected: `FAIL` because the current list card has no frame asset or two-level detail markup.
+
+- [x] **Step 3: Implement the minimal list-card markup**
+
+Use a `.surname-seal` image frame with text overlay, then split metadata into title/detail/update rows:
+
+```vue
+
+ {{ genealogy.location }} · {{ genealogy.memberCount }} 位成员
+ 更新于 {{ genealogy.updatedAt }}
+
+```
+
+On narrow screens, hide `.card-updated`; preserve the role and chevron column.
+
+- [x] **Step 3a: Add the visual fixture dates**
+
+Add `updatedAt: '2024-05-12'` to genealogy `1001` and `updatedAt: '2024-04-28'` to genealogy `1002` in `data/mock.js`. These fields are display-only fixtures and do not change the API contract.
+
+- [x] **Step 4: Implement CSS sizing and small-screen fallback**
+
+Increase card name/title prominence, keep row-frame as the only paper frame, set right-side role/chevron to a centered fixed column, and make `.card-updated` disappear at `max-width: 340px` before text can collide.
+
+- [x] **Step 5: Run the contract and verify it passes**
+
+Run: `powershell -ExecutionPolicy Bypass -File tests\g01-visual-contract.ps1`
+Expected: `PASS G-01 visual contract`.
+
+### Task 4: Documentation and full verification
+
+**Files:**
+- Modify: `docs/design/G01_我的家谱_设计记录.md`
+- Modify: `docs/规划.md`
+
+**Interfaces:**
+- Records only completed implementation and verification; leaves G01 unchecked until an HBuilderX comparison screenshot is accepted.
+
+- [x] **Step 1: Record the completed implementation scope**
+
+Append the new asset paths, the explicit visual reference, and the small-screen fallback to the G01 design record. Mark the implementation subtask complete in `docs/规划.md`; do not mark the full G01 design complete.
+
+- [x] **Step 2: Run focused verification**
+
+Run:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File tests\g01-visual-contract.ps1
+powershell -ExecutionPolicy Bypass -File tests\compile-audit.ps1
+powershell -ExecutionPolicy Bypass -File tests\uni-scss-injection.ps1
+```
+
+Expected: three `PASS` lines and no warnings.
+
+- [ ] **Step 3: Capture visual evidence in HBuilderX**
+
+At the same phone viewport as the supplied comparison, capture G01 and compare it side-by-side with the v5 reference. Confirm the current title/meta row, all three seals, four shortcut icon sizes, list row hierarchy, and cloud button ornament. If a mismatch remains, record it rather than marking G01 complete.
+
+## Plan self-review
+
+- Spec coverage: Tasks 1–3 implement every asset, layout, small-screen, and visual hierarchy requirement; Task 4 records and verifies it.
+- No placeholders: all new paths, CSS tokens, component classes, commands, and expected outputs are named.
+- Consistency: page uses current panel assets; card uses row seal and metadata assets; both are enforced by one G01 visual contract.
+
+### Task 5: 参考稿字段与列表基线校正
+
+**Files:**
+- Modify: `data/mock.js`
+- Modify: `pages/genealogy/index.vue`
+- Modify: `components/GenealogyCard.vue`
+- Modify: `tests/g01-visual-contract.ps1`
+
+**Interfaces:**
+- Produces reference-matched visual fixture fields without changing any remote API contract.
+- Keeps the existing navigation and card `select` event unchanged.
+
+- [x] **Step 1: Write the failing visual-fixture contract**
+
+Require the four reference names/locations/counts, `.current-info-divider`, a row-direction `.card-side`, and no `calendar-v1.png` use in the list component.
+
+- [x] **Step 2: Run the contract and verify it fails**
+
+Run: `powershell -ExecutionPolicy Bypass -File tests\g01-visual-contract.ps1`
+Expected: `FAIL` because the old demonstration fields, missing current divider, vertical role/arrow, and calendar icon are still present.
+
+- [x] **Step 3: Apply the smallest source correction**
+
+Replace only G01 mock fixture fields with the strings in the approved reference. Add the current-panel divider; render fixed vertical “家谱” list seal text; remove the list calendar icon; align `.card-role` and `.card-chevron` horizontally.
+
+- [x] **Step 4: Run focused verification**
+
+Run the G01 visual contract, PNG alpha audit, compile audit, SCSS injection audit, and Vue 3 entry audit. Keep Task 4 visual capture unchecked until the user accepts a new HBuilderX screenshot.
diff --git a/docs/superpowers/plans/2026-07-12-g01-visual-shell.md b/docs/superpowers/plans/2026-07-12-g01-visual-shell.md
new file mode 100644
index 0000000..ca44576
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-12-g01-visual-shell.md
@@ -0,0 +1,221 @@
+# G-01 Visual Shell Implementation Plan
+
+> For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox syntax for tracking.
+
+**Goal:** Rebuild the Android “我的家谱” G-01 page as the approved 家祠卷轴 design with verified local PNG assets and stable mock presentation data.
+
+**Architecture:** pages/genealogy/index.vue owns local visual states only. It composes a heritage header, paper-slip list items, and safe bottom navigation; it does not make remote API calls during this visual-construction phase.
+
+**Tech Stack:** uni-app, Vue 3 script setup, SCSS/rpx, local RGBA PNG/JPG assets, PowerShell checks.
+
+## Global Constraints
+
+- Android only; verify 360 × 800 and 320 × 568 without overlap.
+- No API, authentication, packaging, dependency, or unrelated-flow changes.
+- Every functional icon is a local RGBA PNG under static/assets/icons/.
+- Header uses the confirmed original brand/jiapu-seal-logo.png in the left safe area; title is centered; notification dot renders only for unread messages.
+- Tabbar is fixed at the bottom, has 64dp content plus Android navigation inset, and keeps the icon and label clear of the lower edge.
+- Do not mark G-01 design complete until assets, normal/empty/loading states, and small-screen checks have evidence.
+
+## File Responsibilities
+
+| File | Responsibility |
+| --- | --- |
+| static/assets/icons/genealogy/*-v2.png | Four compact antique-gold G-01 shortcut icons. |
+| static/assets/icons/action/add-v2.png | The compact 新建家谱 icon. |
+| static/assets/icons/common/chevron-right-v2.png | Compact right-arrow asset for the paper-slip list row. |
+| static/assets/icons/tab/*-v4.png | Default and active 24dp tab icon pairs. |
+| components/PageHeader.vue | Heritage header, original Logo, notification PNG and optional dot. |
+| components/AppTabbar.vue | Fixed Android-safe three-tab navigation. |
+| components/GenealogyCard.vue | Compact paper-slip genealogy list row. |
+| pages/genealogy/index.vue | G-01 content, local state and route actions. |
+| tests/g01-visual-contract.ps1 | Asset and static page-contract checks. |
+
+## Task 1: Produce and register G-01 visual assets
+
+**Files:**
+- Create: static/assets/icons/genealogy/tree-v2.png
+- Create: static/assets/icons/genealogy/members-v2.png
+- Create: static/assets/icons/genealogy/generation-poem-v2.png
+- Create: static/assets/icons/genealogy/application-v2.png
+- Create: static/assets/icons/action/add-v2.png
+- Create: static/assets/icons/common/chevron-right-v2.png
+- Create: static/assets/icons/tab/{genealogy,family,profile}-v4.png
+- Create: static/assets/icons/tab/{genealogy,family,profile}-active-v4.png
+- Create: tests/g01-visual-contract.ps1
+- Modify: docs/design/G01_我的家谱_设计记录.md
+
+**Interfaces:**
+- Consumes: D1 icon rules and the G-01 asset table.
+- Produces: the listed asset paths; no legacy static/icons/tab-*.png consumer remains.
+
+- [x] **Step 1: Write the failing asset contract**
+
+ $root = Split-Path -Parent $PSScriptRoot
+ $requiredAssets = @(
+ 'static/assets/icons/brand/jiapu-seal-logo.png',
+ 'static/assets/icons/common/notice-v3.png',
+ 'static/assets/icons/genealogy/tree-v2.png',
+ 'static/assets/icons/genealogy/members-v2.png',
+ 'static/assets/icons/genealogy/generation-poem-v2.png',
+ 'static/assets/icons/genealogy/application-v2.png',
+ 'static/assets/icons/action/add-v2.png',
+ 'static/assets/icons/common/chevron-right-v2.png',
+ 'static/assets/icons/tab/genealogy-v4.png',
+ 'static/assets/icons/tab/genealogy-active-v4.png',
+ 'static/assets/icons/tab/family-v4.png',
+ 'static/assets/icons/tab/family-active-v4.png',
+ 'static/assets/icons/tab/profile-v4.png',
+ 'static/assets/icons/tab/profile-active-v4.png'
+ )
+ foreach ($asset in $requiredAssets) {
+ if (-not (Test-Path -LiteralPath (Join-Path $root $asset))) {
+ throw "Missing G-01 visual asset: $asset"
+ }
+ }
+ Write-Output 'PASS G-01 visual contract'
+
+- [x] **Step 2: Run the contract and confirm it fails**
+
+Run: powershell -ExecutionPolicy Bypass -File C:\Users\Rain\Desktop\job\jiapuapp\tests\g01-visual-contract.ps1
+
+Expected: an error that names tree-v2.png as missing.
+
+- [x] **Step 3: Generate and validate every icon**
+
+Generate each asset separately as a tight 96 × 96px transparent PNG. Shortcut and add icons use antique-gold #B58A4B with the approved restrained double-line style. Tab pairs use the same 24dp silhouette in default ink #342A24 and selected cinnabar #A33B2B; no text, clouds, leaves, shadows, or excess transparent padding.
+
+- [x] **Step 4: Inspect and register accepted assets**
+
+Inspect Tabs at 46rpx and shortcuts at 48rpx. Reject opaque corners, excess canvas padding, unrecognizable semantics, and any default/selected silhouette mismatch. Update the G-01 asset table with actual path, 96 × 96px, transparency result, and display-size result. Do not tick page-completion boxes yet.
+
+- [x] **Step 5: Re-run the asset contract**
+
+Expected: PASS G-01 visual contract.
+
+## Task 2: Rebuild header and Android-safe Tabbar
+
+**Files:**
+- Modify: components/PageHeader.vue
+- Modify: components/AppTabbar.vue
+- Modify: tests/g01-visual-contract.ps1
+
+**Interfaces:**
+- Produces: PageHeader title=我的家谱 with unreadCount, and AppTabbar active=genealogy.
+
+- [x] **Step 1: Extend the failing static contract**
+
+ $header = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/PageHeader.vue')
+ $tabbar = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/AppTabbar.vue')
+ if ($header -notmatch 'jiapu-seal-logo\.png') { throw 'Header does not use the approved brand PNG.' }
+ if ($header -notmatch 'notice-v3\.png') { throw 'Header does not use the approved notice PNG.' }
+ if ($header -notmatch 'unreadCount\s*>\s*0') { throw 'Header badge is not conditional on unread count.' }
+ if ($tabbar -match '/static/icons/tab-') { throw 'Tabbar still references rejected legacy tab assets.' }
+ if ($tabbar -notmatch 'safe-area-inset-bottom') { throw 'Tabbar has no Android safe-area padding.' }
+ if ($tabbar -notmatch 'font-size:\s*24rpx') { throw 'Tabbar label size is below the approved G-01 size.' }
+
+- [x] **Step 2: Confirm the contract fails before components change**
+
+Run the focused contract.
+
+Expected: failure describing the legacy header or Tabbar implementation.
+
+- [x] **Step 3: Implement the header**
+
+Use image-only controls: an 88rpx Logo touch area, centered title, and equal-width 88rpx notice touch area using notice-v3.png. Add v-if unreadCount > 0 for the cinnabar dot. Use the header line-art PNG over a cinnabar base. Do not render text labels for Logo or notification action.
+
+- [x] **Step 4: Implement the Tabbar**
+
+Map every item to its v4 default/active PNG paths and choose by active === item.key. Keep a paper-white fixed bar, visible content height 128rpx, padding-bottom env(safe-area-inset-bottom), icon 48rpx, label 24rpx, and each touch target at least 96rpx. Do not use gap.
+
+- [x] **Step 5: Re-run the contract**
+
+Expected: PASS G-01 visual contract.
+
+## Task 3: Rebuild G-01 content and presentation states
+
+**Files:**
+- Modify: components/GenealogyCard.vue
+- Modify: pages/genealogy/index.vue
+- Modify: tests/g01-visual-contract.ps1
+
+**Interfaces:**
+- Consumes: genealogies and notifications from @/data/mock.js.
+- Produces: isLoading, hasGenealogies, createdGenealogies, and joinedGenealogies visual branches.
+
+- [x] **Step 1: Add the failing local-state contract**
+
+ $page = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/genealogy/index.vue')
+ if ($page -match "from '@/utils/api\.js'") { throw 'G-01 visual phase must not call the remote API.' }
+ foreach ($state in @('isLoading', 'hasGenealogies', 'createdGenealogies', 'joinedGenealogies')) {
+ if ($page -notmatch $state) { throw "G-01 is missing presentation state: $state" }
+ }
+ foreach ($asset in @('tree-v2.png', 'members-v2.png', 'generation-poem-v2.png', 'application-v2.png', 'add-v2.png')) {
+ if ($page -notmatch [regex]::Escape($asset)) { throw "G-01 does not consume $asset" }
+ }
+
+- [x] **Step 2: Confirm the contract fails on the legacy page**
+
+Expected: failure for the API import or missing visual state.
+
+- [x] **Step 3: Implement the page state owner**
+
+Remove appApi, genealogyContext, and the mounted API call. Import genealogies and notifications from @/data/mock.js, then define:
+
+ const isLoading = ref(false)
+ const list = ref(genealogies)
+ const hasGenealogies = computed(() => list.value.length > 0)
+ const createdGenealogies = computed(() => list.value.filter((item, index) => index === 0))
+ const joinedGenealogies = computed(() => list.value.filter((item, index) => index > 0))
+
+Retain only route navigation: tree /pages/tree/index, members /pages/genealogy/detail, generation poem /pages/content/list?type=poem, applications /pages/genealogy/applications, create /pages/genealogy/create, search /pages/genealogy/search, and notifications /pages/notification/index.
+
+- [x] **Step 4: Implement approved compact layout**
+
+Compose heritage header, current-genealogy paper slip, four equal shortcut entries with v2 assets, separate 我创建的 / 我加入的 groups, compact paper-slip rows, and the add button. Implement a distinct loading state and an empty paper/scroll state with 新建家谱 and 搜索并申请加入. Apply paper texture to the page and mountain/bamboo only to the lower content region. Add bottom content padding that clears the fixed Tabbar.
+
+- [x] **Step 5: Simplify GenealogyCard.vue**
+
+Use a seal-style surname block, title, short location/member metadata, identity label, and common/chevron-right-v2.png. Remove the legacy navy cover, motto, and active-member count. Ensure long copy uses min-width: 0, overflow: hidden, and text-overflow: ellipsis.
+
+- [x] **Step 6: Re-run the focused contract**
+
+Expected: PASS G-01 visual contract.
+
+## Task 4: Verify and update the project record
+
+**Files:**
+- Modify: docs/design/G01_我的家谱_设计记录.md
+- Modify: docs/规划.md
+- Test: tests/g01-visual-contract.ps1
+- Test: tests/compile-audit.ps1
+- Test: tests/uni-scss-injection.ps1
+
+- [x] **Step 1: Run the automated checks**
+
+Run:
+
+ powershell -ExecutionPolicy Bypass -File C:\Users\Rain\Desktop\job\jiapuapp\tests\g01-visual-contract.ps1
+ powershell -ExecutionPolicy Bypass -File C:\Users\Rain\Desktop\job\jiapuapp\tests\compile-audit.ps1
+ powershell -ExecutionPolicy Bypass -File C:\Users\Rain\Desktop\job\jiapuapp\tests\uni-scss-injection.ps1
+
+Expected: each command prints its PASS result.
+
+- [ ] **Step 2: Verify at Android reference sizes**
+
+Inspect 360 × 800 and 320 × 568. Confirm no overlap among Logo, title, and bell; notice dot only appears with unread entries; shortcut labels remain one line; list text truncates instead of overlaps; create action clears the Tabbar; all three Tab labels remain fully visible above system navigation.
+
+- [ ] **Step 3: Update only verified design state**
+
+In the G-01 record, tick only conditions backed by Tasks 1–2 and the two viewport checks. In the overall planning document, leave G-01 unchecked if even one state, asset, or viewport condition remains incomplete; otherwise record visual shell implemented and verified.
+
+- [ ] **Step 4: Commit only when Git becomes available**
+
+If a Git worktree exists, commit changed visual-shell files with message: feat: build G01 genealogy visual shell. If Git remains unavailable, report that no commit was created and do not initialize a repository.
+
+## Plan Self-Review
+
+- Task 1 covers local assets and transparency checks.
+- Task 2 covers the Logo, conditional notification dot, and safe Tabbar.
+- Task 3 covers G-01 normal, loading, and empty states plus approved compact layout.
+- Task 4 requires automated and small-screen verification before any completion status changes.
diff --git a/docs/superpowers/specs/2026-07-11-real-api-core-flow-design.md b/docs/superpowers/specs/2026-07-11-real-api-core-flow-design.md
new file mode 100644
index 0000000..f0c3f94
--- /dev/null
+++ b/docs/superpowers/specs/2026-07-11-real-api-core-flow-design.md
@@ -0,0 +1,29 @@
+# 真实接口核心流程设计
+
+## 目标
+
+把现有静态界面升级为可在 Mock 和远端接口之间明确切换的核心流程:登录后进入我的家谱,按当前家谱 ID 查看详情、世系树和成员,并完成创建家谱后录入首代人物。
+
+## 方案选择
+
+- 采用显式 `mode: 'mock' | 'remote'` 配置;远端请求失败时显示错误,不自动切回 Mock。
+- 认证支持密码登录和短信登录。密码登录用于已有测试账号;短信入口按 OpenAPI 请求验证码和登录。
+- API 适配层统一解包 OpenAPI 的 `{ code, msg, data }` 响应,统一保存/清除 token,并在 401/403 时提示重新登录。
+- 当前家谱 ID 以页面参数为主、持久化上下文为辅。详情、树、成员、审核、家族动态不得再写死 `1001`。
+
+## 范围
+
+本阶段完成认证、会话、家谱上下文、创建家谱、录入首代、详情、世系树、成员档案的真实数据链路。
+
+本阶段不实现谱文、相册、祭祀、VIP、帮助、反馈等内容模块;这些入口继续明确提示“后续开放”。
+
+## 验收
+
+1. Mock 模式下可完成:登录、创建家谱、录入首代、进入对应世系树、打开对应成员档案。
+2. Remote 模式下所有请求携带 `clientid` 与已保存 token,并按接口响应的 `data` 字段消费。
+3. 远端登录/接口失败时不会跳转成功、不会静默返回 Mock 数据,并能给出可理解提示。
+4. 页面跳转不再依赖硬编码家谱 ID 或成员 ID。
+
+## 已知外部限制
+
+当前远端密码登录探测返回 HTTP 500,前端会保留远端模式和错误提示,但无法在服务端恢复前完成真实账号的端到端验收。
diff --git a/docs/superpowers/specs/2026-07-12-g01-reference-typography-design.md b/docs/superpowers/specs/2026-07-12-g01-reference-typography-design.md
new file mode 100644
index 0000000..1b6d8e3
--- /dev/null
+++ b/docs/superpowers/specs/2026-07-12-g01-reference-typography-design.md
@@ -0,0 +1,62 @@
+# G01 参考稿信息层级校正设计
+
+## 已确认的目标
+
+- 以 `docs/design/screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png` 为唯一视觉基准;不再以当前浏览器页为准缩放设计。
+- 本轮只校正用户箭头指出的内容区:当前家谱题签的信息层级、四个快捷入口、分隔线之后的两条列表和“新建家谱”按钮。
+- 保留已确认的朱砂页头、宣纸底纹、淡墨山水和底部 Tabbar;不接入接口、不改路由;仅为现有展示用 `data/mock.js` 家谱条目补充参考稿需要的 `updatedAt` 日期字段。
+- 预览阶段的展示字段必须与参考稿一致:当前家谱为“汤氏家谱 / 河南·洛阳 / 158 位成员 / 管理员”,已加入家谱为“汤氏宗谱 / 山东·济宁 / 286 位成员 / 成员”。视觉验收期间不得保留四川、达州、刘氏等旧演示字段。
+
+## 方案比较与选择
+
+1. 仅增大现有 CSS 字号和图标容器:改动少,但仍缺地点/成员/管理员图标和更新时间层级,不能还原参考稿。
+2. 同时压缩所有内容区:会牺牲旧安卓小屏可读性,也无法解决信息结构错误。
+3. **按参考稿重建四个内容区的信息栅格**:补齐独立 PNG 元信息图标,放大可见图标和标题字号,使用两行列表元信息与右侧角色列。选此方案。
+
+## 页面规格
+
+### 当前家谱题签
+
+- 保持纸签框资产和透明四角;名称为最高层级,使用楷体回退链。
+- 左侧“家谱”不是普通红色圆角块:改为独立的竖式朱砂谱印框 PNG,包含参考稿的双层金白描边与红印底;谱印固定显示“家谱”,家谱名称和数值仍由页面数据渲染,不嵌死在图片中。
+- 当前家谱谱印比列表谱印更高;两者分别使用对应尺寸的 PNG 框,保持参考稿的层级,不能共用一个拉伸框。
+- 元信息改为三组:地点、成员数、管理员;每组由 24dp 的透明 PNG 图标与文字构成,不能再只用文字串联。
+- 三组在可用宽度内均匀排列;320px 小屏可换行但不重叠。
+- 名称与三组元信息之间保留参考稿中的细金线分隔;在 430px 验收宽度三组必须同一行。
+
+### 快捷入口与分隔线
+
+- 快捷图标的显示框放大,使实际图形接近参考稿的视觉面积;标签同步提升,四项保持等宽。
+- 快捷区与金线云纹分隔线保留明确留白,分区标题不得贴线。
+
+### 家谱列表
+
+- 每条维持纸签框和透明四角;标题提升为主要信息。
+- 左侧使用较小的竖式朱砂谱印框 PNG;已加入家谱可保持低饱和底色,但边框、比例与“我创建的”谱印一致。
+- 次行固定为“地点 + 成员数”;右侧固定为“角色 + 箭头”,并在宽屏下补充“更新于日期”。
+- 小屏降级时优先保留标题、地点、成员数和角色;更新时间可隐藏,禁止文字压到箭头。
+- 角色文字与右箭头使用同一水平基线;日期是文本信息,不增加参考稿中没有的日历图标。
+- 列表谱印固定显示竖排“家谱”,不显示动态姓氏;已加入列表使用低饱和谱印状态。
+
+### 新建按钮
+
+- 保留独立 PNG 加号;放大文字和图标实际显示尺寸。
+- 补齐参考稿的左右云纹装饰 PNG,不用 CSS 或文字模拟装饰。
+
+## 资产与兼容性
+
+- 新增地点、成员、管理员、更新时间和按钮云纹均为项目内 RGBA PNG;图标画布为 96px,并检查四角透明。
+- 不用 `clip-path`、蒙版或其他旧 Android WebView 风险较高的裁切方案。
+- 所有布局继续使用 rpx、弹性布局、单行省略和 `max-width: 340px` 小屏规则。
+
+## 验收与验证
+
+1. 自动契约先失败:要求新元信息图标、两行列表结构、放大的快捷图标和新建按钮云纹存在;旧的纯文字元信息实现必须不再通过。
+2. 完成最小代码与 PNG 资产改动后,运行 G01 契约、编译审计、SCSS 注入审计和 PNG 透明角检查。
+3. 使用 HBuilderX 在同一设备尺寸重新截图,与参考稿并排比对:重点确认箭头所指的字号、间距、图标面积、列表行高与按钮装饰。
+
+## 自检
+
+- 无占位项、无接口或路由扩展。
+- 唯一视觉来源是已确认的 G01 v5 参考稿;本轮没有重设颜色、背景或页面功能。
+- 若 320px 宽度装不下,按本文件定义隐藏更新时间,而不是压缩全部文字。
diff --git a/docs/交接记录.md b/docs/交接记录.md
new file mode 100644
index 0000000..6e3b312
--- /dev/null
+++ b/docs/交接记录.md
@@ -0,0 +1,105 @@
+# 家谱 APP 交接记录
+
+> 更新时间:2026-07-13
+> 用途:更换电脑或更换 GPT 后,先读本文件即可恢复当前工作上下文。
+> 保密说明:账号、密码、令牌和其他敏感信息不记录在本文件。
+
+## 1. 新接手者先做什么
+
+按以下顺序阅读,不要跳过:
+
+1. 根目录 `AGENTS.md`:项目工作约束,要求先确认依据、最小修改、修改后验证。
+2. `docs/规划.md`:全项目范围、完成状态与推进顺序。
+3. `docs/design/D1_安卓视觉规范与页面壳.md`:Android 视觉、兼容性、PNG 资产与背景规则。
+4. `docs/design/G01_我的家谱_设计记录.md`:G01 已完成的施工与尚未验收项。
+5. `docs/superpowers/specs/2026-07-12-g01-reference-typography-design.md` 与 `docs/superpowers/plans/2026-07-12-g01-information-hierarchy.md`:G01 的已确认规格与执行记录。
+
+视觉唯一基准:
+
+- `docs/design/screens/G01-我的家谱-紧凑版设计稿-v5-Tabbar安全区.png`
+
+不要把“红色中国风”当作完成标准;G01 需要按此图逐区、逐字段、逐比例比对。
+
+## 2. 项目与范围
+
+- 项目:uni-app,目标为 Android,使用 HBuilderX 打包。
+- 当前阶段:先完成全部页面视觉设计和小屏验收;**暂不恢复接口联调、真实账号联调或 Android 打包**。
+- 当前正在处理:`G-01 我的家谱`。
+- 当前不做:iOS、PC 管理端、官网,以及新增业务接口。
+- 本地展示数据的唯一来源:`data/mock.js`;G01 当前不调用远程 API。
+- 本目录当前不是 Git 仓库;不要擅自初始化仓库、重置文件或删除既有用户文件。
+
+## 3. 用户已确认的视觉规则
+
+- 风格是“家祠卷轴”:朱砂页头、暖宣纸、古金细线、墨褐文字、淡墨山水/竹影;禁止绿色页面背景。
+- 需要使用的图标、谱印框与云纹必须是项目内的透明 PNG;不能用文字、emoji、CSS 图形或网络图片替代可见图标。
+- 参照图中的布局、字号、字段、图标可见面积和留白需要尽量一比一还原;不要只做“差不多”的国风页面。
+- 要兼容老款 Android:用 rpx、Flex、边距与基础圆角;避免 CSS Grid、`flex gap`、`clip-path`、复杂滤镜和大面积动画。
+- 每次完成一个可验收子项,更新 `docs/规划.md` 和对应设计记录;没有 HBuilderX 同尺寸截图确认时,不能勾选整个页面完成。
+
+## 4. G01 当前实际状态
+
+### 已施工并通过静态检查
+
+- 朱砂顶部、项目 Logo、条件式通知红点、宣纸底纹、淡墨底图、底部 Tabbar。
+- 当前家谱题签、四个快捷入口、分隔金线、创建/加入列表、新建按钮。
+- 透明 PNG 资产已接入:快捷入口、Tab、通知、箭头、元信息图标、谱印框和新建云纹。
+- 当前演示字段已统一为参考稿:
+ - 当前家谱:`汤氏家谱 / 河南·洛阳 / 158 位成员 / 管理员`
+ - 已加入家谱:`汤氏宗谱 / 山东·济宁 / 286 位成员 / 成员`
+- 当前题签有标题/元信息分隔线;列表右侧角色和箭头应为同一水平基线;日期为“更新于”文字,不显示参考稿中没有的日历图标。
+
+### 必须继续做的事
+
+1. 用 HBuilderX 刷新 G01,并使用与参考稿相同的手机预览尺寸截屏。
+2. 将新截图与 v5 参考稿并排比对,重点检查:
+ - 顶部家谱题签的字体、字段、分隔线和三组元信息是否同一行;
+ - 四个快捷图标的实际可见面积与文字大小;
+ - 两条列表的谱印、标题、地点/成员、日期、右侧角色和箭头基线;
+ - 新建按钮的云纹、文字、底部山水和 Tabbar 安全区。
+3. 有任何差异时,只改对应 G01 组件并重新截图;不要提前勾选 `G-01`。
+4. 最终还要按 `320 × 568` 和 `360 × 800` 做小屏检查,确认无重叠、无裁切、无底部遮挡。
+
+## 5. G01 关键文件
+
+| 作用 | 文件 |
+| --- | --- |
+| G01 页面与当前题签/快捷入口/新建按钮 | `pages/genealogy/index.vue` |
+| G01 列表项 | `components/GenealogyCard.vue` |
+| 顶部标题栏/Logo/通知点 | `components/PageHeader.vue` |
+| 底部导航 | `components/AppTabbar.vue` |
+| 展示数据 | `data/mock.js` |
+| 视觉变量 | `styles/tokens.scss`、`styles/global.scss` |
+| G01 图标 | `static/assets/icons/` |
+| G01 背景 | `static/assets/backgrounds/` |
+| G01 契约检查 | `tests/g01-visual-contract.ps1` |
+| 新 PNG 透明检查 | `tests/g01-asset-alpha-audit.ps1` |
+
+## 6. 已知技术注意项
+
+- `tests/g01-visual-contract.ps1` 用 UTF-8 Base64 形式检查中文展示字段。这是为了避免 Windows PowerShell 5.1 将无 BOM UTF-8 脚本中的中文字符串误读为乱码;不要把该段直接改回裸中文字面量。
+- `calendar-v1.png` 已因为参考稿没有该图标而删除;不要再次接入日期图标。
+- G01 的背景资产应继续使用 `footer-mountain-bamboo.png`,不要换回实心灰山的 `footer-mountain-bamboo-v2.png`。
+- 题签透明角已经过检查;若再次出现白角,优先检查题签容器是否重新加上不透明的矩形底色。
+- 所有新 PNG 在接入前都要检查:RGBA、四角透明、主体没有大量空白、显示尺寸符合 24dp/设计稿。
+
+## 7. 复核命令
+
+在项目根目录的 PowerShell 中运行:
+
+```powershell
+powershell -ExecutionPolicy Bypass -File tests\g01-visual-contract.ps1
+powershell -ExecutionPolicy Bypass -File tests\g01-asset-alpha-audit.ps1
+powershell -ExecutionPolicy Bypass -File tests\compile-audit.ps1
+powershell -ExecutionPolicy Bypass -File tests\uni-scss-injection.ps1
+powershell -ExecutionPolicy Bypass -File tests\vue3-entry.ps1
+```
+
+最近一次结果:五项均为 `PASS`。这只证明代码和资产契约通过,**不替代 HBuilderX 视觉验收**。
+
+## 8. 换电脑操作
+
+1. 完整复制整个 `jiapuapp` 项目文件夹,保留 `docs/`、`static/`、`tests/`、`data/` 和所有 Vue/SCSS 文件。
+2. 新电脑安装 HBuilderX 后,直接“打开目录”选择复制后的项目根目录。
+3. 将本文件和新的 HBuilderX 截图一起发给新的 GPT,并说明:“先读 `docs/交接记录.md` 与 `AGENTS.md`,继续 G01 的视觉验收,不要改接口。”
+4. 新 GPT 修改后,先运行第 7 节命令,再由用户在 HBuilderX 提供同尺寸截图复核。
diff --git a/docs/规划.md b/docs/规划.md
new file mode 100644
index 0000000..47874d1
--- /dev/null
+++ b/docs/规划.md
@@ -0,0 +1,207 @@
+# 规划
+
+> 电脑或 GPT 更换时,先阅读:[项目交接记录](交接记录.md)。
+
+> 状态:执行中
+> 建立日期:2026-07-11
+> 唯一适用端:Android `uni-app`(HBuilderX 打包)
+> 执行原则:**先完成全部页面设计,再开始逐页实现;每完成一项,立即在本文件勾选。**
+
+## 1. 本轮边界
+
+- 只做手机 Android 用户端;不做 iOS、H5、PC 管理端、后台管理端、官网。
+- 范围覆盖思维导图中的 Android 用户端:登录、家谱、家族、消息、我的,以及必要的空白、加载、异常、权限状态。
+- 现有 uni-app 页面与接口代码保留为临时功能骨架,但**不计入任何页面设计完成项**,也不再决定最终视觉样式。
+- 在“设计验收”完成前,暂停新增业务接口、真实账号联调和打包工作。
+- 接口暂未提供的视频、邀请码、多管理员分级等功能:本轮可完成其页面视觉和无数据状态;真实功能接入留到接口阶段。
+
+## 2. 视觉方向(已确定)
+
+### 2.1 风格名称
+
+**家祠卷轴**:喜庆而庄重。以宣纸的温度承载家族记忆,以朱砂表达礼序和重要操作,以古金表达传承;避免网红化、过度卡片化和大面积高饱和红色。
+
+### 2.2 颜色与材质
+
+| 用途 | 色彩 | 规则 |
+| --- | --- | --- |
+| 页面底色 | 宣纸暖白 `#F6F0E5` | 不使用绿色背景。 |
+| 主操作 / 选中 | 朱砂 `#A33B2B` | 仅用于主按钮、当前状态、关键提醒。 |
+| 标题 / 主要文字 | 墨褐 `#342A24` | 不使用偏绿的深色。 |
+| 细分隔 / 装饰 | 古金 `#B58A4B` | 只做细线、印记、等级,不做大色块。 |
+| 容器 | 纸白 `#FFF9EE` | 用轻边线或极浅阴影分层。 |
+| 次级文字 | 暖灰褐 `#7A6958` | 保持正文可读性。 |
+
+### 2.3 固定组件规则
+
+- 顶部为简洁的家祠题签式标题栏;可回退页面保留清晰返回区,不能让装饰挤压标题。
+- 底部导航固定为“家谱 / 家族 / 我的”,通知作为右上角入口,不单独占 Tab。
+- 家谱树中的人物使用竖式人物牌位;姓名、字辈、关系与在世状态层次清楚,不能用装饰遮挡信息。
+- 所有功能图标均使用透明背景 PNG,统一由 `static/icons/` 管理;不用网络图标字体,不混用不同画风。
+- 图片内容优先使用用户上传的真实照片;没有照片时使用统一的纸纹占位图,不使用 AI 人像。
+
+## 3. Android 老机型与小屏兼容(必须验收)
+
+### 3.1 尺寸与排版
+
+- 以 `320 × 568`、`360 × 640` 为关键小屏;`360 × 800`、`412 × 915` 为常规屏进行核对。
+- 布局使用流式纵向滚动;不以单一固定页面高度承载内容,不让底部按钮压住表单。
+- 一行中可能出现长姓名、长家谱名、长地址时必须截断或换行,不允许文字重叠。
+- 关键正文不小于 `24rpx`,主要按钮高度不小于 `88rpx`,所有可点击区域不小于约 `44dp`。
+- 双列内容在 `320px` 宽度下自动折为单列;人物树、照片墙等横向内容提供明确的横向滚动区域与边缘提示。
+- 顶部、底部均预留系统状态栏和 Android 三键/手势导航空间;无安全区能力的设备也要有保底内边距。
+
+### 3.2 性能与技术限制
+
+- 不使用全屏大尺寸背景图、视频背景、复杂滤镜、毛玻璃和持续动画。
+- 不依赖 `flex gap`、复杂 CSS Grid 或只在新 WebView 支持的视觉效果;以 Flex、边距和基础圆角为主。
+- 首屏图标为小尺寸透明 PNG;列表图片按缩略图加载,避免一次性渲染大量高清图。
+- 家谱树按层级或视区呈现,不在低端机一次渲染无边界的全部成员节点。
+- 表单、弹窗、图片预览都要可关闭、可返回,并在键盘弹起时保持提交按钮可见。
+
+### 3.3 兼容验收方式
+
+- 每个页面设计完成后,先按 `320 × 568` 检查:标题、长文案、表单、底部操作、横向内容五项均不得重叠。
+- 全部页面实现后,以 Android 真机或模拟器覆盖上述四组尺寸,记录在本文件“最终验收”中。
+- 这里的“兼容”指页面尺寸、性能和交互可用性;具体最低 Android 系统版本在最终 HBuilderX 打包配置阶段按实际 SDK 再确认。
+
+## 4. 页面设计的完成标准
+
+一页只有同时满足以下四项,才能标记为“设计完成”:
+
+1. 有明确的正常态布局、字体层级、颜色、图标、间距与操作层级。
+2. 有该页需要的空数据、加载、失败、无权限或审核中状态。
+3. 标明每个主要点击入口的去向、返回路径和确认操作。
+4. 已按 `320 × 568` 小屏规则检查,无重叠、无被底部导航遮挡的内容。
+
+## 5. 全量页面清单与完成状态
+
+### 5.1 设计基础与通用状态
+
+- [x] P-00 锁定 Android 端范围、家祠卷轴视觉方向与透明 PNG 图标规则。
+- [x] P-01 锁定老机型 / 小屏适配原则与验收尺寸。
+- [x] P-02 设计系统:颜色、字号、间距、圆角、按钮、标签、列表、表单、弹窗、空状态。见 [D1 安卓视觉规范与页面壳](design/D1_安卓视觉规范与页面壳.md)。
+- [x] P-03 公共页面壳:顶部题签、返回栏、三 Tab、右上角通知入口、安全区与底部操作栏。见 [D1 安卓视觉规范与页面壳](design/D1_安卓视觉规范与页面壳.md)。
+- [x] P-04 通用状态:首次加载、列表加载、网络失败、空数据、无权限、审核中、删除确认、操作成功提示。见 [D1 安卓视觉规范与页面壳](design/D1_安卓视觉规范与页面壳.md)。
+- [ ] P-05 生成并验收项目 Logo、导航、通知与公共操作的透明 PNG 图标资产。见 [D1 安卓视觉规范与页面壳](design/D1_安卓视觉规范与页面壳.md)。
+- [ ] P-06 生成并验收祠堂红头、宣纸纸纹、山水 / 竹影等项目内背景资产。见 [D1 安卓视觉规范与页面壳](design/D1_安卓视觉规范与页面壳.md)。
+
+### 5.2 账户与首次使用
+
+- [ ] A-01 启动 / 登录引导页。
+- [ ] A-02 账号密码登录页。
+- [ ] A-03 手机验证码登录页。
+- [ ] A-04 注册页与服务协议确认。
+- [ ] A-05 忘记密码 / 重设密码页。
+- [ ] A-06 账号受限、登录失败与未完成注册状态。
+
+### 5.3 家谱:创建、加入与管理入口
+
+- [x] G-01 信息层级与谱印施工:元信息图标、谱印框、快捷入口、列表双层信息和新建云纹已实现并通过资产/编译审计;待 HBuilderX 同尺寸截图验收,故完整 G-01 仍未勾选。
+- [ ] G-01 我的家谱列表(已创建、已加入、搜索入口、通知入口)。
+- [ ] G-02 无家谱空状态(创建家谱 / 搜索加入)。
+- [ ] G-03 创建家谱表单(名称、地域、简介、公开范围)。
+- [ ] G-04 创建成功后的始祖录入页。
+- [ ] G-05 家谱总览(名称、编号、地域、成员数、管理员、公开状态、快捷入口)。
+- [ ] G-06 公开家谱搜索与筛选页。
+- [ ] G-07 家谱搜索结果与家谱简介页。
+- [ ] G-08 申请加入表单、提交成功与审核中页。
+- [ ] G-09 我的申请记录页。
+- [ ] G-10 管理员申请审核列表与审核确认页。
+- [ ] G-11 家谱设置、公开范围与成员访问说明页。
+- [ ] G-12 字辈诗列表、详情与管理员维护页。
+
+### 5.4 世系树、成员与亲属关系
+
+- [ ] T-01 世系树总览(缩放、横向移动、分支切换、当前人物定位)。
+- [ ] T-02 世系树小屏提示与无成员 / 数据加载失败状态。
+- [ ] T-03 成员档案详情(基本资料、代际、关系、照片、人生记录入口)。
+- [ ] T-04 新增直系亲属页(父母、配偶、子女的不同上下文)。
+- [ ] T-05 编辑成员资料页。
+- [ ] T-06 编辑亲属关系页与关系冲突提示。
+- [ ] T-07 成员目录与搜索页。
+- [ ] T-08 成员去世、隐私隐藏、无编辑权限等特殊状态。
+
+### 5.5 家族:动态、谱文、相册与影像
+
+- [ ] F-01 家族首页 / 家族动态流。
+- [ ] F-02 发布动态页(文字、图片、可见范围)。
+- [ ] F-03 动态详情、评论、点赞与删除确认页。
+- [ ] F-04 谱文分类与文章列表页。
+- [ ] F-05 谱文详情页。
+- [ ] F-06 新建 / 编辑谱文页。
+- [ ] F-07 相册列表页。
+- [ ] F-08 相册详情与照片墙页。
+- [ ] F-09 图片预览、上传照片与上传失败状态。
+- [ ] F-10 视频列表、详情、发布入口与“接口尚未开放”状态。
+
+### 5.6 家族:人物、礼仪与档案
+
+- [ ] R-01 人物录列表页。
+- [ ] R-02 人物录详情与新增 / 编辑页。
+- [ ] R-03 贺礼列表页。
+- [ ] R-04 新增贺礼、贺礼详情与删除确认页。
+- [ ] R-05 礼仪活动列表(婚礼、升学、生日、其他)。
+- [ ] R-06 礼仪详情页。
+- [ ] R-07 新建 / 编辑礼仪活动页。
+- [ ] R-08 成长日志列表、详情与新增 / 编辑页。
+- [ ] R-09 人生事列表、详情与新增 / 编辑页。
+- [ ] R-10 备忘录列表、详情与新增 / 编辑页。
+- [ ] R-11 功德记录列表、详情与新增 / 编辑页。
+
+### 5.7 消息与个人中心
+
+- [ ] N-01 消息中心(生日、贺礼、礼仪、应用通知、点赞评论、推广反馈分类)。
+- [ ] N-02 消息详情、已读状态与空消息状态。
+- [ ] M-01 我的首页(头像、身份、所属家谱、快捷入口)。
+- [ ] M-02 个人资料查看 / 编辑页。
+- [ ] M-03 安全设置页。
+- [ ] M-04 修改密码页。
+- [ ] M-05 修改手机号页。
+- [ ] M-06 帮助中心与搜索页。
+- [ ] M-07 意见反馈页(文字、图片、联系方式)。
+- [ ] M-08 应用推广页。
+- [ ] M-09 VIP 权益、套餐与订单状态页。
+- [ ] M-10 关于我们、服务协议、隐私政策与退出登录确认页。
+
+## 6. 设计推进顺序(开发前必须全部完成)
+
+| 阶段 | 交付内容 | 状态 |
+| --- | --- | --- |
+| D1 | P-02 至 P-06:统一设计系统、Android 页面壳、公共图标与背景资产 | 进行中(P-02 至 P-04 已完成;P-05、P-06 资产生成中) |
+| D2 | A-01 至 A-06:账户与首次使用全套页面 | 未开始 |
+| D3 | G-01 至 G-12:家谱创建、加入、总览与管理全套页面 | 进行中(G-01 视觉壳已施工,待 HBuilderX 小屏预览验收;其余页面未开始) |
+| D4 | T-01 至 T-08:世系树、成员、关系编辑全套页面 | 未开始 |
+| D5 | F-01 至 F-10、R-01 至 R-11:家族内容全套页面 | 未开始 |
+| D6 | N-01 至 N-02、M-01 至 M-10:消息与个人中心全套页面 | 未开始 |
+| D7 | 全量小屏复核、视觉一致性复核、页面流程复核 | 未开始 |
+| D8 | 逐页实现 uni-app、接入接口、Android 打包验证 | 未开始(D7 完成后才可开始) |
+
+## 7. 页面流转主线(设计时必须保持一致)
+
+1. 登录 → 我的家谱为空 → 创建家谱 → 录入始祖 → 家谱总览 → 世系树 → 成员档案 / 新增亲属。
+2. 登录 → 搜索公开家谱 → 查看简介 → 申请加入 → 审核中 → 管理员审核 → 进入家谱总览。
+3. 家族 Tab → 动态 / 谱文 / 相册 / 人物录 / 礼仪 / 档案 → 详情 → 发布或编辑(取决于权限)。
+4. 任一页面的通知入口 → 消息中心 → 相应业务详情。
+5. 我的 → 个人资料 / 安全 / 帮助 / 反馈 / VIP / 关于与协议。
+
+## 8. 本轮执行纪律
+
+- 每次只推进一个设计阶段;阶段内的每个页面完成后,更新本文件对应复选框。
+- 未有高保真设计和小屏检查的页面,不得开始其业务开发。
+- 已确认的视觉规范是代码还原依据;若实现与设计不一致,以设计为准修正实现。
+- 参考图是视觉锚点;核心材质、层级、比例、选中效果必须逐项一比一核对,不能只保留“红色中国风”的笼统印象。
+- 设计稿不得保留无业务意义的横线、破折号或模拟字符;颜色差异必须有可见且明确的业务含义。
+- 后续每一个页面新增的功能图标,必须同步生成、保存并验收透明 PNG 实体资产;未生成资产的页面不得标记为“设计完成”。
+- 功能图标必须遵循线上成熟标准的 24dp 语义、留白、触控与选中规则,并导出为透明 PNG;项目可在此基础上保留已确认的古金双线、轻云纹收口等家祠风格。当前从大图裁切的试验资产不得引用,需按单图标标准重新导出后验收。
+- 大部分按钮、顶部操作和快捷入口图标采用已确认的古金双线、轻云纹收口风格;底部 Tab 使用简洁同色系版本。每一页必须指定并使用已生成的背景资产组合,未完成背景设计的页面不得标记为“设计完成”。
+- 全页 AI 生成图可作为页面的美术效果锚点,但不能单独作为布局验收依据;页面必须同时按固定 Android 栅格、真实数据、真实 PNG 资产制作。已生成的“我的家谱”效果图保留其朱砂、宣纸、题签、山水和庄重层级,留白、Logo、占位文案与图标标准问题按设计栅格修正后才可计入完成项。
+- 新增需求先写入本文件,再决定是否纳入本轮,避免在页面开发中临时扩张范围。
+
+## 9. 最终验收(暂未开始)
+
+- [ ] 所有 P / A / G / T / F / R / N / M 页面均有设计完成标记。
+- [ ] 全部设计在 `320 × 568` 下无内容重叠、无遮挡、无不可点操作。
+- [ ] 所有图标均为透明背景 PNG,且无绿色页面背景。
+- [ ] 页面风格与“家祠卷轴”设计一致,代码页面与确认设计逐屏比对通过。
+- [ ] 全量页面设计确认后,才解锁 uni-app 实现、接口联调和 Android 打包。
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..2483001
--- /dev/null
+++ b/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..70b8fd0
--- /dev/null
+++ b/main.js
@@ -0,0 +1,8 @@
+import App from './App.vue'
+
+import { createSSRApp } from 'vue'
+
+export function createApp() {
+ const app = createSSRApp(App)
+ return { app }
+}
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..0691a0c
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,35 @@
+{
+ "name": "家谱",
+ "appid": "__UNI__JIAPUAPP",
+ "description": "家祠卷轴风格的家谱共建应用",
+ "versionName": "1.0.0",
+ "versionCode": 100,
+ "transformPx": false,
+ "app-plus": {
+ "usingComponents": true,
+ "nvueStyleCompiler": "uni-app",
+ "compilerVersion": 3,
+ "splashscreen": {
+ "alwaysShowBeforeRender": true,
+ "waiting": true,
+ "autoclose": true,
+ "delay": 0
+ },
+ "distribute": {
+ "android": {
+ "permissions": [
+ "",
+ "",
+ ""
+ ]
+ },
+ "ios": {
+ "dSYMs": false
+ }
+ }
+ },
+ "mp-weixin": {
+ "usingComponents": true
+ },
+ "vueVersion": "3"
+}
diff --git a/pages.json b/pages.json
new file mode 100644
index 0000000..3082bc9
--- /dev/null
+++ b/pages.json
@@ -0,0 +1,66 @@
+{
+ "pages": [
+ {
+ "path": "pages/auth/login",
+ "style": { "navigationStyle": "custom", "enablePullDownRefresh": false }
+ },
+ {
+ "path": "pages/genealogy/index",
+ "style": { "navigationStyle": "custom", "enablePullDownRefresh": false }
+ },
+ {
+ "path": "pages/genealogy/create",
+ "style": { "navigationStyle": "custom" }
+ },
+ {
+ "path": "pages/genealogy/detail",
+ "style": { "navigationStyle": "custom" }
+ },
+ {
+ "path": "pages/genealogy/search",
+ "style": { "navigationStyle": "custom" }
+ },
+ {
+ "path": "pages/genealogy/applications",
+ "style": { "navigationStyle": "custom" }
+ },
+ {
+ "path": "pages/lineage/first-person",
+ "style": { "navigationStyle": "custom" }
+ },
+ {
+ "path": "pages/content/list",
+ "style": { "navigationStyle": "custom" }
+ },
+ {
+ "path": "pages/content/editor",
+ "style": { "navigationStyle": "custom" }
+ },
+ {
+ "path": "pages/tree/index",
+ "style": { "navigationStyle": "custom", "disableScroll": true }
+ },
+ {
+ "path": "pages/member/detail",
+ "style": { "navigationStyle": "custom" }
+ },
+ {
+ "path": "pages/family/index",
+ "style": { "navigationStyle": "custom" }
+ },
+ {
+ "path": "pages/profile/index",
+ "style": { "navigationStyle": "custom" }
+ },
+ {
+ "path": "pages/notification/index",
+ "style": { "navigationStyle": "custom" }
+ }
+ ],
+ "globalStyle": {
+ "navigationBarTextStyle": "black",
+ "navigationBarTitleText": "家谱",
+ "navigationBarBackgroundColor": "#F6F0E5",
+ "backgroundColor": "#F6F0E5"
+ }
+}
diff --git a/pages/auth/login.vue b/pages/auth/login.vue
new file mode 100644
index 0000000..6e90236
--- /dev/null
+++ b/pages/auth/login.vue
@@ -0,0 +1,84 @@
+
+
+
+ 谱
+ 家脉绵延 · 生生不息
+ 家谱
+ 为家族留存可传承的记忆
+
+
+
+ 登录家谱
+ 使用已注册手机号和密码登录。
+ 手机号
+
+ 密码
+
+ {{ errorMessage }}
+
+ 短信登录将在验证码服务联调后开放。
+ 登录即表示同意《用户协议》与《隐私政策》
+
+
+
+
+
+
+
diff --git a/pages/content/editor.vue b/pages/content/editor.vue
new file mode 100644
index 0000000..f69535e
--- /dev/null
+++ b/pages/content/editor.vue
@@ -0,0 +1,35 @@
+
+
+
+ 记录此刻发布后,当前家谱的成员可以在家族圈查看。
+
+
+
+
+
+
+
diff --git a/pages/content/list.vue b/pages/content/list.vue
new file mode 100644
index 0000000..ede65c7
--- /dev/null
+++ b/pages/content/list.vue
@@ -0,0 +1,42 @@
+
+
+
+ {{ config.copy }}
+ {{ item.title || item.articleTitle || item.albumName || item.ceremonyName || '家族记录' }}{{ item.summary || item.content || item.description || item.feedContent || '内容待补充' }}{{ item.date || item.createTime || '' }}暂无{{ config.title }}
+ {{ loadError }}
+
+
+
+
+
+
diff --git a/pages/family/index.vue b/pages/family/index.vue
new file mode 100644
index 0000000..d6c3410
--- /dev/null
+++ b/pages/family/index.vue
@@ -0,0 +1,60 @@
+
+
+ 家族共建家族记事发布动态
+ {{ loadError }}
+ {{ item.name }}{{ item.detail }}
+ 家族近况全部动态
+ {{ feed.type || feed.feedType || '动态' }}{{ feed.date || feed.createTime || '' }}{{ feed.title || '家族动态' }}{{ feed.content || feed.feedContent }}发布人:{{ feed.author || feed.creatorName || '族人' }}暂未发布家族动态
+
+
+
+
+
+
+
diff --git a/pages/genealogy/applications.vue b/pages/genealogy/applications.vue
new file mode 100644
index 0000000..04ccb27
--- /dev/null
+++ b/pages/genealogy/applications.vue
@@ -0,0 +1,67 @@
+
+
+
+ {{ loadError || '通过前请核实申请人的亲属关系;审核结果会通过通知告知对方。' }}
+
+
+ {{ item.name }}{{ item.phone }}{{ item.appliedAt }}
+ {{ item.relation }}
+
+ {{ item.status === 'APPROVED' ? '已通过' : '已拒绝' }}
+
+ 暂无待审核申请
+
+
+
+
+
+
+
diff --git a/pages/genealogy/create.vue b/pages/genealogy/create.vue
new file mode 100644
index 0000000..9d9dfee
--- /dev/null
+++ b/pages/genealogy/create.vue
@@ -0,0 +1,68 @@
+
+
+
+
+ 立谱信息
+ 家谱建立后可继续完善,名称与访问规则由创建者维护。
+
+ 姓氏
+
+
+
+ 谱名
+
+
+
+ 堂号
+
+
+
+ 所在地
+
+
+
+
+
+
+ 访问规则
+ 默认仅成员可见
+
+ 保护族人资料。创建者可在家谱设置中开放搜索与申请加入。
+
+
+
+
+
+
+
+
+
diff --git a/pages/genealogy/detail.vue b/pages/genealogy/detail.vue
new file mode 100644
index 0000000..fe4b43f
--- /dev/null
+++ b/pages/genealogy/detail.vue
@@ -0,0 +1,83 @@
+
+
+
+
+ {{ genealogy.hall || '堂号待补' }} · {{ genealogy.location || '所在地待补' }}
+ {{ genealogy.name }}
+ {{ genealogy.motto || '敦亲睦族,敬祖传家。' }}
+ 共 {{ genealogy.memberCount || 0 }} 人已激活 {{ genealogy.activeCount || 0 }} 人{{ genealogy.visibility || '仅成员可见' }}
+
+ {{ loadError || '正在载入家谱…' }}
+
+
+
+ 家谱核心
+
+ 世系树查看家脉关系
+ 录入族人从首代开始完善
+ 字辈谱传承行辈次序
+ 入谱审核处理加入申请
+
+
+ 家族近况进入家族圈查看动态查看
+ 邀亲人,共建家谱族人申请加入后,由创建者审核确认。
+
+
+
+
+
+
+
diff --git a/pages/genealogy/index.vue b/pages/genealogy/index.vue
new file mode 100644
index 0000000..a6e3226
--- /dev/null
+++ b/pages/genealogy/index.vue
@@ -0,0 +1,573 @@
+
+
+
+
+
+
+
+
+
+ 正在整理家谱
+ 请稍候,家族记忆正在归卷。
+
+
+
+
+
+
+
+ 家谱
+
+
+ {{ currentGenealogy.name }}
+
+
+
+
+ {{
+ currentGenealogy.location
+ }}
+
+
+
+ {{ currentGenealogy.memberCount }} 位成员
+
+
+
+ 管理员
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+ 我创建的
+
+
+
+
+ 我加入的
+
+
+
+
+
+
+ 新建家谱
+
+
+
+
+
+
+ 待立家谱
+ 从第一位家人开始
+ 为家族留下可传承的名字、故事与记忆。
+
+
+ 新建家谱
+
+ 搜索并申请加入
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/genealogy/search.vue b/pages/genealogy/search.vue
new file mode 100644
index 0000000..e54d8c5
--- /dev/null
+++ b/pages/genealogy/search.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+ 搜索
+
+ 仅展示已开放申请加入的家谱。
+
+
+ 暂未找到公开家谱可联系家谱创建者开通公开申请
+
+
+
+
+
+
+
diff --git a/pages/lineage/first-person.vue b/pages/lineage/first-person.vue
new file mode 100644
index 0000000..3f16eb1
--- /dev/null
+++ b/pages/lineage/first-person.vue
@@ -0,0 +1,60 @@
+
+
+
+
+ 家谱从这一位开始
+ 可先录入姓名、世代和简要生平,其他资料后续随时补充。
+ 姓名
+ 世代第一世
+ 出生日期
+ 生平简述
+
+
+
+
+
+
+
+
diff --git a/pages/member/detail.vue b/pages/member/detail.vue
new file mode 100644
index 0000000..85a5b53
--- /dev/null
+++ b/pages/member/detail.vue
@@ -0,0 +1,58 @@
+
+
+
+ {{ member.name.slice(0, 1) }}{{ member.name }}第 {{ member.generation }} 世 · {{ member.relation }} · 家谱成员
+ {{ loadError || '正在载入成员档案…' }}
+
+ 基本资料{{ item.label }}{{ item.value }}
+ 亲属关系亲属关系将在编辑功能接入后完整维护。
+ 生平事迹{{ member.introduction }}
+
+
+
+
+
+
+
diff --git a/pages/notification/index.vue b/pages/notification/index.vue
new file mode 100644
index 0000000..8454fa2
--- /dev/null
+++ b/pages/notification/index.vue
@@ -0,0 +1,54 @@
+
+
+
+ {{ loadError || '与家谱相关的提醒都会留在这里。' }}
+ {{ item.title }}{{ item.content }}{{ item.time }}
+
+
+
+
+
+
diff --git a/pages/profile/index.vue b/pages/profile/index.vue
new file mode 100644
index 0000000..c1bc431
--- /dev/null
+++ b/pages/profile/index.vue
@@ -0,0 +1,48 @@
+
+
+ {{ profile.name ? profile.name.slice(0, 1) : '谱' }}{{ profile.name || '家谱用户' }}{{ profile.phone || '资料加载中' }} · {{ profile.role || '家谱成员' }}资料
+ {{ loadError }}
+ 待你处理家谱相关提醒与审核通知{{ unreadCount }}
+
+
+
+
+
+
+
+
+
diff --git a/pages/tree/index.vue b/pages/tree/index.vue
new file mode 100644
index 0000000..6aac660
--- /dev/null
+++ b/pages/tree/index.vue
@@ -0,0 +1,62 @@
+
+
+
+ 当前家谱世系查找编辑
+
+
+ {{ loadError || '暂未录入族人' }}
+ {{ member.name }}{{ member.relation }}{{ member.years }}
+
+
+ {{ selected.name }}第 {{ selected.generation }} 世 · {{ selected.relation }} · {{ selected.branch }}查看资料添加亲属
+
+
+
+
+
+
diff --git a/static/assets/backgrounds/footer-mountain-bamboo-v2.png b/static/assets/backgrounds/footer-mountain-bamboo-v2.png
new file mode 100644
index 0000000..6b96be2
Binary files /dev/null and b/static/assets/backgrounds/footer-mountain-bamboo-v2.png differ
diff --git a/static/assets/backgrounds/footer-mountain-bamboo.png b/static/assets/backgrounds/footer-mountain-bamboo.png
new file mode 100644
index 0000000..bdfec0d
Binary files /dev/null and b/static/assets/backgrounds/footer-mountain-bamboo.png differ
diff --git a/static/assets/backgrounds/genealogy-current-slip-frame.png b/static/assets/backgrounds/genealogy-current-slip-frame.png
new file mode 100644
index 0000000..b0f2e6a
Binary files /dev/null and b/static/assets/backgrounds/genealogy-current-slip-frame.png differ
diff --git a/static/assets/backgrounds/genealogy-list-slip-frame.png b/static/assets/backgrounds/genealogy-list-slip-frame.png
new file mode 100644
index 0000000..508b467
Binary files /dev/null and b/static/assets/backgrounds/genealogy-list-slip-frame.png differ
diff --git a/static/assets/backgrounds/genealogy-section-divider-v2.png b/static/assets/backgrounds/genealogy-section-divider-v2.png
new file mode 100644
index 0000000..3df8d55
Binary files /dev/null and b/static/assets/backgrounds/genealogy-section-divider-v2.png differ
diff --git a/static/assets/backgrounds/header-cinnabar-texture-v2.jpg b/static/assets/backgrounds/header-cinnabar-texture-v2.jpg
new file mode 100644
index 0000000..069c5f3
Binary files /dev/null and b/static/assets/backgrounds/header-cinnabar-texture-v2.jpg differ
diff --git a/static/assets/backgrounds/header-hall-lineart.png b/static/assets/backgrounds/header-hall-lineart.png
new file mode 100644
index 0000000..53ceec5
Binary files /dev/null and b/static/assets/backgrounds/header-hall-lineart.png differ
diff --git a/static/assets/backgrounds/paper-rice-texture-v2.jpg b/static/assets/backgrounds/paper-rice-texture-v2.jpg
new file mode 100644
index 0000000..9d7413d
Binary files /dev/null and b/static/assets/backgrounds/paper-rice-texture-v2.jpg differ
diff --git a/static/assets/backgrounds/paper-rice-texture.jpg b/static/assets/backgrounds/paper-rice-texture.jpg
new file mode 100644
index 0000000..ef38390
Binary files /dev/null and b/static/assets/backgrounds/paper-rice-texture.jpg differ
diff --git a/static/assets/icons/action/add-v2.png b/static/assets/icons/action/add-v2.png
new file mode 100644
index 0000000..4573621
Binary files /dev/null and b/static/assets/icons/action/add-v2.png differ
diff --git a/static/assets/icons/action/add.png b/static/assets/icons/action/add.png
new file mode 100644
index 0000000..6e7701b
Binary files /dev/null and b/static/assets/icons/action/add.png differ
diff --git a/static/assets/icons/action/create-cloud-v1.png b/static/assets/icons/action/create-cloud-v1.png
new file mode 100644
index 0000000..81f1820
Binary files /dev/null and b/static/assets/icons/action/create-cloud-v1.png differ
diff --git a/static/assets/icons/action/delete.png b/static/assets/icons/action/delete.png
new file mode 100644
index 0000000..3299910
Binary files /dev/null and b/static/assets/icons/action/delete.png differ
diff --git a/static/assets/icons/action/edit.png b/static/assets/icons/action/edit.png
new file mode 100644
index 0000000..4ae2b4a
Binary files /dev/null and b/static/assets/icons/action/edit.png differ
diff --git a/static/assets/icons/action/setting.png b/static/assets/icons/action/setting.png
new file mode 100644
index 0000000..b8ff905
Binary files /dev/null and b/static/assets/icons/action/setting.png differ
diff --git a/static/assets/icons/action/share.png b/static/assets/icons/action/share.png
new file mode 100644
index 0000000..572b32d
Binary files /dev/null and b/static/assets/icons/action/share.png differ
diff --git a/static/assets/icons/action/upload.png b/static/assets/icons/action/upload.png
new file mode 100644
index 0000000..c53690f
Binary files /dev/null and b/static/assets/icons/action/upload.png differ
diff --git a/static/assets/icons/brand/jiapu-seal-logo-header.png b/static/assets/icons/brand/jiapu-seal-logo-header.png
new file mode 100644
index 0000000..c75c77b
Binary files /dev/null and b/static/assets/icons/brand/jiapu-seal-logo-header.png differ
diff --git a/static/assets/icons/brand/jiapu-seal-logo-source.png b/static/assets/icons/brand/jiapu-seal-logo-source.png
new file mode 100644
index 0000000..346176f
Binary files /dev/null and b/static/assets/icons/brand/jiapu-seal-logo-source.png differ
diff --git a/static/assets/icons/brand/jiapu-seal-logo.png b/static/assets/icons/brand/jiapu-seal-logo.png
new file mode 100644
index 0000000..6e77806
Binary files /dev/null and b/static/assets/icons/brand/jiapu-seal-logo.png differ
diff --git a/static/assets/icons/common/admin-v1.png b/static/assets/icons/common/admin-v1.png
new file mode 100644
index 0000000..63ba2db
Binary files /dev/null and b/static/assets/icons/common/admin-v1.png differ
diff --git a/static/assets/icons/common/back.png b/static/assets/icons/common/back.png
new file mode 100644
index 0000000..2e8796c
Binary files /dev/null and b/static/assets/icons/common/back.png differ
diff --git a/static/assets/icons/common/chevron-right-v2.png b/static/assets/icons/common/chevron-right-v2.png
new file mode 100644
index 0000000..26584c7
Binary files /dev/null and b/static/assets/icons/common/chevron-right-v2.png differ
diff --git a/static/assets/icons/common/close.png b/static/assets/icons/common/close.png
new file mode 100644
index 0000000..5293120
Binary files /dev/null and b/static/assets/icons/common/close.png differ
diff --git a/static/assets/icons/common/location-v1.png b/static/assets/icons/common/location-v1.png
new file mode 100644
index 0000000..d3c9de5
Binary files /dev/null and b/static/assets/icons/common/location-v1.png differ
diff --git a/static/assets/icons/common/member-meta-v1.png b/static/assets/icons/common/member-meta-v1.png
new file mode 100644
index 0000000..5650edd
Binary files /dev/null and b/static/assets/icons/common/member-meta-v1.png differ
diff --git a/static/assets/icons/common/more.png b/static/assets/icons/common/more.png
new file mode 100644
index 0000000..879bc9b
Binary files /dev/null and b/static/assets/icons/common/more.png differ
diff --git a/static/assets/icons/common/notice-v2.png b/static/assets/icons/common/notice-v2.png
new file mode 100644
index 0000000..95666dd
Binary files /dev/null and b/static/assets/icons/common/notice-v2.png differ
diff --git a/static/assets/icons/common/notice-v3.png b/static/assets/icons/common/notice-v3.png
new file mode 100644
index 0000000..7ccc9f5
Binary files /dev/null and b/static/assets/icons/common/notice-v3.png differ
diff --git a/static/assets/icons/common/notice.png b/static/assets/icons/common/notice.png
new file mode 100644
index 0000000..2368a00
Binary files /dev/null and b/static/assets/icons/common/notice.png differ
diff --git a/static/assets/icons/common/search.png b/static/assets/icons/common/search.png
new file mode 100644
index 0000000..ec2ae33
Binary files /dev/null and b/static/assets/icons/common/search.png differ
diff --git a/static/assets/icons/genealogy/application-v2.png b/static/assets/icons/genealogy/application-v2.png
new file mode 100644
index 0000000..20e75d5
Binary files /dev/null and b/static/assets/icons/genealogy/application-v2.png differ
diff --git a/static/assets/icons/genealogy/generation-poem-v2.png b/static/assets/icons/genealogy/generation-poem-v2.png
new file mode 100644
index 0000000..272f615
Binary files /dev/null and b/static/assets/icons/genealogy/generation-poem-v2.png differ
diff --git a/static/assets/icons/genealogy/members-v2.png b/static/assets/icons/genealogy/members-v2.png
new file mode 100644
index 0000000..5650edd
Binary files /dev/null and b/static/assets/icons/genealogy/members-v2.png differ
diff --git a/static/assets/icons/genealogy/seal-current-frame-v1.png b/static/assets/icons/genealogy/seal-current-frame-v1.png
new file mode 100644
index 0000000..7b356af
Binary files /dev/null and b/static/assets/icons/genealogy/seal-current-frame-v1.png differ
diff --git a/static/assets/icons/genealogy/seal-row-frame-v1.png b/static/assets/icons/genealogy/seal-row-frame-v1.png
new file mode 100644
index 0000000..e825789
Binary files /dev/null and b/static/assets/icons/genealogy/seal-row-frame-v1.png differ
diff --git a/static/assets/icons/genealogy/tree-v2.png b/static/assets/icons/genealogy/tree-v2.png
new file mode 100644
index 0000000..2c49ca1
Binary files /dev/null and b/static/assets/icons/genealogy/tree-v2.png differ
diff --git a/static/assets/icons/tab/family-active-v4.png b/static/assets/icons/tab/family-active-v4.png
new file mode 100644
index 0000000..9fd2df9
Binary files /dev/null and b/static/assets/icons/tab/family-active-v4.png differ
diff --git a/static/assets/icons/tab/family-active.png b/static/assets/icons/tab/family-active.png
new file mode 100644
index 0000000..ff52341
Binary files /dev/null and b/static/assets/icons/tab/family-active.png differ
diff --git a/static/assets/icons/tab/family-v4.png b/static/assets/icons/tab/family-v4.png
new file mode 100644
index 0000000..35146dd
Binary files /dev/null and b/static/assets/icons/tab/family-v4.png differ
diff --git a/static/assets/icons/tab/family.png b/static/assets/icons/tab/family.png
new file mode 100644
index 0000000..b31c797
Binary files /dev/null and b/static/assets/icons/tab/family.png differ
diff --git a/static/assets/icons/tab/genealogy-active-v3.png b/static/assets/icons/tab/genealogy-active-v3.png
new file mode 100644
index 0000000..9d100d4
Binary files /dev/null and b/static/assets/icons/tab/genealogy-active-v3.png differ
diff --git a/static/assets/icons/tab/genealogy-active-v4.png b/static/assets/icons/tab/genealogy-active-v4.png
new file mode 100644
index 0000000..68e6d72
Binary files /dev/null and b/static/assets/icons/tab/genealogy-active-v4.png differ
diff --git a/static/assets/icons/tab/genealogy-active.png b/static/assets/icons/tab/genealogy-active.png
new file mode 100644
index 0000000..119c805
Binary files /dev/null and b/static/assets/icons/tab/genealogy-active.png differ
diff --git a/static/assets/icons/tab/genealogy-v3.png b/static/assets/icons/tab/genealogy-v3.png
new file mode 100644
index 0000000..c3cec9e
Binary files /dev/null and b/static/assets/icons/tab/genealogy-v3.png differ
diff --git a/static/assets/icons/tab/genealogy-v4.png b/static/assets/icons/tab/genealogy-v4.png
new file mode 100644
index 0000000..af4abec
Binary files /dev/null and b/static/assets/icons/tab/genealogy-v4.png differ
diff --git a/static/assets/icons/tab/genealogy.png b/static/assets/icons/tab/genealogy.png
new file mode 100644
index 0000000..03b6083
Binary files /dev/null and b/static/assets/icons/tab/genealogy.png differ
diff --git a/static/assets/icons/tab/profile-active-v4.png b/static/assets/icons/tab/profile-active-v4.png
new file mode 100644
index 0000000..a09337d
Binary files /dev/null and b/static/assets/icons/tab/profile-active-v4.png differ
diff --git a/static/assets/icons/tab/profile-active.png b/static/assets/icons/tab/profile-active.png
new file mode 100644
index 0000000..6c1b2af
Binary files /dev/null and b/static/assets/icons/tab/profile-active.png differ
diff --git a/static/assets/icons/tab/profile-v4.png b/static/assets/icons/tab/profile-v4.png
new file mode 100644
index 0000000..9b9d846
Binary files /dev/null and b/static/assets/icons/tab/profile-v4.png differ
diff --git a/static/assets/icons/tab/profile.png b/static/assets/icons/tab/profile.png
new file mode 100644
index 0000000..33b4045
Binary files /dev/null and b/static/assets/icons/tab/profile.png differ
diff --git a/static/icons/tab-family.png b/static/icons/tab-family.png
new file mode 100644
index 0000000..a88905e
Binary files /dev/null and b/static/icons/tab-family.png differ
diff --git a/static/icons/tab-genealogy.png b/static/icons/tab-genealogy.png
new file mode 100644
index 0000000..ede691a
Binary files /dev/null and b/static/icons/tab-genealogy.png differ
diff --git a/static/icons/tab-profile.png b/static/icons/tab-profile.png
new file mode 100644
index 0000000..6e961e6
Binary files /dev/null and b/static/icons/tab-profile.png differ
diff --git a/styles/global.scss b/styles/global.scss
new file mode 100644
index 0000000..963e5f5
--- /dev/null
+++ b/styles/global.scss
@@ -0,0 +1,58 @@
+page {
+ background: $paper;
+ color: $ink;
+ font-family: "STKaiti", "KaiTi", serif
+}
+
+.page-shell {
+ min-height: 100vh;
+ box-sizing: border-box;
+ background: $paper;
+}
+
+.paper-card {
+ background: $paper-white;
+ border: 1rpx solid $gold-soft;
+ border-radius: 24rpx;
+ box-sizing: border-box;
+}
+
+.section-title {
+ color: $ink;
+ font-size: 34rpx;
+ font-weight: 700;
+ letter-spacing: 2rpx;
+}
+
+.muted-copy {
+ color: $ink-muted;
+ font-size: 26rpx;
+ line-height: 1.7;
+}
+
+.primary-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 92rpx;
+ border: 0;
+ border-radius: 14rpx;
+ background: $brand-red;
+ color: #fff8ec;
+ font-size: 31rpx;
+ font-weight: 700;
+ letter-spacing: 3rpx;
+}
+
+.secondary-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 88rpx;
+ border: 1rpx solid $gold;
+ border-radius: 14rpx;
+ background: transparent;
+ color: $brand-red;
+ font-size: 29rpx;
+ font-weight: 600;
+}
diff --git a/tests/compile-audit.ps1 b/tests/compile-audit.ps1
new file mode 100644
index 0000000..82d2185
--- /dev/null
+++ b/tests/compile-audit.ps1
@@ -0,0 +1,66 @@
+$ErrorActionPreference = 'Stop'
+
+$root = Split-Path -Parent $PSScriptRoot
+$errors = [System.Collections.Generic.List[string]]::new()
+
+function Test-LocalImport {
+ param(
+ [string]$SourceFile,
+ [string]$Target,
+ [string[]]$Extensions
+ )
+
+ if ($Target -match '^(https?:|sass:)') { return $true }
+ if ($Target.StartsWith('@/')) {
+ $candidate = Join-Path $root $Target.Substring(2)
+ } elseif ($Target.StartsWith('./') -or $Target.StartsWith('../')) {
+ $candidate = Join-Path (Split-Path -Parent $SourceFile) $Target
+ } else {
+ return $true
+ }
+
+ if (Test-Path -LiteralPath $candidate) { return $true }
+ foreach ($extension in $Extensions) {
+ if (Test-Path -LiteralPath ($candidate + $extension)) { return $true }
+ }
+ return $false
+}
+
+$pages = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages.json') | ConvertFrom-Json
+foreach ($page in $pages.pages) {
+ $pageFile = Join-Path $root ($page.path + '.vue')
+ if (-not (Test-Path -LiteralPath $pageFile)) {
+ $errors.Add("Route has no page file: $($page.path)")
+ }
+}
+
+$manifest = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'manifest.json') | ConvertFrom-Json
+if ($manifest.vueVersion -eq '3' -and -not (Test-Path -LiteralPath (Join-Path $root 'index.html'))) {
+ $errors.Add('Vue 3 project is missing index.html.')
+}
+
+$sourceFiles = Get-ChildItem -Path $root -Recurse -File | Where-Object {
+ $_.FullName -notmatch '\\unpackage\\' -and $_.Extension -in '.vue', '.js', '.scss'
+}
+
+foreach ($source in $sourceFiles) {
+ $content = Get-Content -Raw -Encoding UTF8 $source.FullName
+ foreach ($match in [regex]::Matches($content, '@(?:import|use|forward)\s+["''](?[^"'']+)["'']')) {
+ $target = $match.Groups['target'].Value
+ if (-not (Test-LocalImport -SourceFile $source.FullName -Target $target -Extensions @('.scss'))) {
+ $errors.Add("Missing Sass import in $($source.FullName.Substring($root.Length + 1)): $target")
+ }
+ }
+ foreach ($match in [regex]::Matches($content, '(?:from|import)\s+["''](?[^"'']+)["'']')) {
+ $target = $match.Groups['target'].Value
+ if (-not (Test-LocalImport -SourceFile $source.FullName -Target $target -Extensions @('.js', '.vue', '.json'))) {
+ $errors.Add("Missing module import in $($source.FullName.Substring($root.Length + 1)): $target")
+ }
+ }
+}
+
+if ($errors.Count -gt 0) {
+ throw "Compile audit failed:`n$($errors -join "`n")"
+}
+
+Write-Output 'PASS compile audit'
diff --git a/tests/core-flow-contract.ps1 b/tests/core-flow-contract.ps1
new file mode 100644
index 0000000..230c808
--- /dev/null
+++ b/tests/core-flow-contract.ps1
@@ -0,0 +1,114 @@
+$ErrorActionPreference = 'Stop'
+
+$root = Split-Path -Parent $PSScriptRoot
+$requiredFiles = @('utils/config.js', 'utils/session.js', 'utils/genealogy-context.js', 'utils/api.js', 'utils/md5.js', 'pages/auth/login.vue', 'pages/lineage/first-person.vue', 'pages/content/list.vue', 'pages/content/editor.vue')
+foreach ($file in $requiredFiles) {
+ if (-not (Test-Path (Join-Path $root $file))) {
+ throw "Missing core-flow file: $file"
+ }
+}
+
+$config = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'utils/config.js')
+foreach ($token in @('mode:', 'isMockMode')) {
+ if ($config -notmatch [regex]::Escape($token)) {
+ throw "Missing runtime mode contract: $token"
+ }
+}
+
+$context = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'utils/genealogy-context.js')
+foreach ($method in @('getCurrentGenealogyId', 'setCurrentGenealogyId', 'clearCurrentGenealogyId')) {
+ if ($context -notmatch [regex]::Escape($method)) {
+ throw "Missing genealogy context method: $method"
+ }
+}
+
+$api = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'utils/api.js')
+foreach ($method in @('unwrapResponse', 'sendSmsCode', 'loginWithPassword', 'loginWithSms')) {
+ if ($api -notmatch [regex]::Escape($method)) {
+ throw "Missing auth API method: $method"
+ }
+}
+
+foreach ($method in @('createPerson', 'getPerson')) {
+ if ($api -notmatch [regex]::Escape($method)) {
+ throw "Missing lineage API method: $method"
+ }
+}
+
+foreach ($method in @('getArticles', 'getAlbums', 'getCeremonies', 'getGrowthRecords', 'createFeed')) {
+ if ($api -notmatch [regex]::Escape($method)) {
+ throw "Missing family content API method: $method"
+ }
+}
+
+if ($api -notmatch 'getProfile') {
+ throw 'Missing current-user profile API method'
+}
+
+$login = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/auth/login.vue')
+foreach ($token in @('calcMD5', 'loginWithPassword', 'password', 'isSubmitting')) {
+ if ($login -notmatch [regex]::Escape($token)) {
+ throw "Missing password login behavior: $token"
+ }
+}
+
+$pages = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages.json')
+if ($pages -notmatch 'pages/lineage/first-person') {
+ throw 'Missing first-person page route'
+}
+foreach ($route in @('pages/content/list', 'pages/content/editor')) {
+ if ($pages -notmatch [regex]::Escape($route)) {
+ throw "Missing content page route: $route"
+ }
+}
+
+foreach ($pageFile in @('pages/genealogy/detail.vue', 'pages/tree/index.vue', 'pages/member/detail.vue')) {
+ $page = Get-Content -Raw -Encoding UTF8 (Join-Path $root $pageFile)
+ if ($page -match "id=1001|id=3") {
+ throw "Hard-coded route ID remains in $pageFile"
+ }
+ if ($page -notmatch 'genealogyContext') {
+ throw "Missing genealogy context in $pageFile"
+ }
+}
+
+foreach ($method in @('markNotificationRead', 'markAllNotificationsRead')) {
+ if ($api -notmatch [regex]::Escape($method)) {
+ throw "Missing notification API method: $method"
+ }
+}
+
+foreach ($pageFile in @('pages/genealogy/applications.vue', 'pages/notification/index.vue')) {
+ $page = Get-Content -Raw -Encoding UTF8 (Join-Path $root $pageFile)
+ if ($page -match '1001') {
+ throw "Hard-coded genealogy ID remains in $pageFile"
+ }
+}
+
+$applications = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/genealogy/applications.vue')
+if ($applications -notmatch 'genealogyContext') {
+ throw 'Missing genealogy context in applications page'
+}
+
+$notifications = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/notification/index.vue')
+foreach ($method in @('markNotificationRead', 'markAllNotificationsRead')) {
+ if ($notifications -notmatch [regex]::Escape($method)) {
+ throw "Notification page does not use $method"
+ }
+}
+
+$family = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/family/index.vue')
+foreach ($token in @('genealogyContext', 'openSection', 'pages/content/list', 'pages/content/editor')) {
+ if ($family -notmatch [regex]::Escape($token)) {
+ throw "Missing family content flow: $token"
+ }
+}
+
+$profile = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/profile/index.vue')
+foreach ($token in @('appApi', 'getProfile', 'profile.name')) {
+ if ($profile -notmatch [regex]::Escape($token)) {
+ throw "Missing dynamic profile behavior: $token"
+ }
+}
+
+Write-Output 'PASS core-flow session and auth contract'
diff --git a/tests/g01-asset-alpha-audit.ps1 b/tests/g01-asset-alpha-audit.ps1
new file mode 100644
index 0000000..f02e4f7
--- /dev/null
+++ b/tests/g01-asset-alpha-audit.ps1
@@ -0,0 +1,35 @@
+$ErrorActionPreference = 'Stop'
+
+$root = Split-Path -Parent $PSScriptRoot
+Add-Type -AssemblyName System.Drawing
+
+$assets = @(
+ 'static/assets/icons/common/location-v1.png',
+ 'static/assets/icons/common/member-meta-v1.png',
+ 'static/assets/icons/common/admin-v1.png',
+ 'static/assets/icons/genealogy/seal-current-frame-v1.png',
+ 'static/assets/icons/genealogy/seal-row-frame-v1.png',
+ 'static/assets/icons/action/create-cloud-v1.png'
+)
+
+foreach ($asset in $assets) {
+ $bitmap = [System.Drawing.Bitmap]::FromFile((Join-Path $root $asset))
+ $width = [int]$bitmap.Width
+ $height = [int]$bitmap.Height
+ $corners = @(
+ $bitmap.GetPixel(0, 0).A,
+ $bitmap.GetPixel(($width - 1), 0).A,
+ $bitmap.GetPixel(0, ($height - 1)).A,
+ $bitmap.GetPixel(($width - 1), ($height - 1)).A
+ )
+ $visiblePixels = 0
+ for ($y = 0; $y -lt $height; $y++) {
+ for ($x = 0; $x -lt $width; $x++) {
+ if ($bitmap.GetPixel($x, $y).A -gt 24) { $visiblePixels++ }
+ }
+ }
+ $bitmap.Dispose()
+ if (($corners | Where-Object { $_ -ne 0 }).Count -ne 0) { throw "$asset has opaque outer corners." }
+ if ($visiblePixels -lt 80) { throw "$asset has no usable visible artwork." }
+ Write-Output "PASS $asset transparent corners and visible artwork"
+}
diff --git a/tests/g01-visual-contract.ps1 b/tests/g01-visual-contract.ps1
new file mode 100644
index 0000000..e216cd5
--- /dev/null
+++ b/tests/g01-visual-contract.ps1
@@ -0,0 +1,107 @@
+$ErrorActionPreference = 'Stop'
+
+$root = Split-Path -Parent $PSScriptRoot
+$requiredAssets = @(
+ 'static/assets/icons/brand/jiapu-seal-logo.png',
+ 'static/assets/icons/brand/jiapu-seal-logo-header.png',
+ 'static/assets/icons/common/notice-v3.png',
+ 'static/assets/icons/common/chevron-right-v2.png',
+ 'static/assets/icons/common/location-v1.png',
+ 'static/assets/icons/common/member-meta-v1.png',
+ 'static/assets/icons/common/admin-v1.png',
+ 'static/assets/icons/genealogy/tree-v2.png',
+ 'static/assets/icons/genealogy/members-v2.png',
+ 'static/assets/icons/genealogy/generation-poem-v2.png',
+ 'static/assets/icons/genealogy/application-v2.png',
+ 'static/assets/icons/genealogy/seal-current-frame-v1.png',
+ 'static/assets/icons/genealogy/seal-row-frame-v1.png',
+ 'static/assets/icons/action/add-v2.png',
+ 'static/assets/icons/action/create-cloud-v1.png',
+ 'static/assets/icons/tab/genealogy-v4.png',
+ 'static/assets/icons/tab/genealogy-active-v4.png',
+ 'static/assets/icons/tab/family-v4.png',
+ 'static/assets/icons/tab/family-active-v4.png',
+ 'static/assets/icons/tab/profile-v4.png',
+ 'static/assets/icons/tab/profile-active-v4.png',
+ 'static/assets/backgrounds/genealogy-current-slip-frame.png',
+ 'static/assets/backgrounds/genealogy-list-slip-frame.png',
+ 'static/assets/backgrounds/header-cinnabar-texture-v2.jpg',
+ 'static/assets/backgrounds/paper-rice-texture-v2.jpg',
+ 'static/assets/backgrounds/footer-mountain-bamboo.png',
+ 'static/assets/backgrounds/genealogy-section-divider-v2.png'
+)
+
+foreach ($asset in $requiredAssets) {
+ if (-not (Test-Path -LiteralPath (Join-Path $root $asset))) {
+ throw "Missing G-01 visual asset: $asset"
+ }
+}
+
+$header = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/PageHeader.vue')
+$tabbar = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/AppTabbar.vue')
+
+if ($header -notmatch 'jiapu-seal-logo-header\.png') { throw 'Header does not use the approved tight brand PNG.' }
+if ($header -notmatch 'jiapu-seal-logo-header\.png') { throw 'Header does not use the tight header Logo asset.' }
+if ($header -notmatch 'notice-v3\.png') { throw 'Header does not use the approved notice PNG.' }
+if ($header -notmatch 'unreadCount\s*>\s*0') { throw 'Header badge is not conditional on unread count.' }
+if ($header -notmatch 'class="header-hall"') { throw 'Header does not isolate the hall line-art as a low-contrast background layer.' }
+if ($header -match 'background-image: url') { throw 'Header still applies hall line-art directly to the red header surface.' }
+if ($header -notmatch 'opacity:\s*\.16') { throw 'Header hall line-art is not reduced to the approved background contrast.' }
+if ($header -notmatch 'header-cinnabar-texture-v2\.jpg') { throw 'Header does not use the approved cinnabar texture asset.' }
+if ($header -notmatch 'height:\s*184rpx') { throw 'Header height does not match the approved compact red-head proportion.' }
+if ($tabbar -match '/static/icons/tab-') { throw 'Tabbar still references rejected legacy tab assets.' }
+if ($tabbar -notmatch 'safe-area-inset-bottom') { throw 'Tabbar has no Android safe-area padding.' }
+if ($tabbar -notmatch 'font-size:\s*28rpx') { throw 'Tabbar label size is below the approved G-01 size.' }
+if ($tabbar -notmatch 'width:\s*56rpx') { throw 'Tabbar icon size is below the approved G-01 size.' }
+
+$page = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/genealogy/index.vue')
+if ($page -match "from '@/utils/api\.js'") { throw 'G-01 visual phase must not call the remote API.' }
+foreach ($state in @('isLoading', 'hasGenealogies', 'createdGenealogies', 'joinedGenealogies')) {
+ if ($page -notmatch $state) { throw "G-01 is missing presentation state: $state" }
+}
+foreach ($asset in @('tree-v2.png', 'members-v2.png', 'generation-poem-v2.png', 'application-v2.png', 'add-v2.png')) {
+ if ($page -notmatch [regex]::Escape($asset)) { throw "G-01 does not consume $asset" }
+}
+if ($page -notmatch 'genealogy-current-slip-frame\.png') { throw 'G-01 does not use the approved current genealogy frame asset.' }
+foreach ($token in @('current-meta-item', 'current-seal-frame', 'location-v1.png', 'member-meta-v1.png', 'admin-v1.png', 'create-cloud-v1.png')) {
+ if ($page -notmatch $token) { throw "G-01 current panel is missing $token" }
+}
+if ($page -notmatch 'width:\s*82rpx') { throw 'G-01 shortcut icons remain below the reference visual size.' }
+foreach ($asset in @('paper-rice-texture-v2.jpg', 'footer-mountain-bamboo.png', 'genealogy-section-divider-v2.png')) {
+ if ($page -notmatch [regex]::Escape($asset)) { throw "G-01 does not consume the approved background asset: $asset" }
+}
+if ($page -match 'footer-mountain-bamboo-v2\.png') { throw 'G-01 still uses the solid mountain footer asset instead of the translucent watercolor landscape.' }
+if ($page -notmatch '(?s)\.page-footer-landscape\s*\{.*?opacity:\s*\.48') { throw 'G-01 footer landscape does not use the approved visible watercolor contrast.' }
+if ($page -match 'background:\s*rgba\(255,\s*249,\s*238,\s*\.88\)') { throw 'G-01 current genealogy card retains a rectangular pale backing behind its transparent corners.' }
+
+$card = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/GenealogyCard.vue')
+if ($card -notmatch 'genealogy-list-slip-frame\.png') { throw 'G-01 list rows do not use the approved slip frame asset.' }
+if ($card -match 'background:\s*rgba\(255,\s*249,\s*238,\s*\.(9|96)\)') { throw 'G-01 list cards retain a rectangular pale backing behind their transparent corners.' }
+foreach ($token in @('seal-row-frame-v1.png', 'card-detail-row', 'card-updated')) {
+ if ($card -notmatch $token) { throw "G-01 list card is missing $token" }
+}
+if ($card -match 'background:\s*#b93b2e') { throw 'G-01 list seal is still a plain CSS red block.' }
+foreach ($asset in @('location-v1.png', 'member-meta-v1.png', 'admin-v1.png', 'seal-current-frame-v1.png', 'seal-row-frame-v1.png', 'create-cloud-v1.png')) {
+ if ($page -notmatch [regex]::Escape($asset) -and $card -notmatch [regex]::Escape($asset)) {
+ throw "G-01 does not consume $asset"
+ }
+}
+
+$mock = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'data/mock.js')
+$fixtures = @(
+ [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('bmFtZTogJ+axpOawj+WutuiwsSc=')),
+ [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('bG9jYXRpb246ICfmsrPljZfCt+a0m+mYsyc=')),
+ 'memberCount: 158',
+ [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('bmFtZTogJ+axpOawj+Wul+iwsSc=')),
+ [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('bG9jYXRpb246ICflsbHkuJzCt+a1juWugSc=')),
+ 'memberCount: 286'
+)
+foreach ($fixture in $fixtures) {
+ if (-not $mock.Contains($fixture)) { throw "G-01 visual fixture is missing $fixture" }
+}
+if ($page -notmatch 'current-info-divider') { throw 'G-01 current panel is missing the reference title-to-meta divider.' }
+if ($card -match 'calendar-v1\.png') { throw 'G-01 list shows a calendar icon that does not exist in the reference.' }
+if ($card -match 'genealogy\.surname') { throw 'G-01 list seal still shows a surname instead of the fixed 家谱 seal.' }
+if ($card -notmatch '(?s)\.card-side\s*\{.*?flex-direction:\s*row') { throw 'G-01 list role and chevron are not aligned on one horizontal baseline.' }
+
+Write-Output 'PASS G-01 visual contract'
diff --git a/tests/manifest-json.ps1 b/tests/manifest-json.ps1
new file mode 100644
index 0000000..c9920b8
--- /dev/null
+++ b/tests/manifest-json.ps1
@@ -0,0 +1,11 @@
+$ErrorActionPreference = 'Stop'
+
+$root = Split-Path -Parent $PSScriptRoot
+
+try {
+ Get-Content -Raw -Encoding UTF8 (Join-Path $root 'manifest.json') | ConvertFrom-Json | Out-Null
+} catch {
+ throw 'manifest.json must be valid JSON.'
+}
+
+Write-Output 'PASS manifest JSON contract'
diff --git a/tests/phase1-contract.ps1 b/tests/phase1-contract.ps1
new file mode 100644
index 0000000..e0e08b8
--- /dev/null
+++ b/tests/phase1-contract.ps1
@@ -0,0 +1,80 @@
+$ErrorActionPreference = 'Stop'
+
+$root = Split-Path -Parent $PSScriptRoot
+$requiredFiles = @(
+ 'App.vue',
+ 'main.js',
+ 'manifest.json',
+ 'pages.json',
+ 'uni.scss',
+ 'styles/global.scss',
+ 'data/mock.js',
+ 'utils/config.js',
+ 'utils/api.js',
+ 'components/AppTabbar.vue',
+ 'components/PageHeader.vue',
+ 'pages/auth/login.vue',
+ 'pages/genealogy/index.vue',
+ 'pages/genealogy/create.vue',
+ 'pages/genealogy/detail.vue',
+ 'pages/genealogy/search.vue',
+ 'pages/genealogy/applications.vue',
+ 'pages/tree/index.vue',
+ 'pages/member/detail.vue',
+ 'pages/family/index.vue',
+ 'pages/profile/index.vue',
+ 'pages/notification/index.vue',
+ 'static/icons/tab-genealogy.png',
+ 'static/icons/tab-family.png',
+ 'static/icons/tab-profile.png'
+)
+
+$missing = $requiredFiles | Where-Object { -not (Test-Path (Join-Path $root $_)) }
+if ($missing) {
+ throw "Missing phase-one files: $($missing -join ', ')"
+}
+
+$pages = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages.json') | ConvertFrom-Json
+$paths = @($pages.pages | ForEach-Object { $_.path })
+$requiredRoutes = @(
+ 'pages/auth/login',
+ 'pages/genealogy/index',
+ 'pages/genealogy/create',
+ 'pages/genealogy/detail',
+ 'pages/genealogy/search',
+ 'pages/genealogy/applications',
+ 'pages/tree/index',
+ 'pages/member/detail',
+ 'pages/family/index',
+ 'pages/profile/index',
+ 'pages/notification/index'
+)
+
+$missingRoutes = $requiredRoutes | Where-Object { $_ -notin $paths }
+if ($missingRoutes) {
+ throw "Missing page routes: $($missingRoutes -join ', ')"
+}
+
+$tokens = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'uni.scss')
+foreach ($token in @('$brand-red:', '$ink:', '$paper:', '$gold:')) {
+ if ($tokens -notmatch [regex]::Escape($token)) {
+ throw "Missing design token: $token"
+ }
+}
+
+Add-Type -AssemblyName System.Drawing
+foreach ($icon in @('tab-genealogy.png', 'tab-family.png', 'tab-profile.png')) {
+ $bitmap = [System.Drawing.Bitmap]::new((Join-Path $root "static/icons/$icon"))
+ try {
+ if (-not [System.Drawing.Image]::IsAlphaPixelFormat($bitmap.PixelFormat)) {
+ throw "Icon must keep an alpha channel: $icon"
+ }
+ if ($bitmap.GetPixel(0, 0).A -ne 0) {
+ throw "Icon corner must be transparent: $icon"
+ }
+ } finally {
+ $bitmap.Dispose()
+ }
+}
+
+Write-Output 'PASS phase-one Uni-App contract'
diff --git a/tests/uni-scss-injection.ps1 b/tests/uni-scss-injection.ps1
new file mode 100644
index 0000000..dc18497
--- /dev/null
+++ b/tests/uni-scss-injection.ps1
@@ -0,0 +1,16 @@
+$ErrorActionPreference = 'Stop'
+
+$root = Split-Path -Parent $PSScriptRoot
+$uniScss = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'uni.scss')
+
+foreach ($token in @('$brand-red:', '$ink:', '$paper:', '$gold:')) {
+ if ($uniScss -notmatch [regex]::Escape($token)) {
+ throw "uni.scss must directly define $token"
+ }
+}
+
+if ($uniScss -match '@(?:import|use|forward)\s+["''](?:\./|\.\./)') {
+ throw 'uni.scss must not use relative Sass imports because it is injected into every page stylesheet.'
+}
+
+Write-Output 'PASS uni.scss injection contract'
diff --git a/tests/vue3-entry.ps1 b/tests/vue3-entry.ps1
new file mode 100644
index 0000000..9fe69c6
--- /dev/null
+++ b/tests/vue3-entry.ps1
@@ -0,0 +1,11 @@
+$ErrorActionPreference = 'Stop'
+
+$root = Split-Path -Parent $PSScriptRoot
+$manifest = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'manifest.json') | ConvertFrom-Json
+$entryPath = Join-Path $root 'index.html'
+
+if ($manifest.vueVersion -eq '3' -and -not (Test-Path $entryPath)) {
+ throw 'Vue 3 uni-app projects require index.html at the project root.'
+}
+
+Write-Output 'PASS Vue 3 entry contract'
diff --git a/tmp-g01-icon-audit.png b/tmp-g01-icon-audit.png
new file mode 100644
index 0000000..f33bb21
Binary files /dev/null and b/tmp-g01-icon-audit.png differ
diff --git a/tmp/imagegen/admin-v1-source.png b/tmp/imagegen/admin-v1-source.png
new file mode 100644
index 0000000..4b325c2
Binary files /dev/null and b/tmp/imagegen/admin-v1-source.png differ
diff --git a/tmp/imagegen/location-v1-source.png b/tmp/imagegen/location-v1-source.png
new file mode 100644
index 0000000..b5bb0f9
Binary files /dev/null and b/tmp/imagegen/location-v1-source.png differ
diff --git a/uni.scss b/uni.scss
new file mode 100644
index 0000000..5862343
--- /dev/null
+++ b/uni.scss
@@ -0,0 +1,9 @@
+$brand-red: #a72f27;
+$brand-red-dark: #7f211c;
+$ink: #2c241e;
+$ink-muted: #776b5c;
+$paper: #f6f0e5;
+$paper-white: #fffaf0;
+$gold: #af8242;
+$gold-soft: #dfcfad;
+$navy: #18232e;
diff --git a/unpackage/dist/cache/.vite/deps/_metadata.json b/unpackage/dist/cache/.vite/deps/_metadata.json
new file mode 100644
index 0000000..c7cc9ac
--- /dev/null
+++ b/unpackage/dist/cache/.vite/deps/_metadata.json
@@ -0,0 +1,8 @@
+{
+ "hash": "569c6bd0",
+ "configHash": "dc147105",
+ "lockfileHash": "e3b0c442",
+ "browserHash": "e2dbcede",
+ "optimized": {},
+ "chunks": {}
+}
\ No newline at end of file
diff --git a/unpackage/dist/cache/.vite/deps/package.json b/unpackage/dist/cache/.vite/deps/package.json
new file mode 100644
index 0000000..3dbc1ca
--- /dev/null
+++ b/unpackage/dist/cache/.vite/deps/package.json
@@ -0,0 +1,3 @@
+{
+ "type": "module"
+}
diff --git a/utils/api.js b/utils/api.js
new file mode 100644
index 0000000..596e0ef
--- /dev/null
+++ b/utils/api.js
@@ -0,0 +1,218 @@
+import { currentUser, genealogies, treeMembers, familyFeeds, familyContent, notifications, joinApplications } from '@/data/mock.js'
+import { hasRemoteConfig, isMockMode, runtimeConfig } from '@/utils/config.js'
+import { session } from '@/utils/session.js'
+
+const successCodes = [0, 200]
+
+export const unwrapResponse = (response) => {
+ if (!response || typeof response !== 'object' || Array.isArray(response)) return response
+ if (!Object.prototype.hasOwnProperty.call(response, 'code')) return response
+ if (!successCodes.includes(Number(response.code))) {
+ throw new Error(response.msg || '请求未成功')
+ }
+ return Object.prototype.hasOwnProperty.call(response, 'data') ? response.data : response
+}
+
+const request = (options, { authenticated = true } = {}) => new Promise((resolve, reject) => {
+ const token = session.getToken()
+ uni.request({
+ ...options,
+ url: `${runtimeConfig.baseUrl}${options.url}`,
+ header: {
+ clientid: runtimeConfig.clientId,
+ ...(authenticated && token ? { Authorization: `Bearer ${token}` } : {}),
+ ...(options.header || {})
+ },
+ success: ({ data, statusCode }) => {
+ if (statusCode < 200 || statusCode >= 300) {
+ reject(new Error(data?.msg || `请求失败(${statusCode})`))
+ return
+ }
+ try {
+ resolve(unwrapResponse(data))
+ } catch (error) {
+ reject(error)
+ }
+ },
+ fail: (error) => reject(new Error(error.errMsg || '网络连接失败'))
+ })
+})
+
+const authPayload = (payload) => ({
+ clientId: runtimeConfig.clientId,
+ tenantId: runtimeConfig.tenantId,
+ ...payload
+})
+
+const saveLogin = (loginResult) => {
+ const token = loginResult?.token || loginResult?.accessToken || loginResult?.tokenValue
+ if (!token) throw new Error('登录响应未包含会话令牌')
+ session.saveToken(token)
+ return loginResult
+}
+
+const toTreeNode = (person, index = 0) => {
+ const generation = Number(person.generationNo || person.generation || 1)
+ return {
+ ...person,
+ id: person.id || person.personId,
+ name: person.personName || person.name || '未命名族人',
+ relation: person.relation || (generation === 1 ? '始祖' : '族人'),
+ generation,
+ years: person.years || [person.birthDate, person.deathDate].filter(Boolean).join('—') || '生卒待补',
+ branch: person.branch || '主支',
+ x: person.x ?? (20 + (index % 4) * 20),
+ y: person.y ?? (generation * 31 - 24)
+ }
+}
+
+export const appApi = {
+ async sendSmsCode({ phone, validToken = '' }) {
+ if (isMockMode()) return { success: true }
+ return request({
+ url: '/genealogy/app/auth/sms/code',
+ method: 'POST',
+ data: authPayload({ grantType: 'sms', sceneCode: 'APP_LOGIN', phone, validToken })
+ }, { authenticated: false })
+ },
+ async loginWithPassword({ phone, passwordHash }) {
+ if (isMockMode()) {
+ session.saveToken('mock-session-token')
+ return { token: 'mock-session-token', userId: currentUser.id }
+ }
+ const result = await request({
+ url: '/genealogy/app/auth/login',
+ method: 'POST',
+ data: authPayload({ grantType: 'password', phone, password: passwordHash })
+ }, { authenticated: false })
+ return saveLogin(result)
+ },
+ async loginWithSms({ phone, smsCode }) {
+ if (isMockMode()) {
+ session.saveToken('mock-session-token')
+ return { token: 'mock-session-token', userId: currentUser.id }
+ }
+ const result = await request({
+ url: '/genealogy/app/auth/login/sms',
+ method: 'POST',
+ data: authPayload({ grantType: 'sms', phone, smsCode })
+ }, { authenticated: false })
+ return saveLogin(result)
+ },
+ async getProfile() {
+ return hasRemoteConfig() ? request({ url: '/genealogy/app/auth/profile' }) : currentUser
+ },
+ async getMyGenealogies() {
+ return hasRemoteConfig() ? request({ url: '/genealogy/app/genealogies/mine' }) : genealogies
+ },
+ async createGenealogy(payload) {
+ if (hasRemoteConfig()) return request({ url: '/genealogy/app/genealogies', method: 'POST', data: payload })
+ const created = { ...payload, id: Date.now(), memberCount: 0, activeCount: 0, motto: '敦亲睦族,敬祖传家。', visibility: '仅成员可见' }
+ genealogies.unshift(created)
+ return created
+ },
+ async getGenealogy(genealogyId) {
+ return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}` }) : genealogies.find((item) => String(item.id) === String(genealogyId))
+ },
+ async getOverview(genealogyId) {
+ return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/overview` }) : await this.getGenealogy(genealogyId)
+ },
+ async getTree(genealogyId) {
+ if (hasRemoteConfig()) {
+ const tree = await request({ url: `/genealogy/app/genealogies/${genealogyId}/lineage/tree` })
+ return (tree || []).map(toTreeNode)
+ }
+ return treeMembers.filter((item) => String(item.genealogyId || 1001) === String(genealogyId)).map(toTreeNode)
+ },
+ async getPerson(genealogyId, personId) {
+ if (hasRemoteConfig()) {
+ const person = await request({ url: `/genealogy/app/genealogies/${genealogyId}/lineage/persons/${personId}` })
+ return toTreeNode(person)
+ }
+ const person = treeMembers.find((item) => String(item.id) === String(personId) && String(item.genealogyId || 1001) === String(genealogyId))
+ return person ? toTreeNode(person) : null
+ },
+ async createPerson(genealogyId, payload) {
+ if (hasRemoteConfig()) {
+ const person = await request({ url: `/genealogy/app/genealogies/${genealogyId}/lineage/persons`, method: 'POST', data: payload })
+ return toTreeNode(person)
+ }
+ const person = toTreeNode({
+ ...payload,
+ id: Date.now(),
+ genealogyId,
+ relation: Number(payload.generationNo) === 1 ? '始祖' : '族人',
+ x: 50,
+ y: 7
+ })
+ treeMembers.push(person)
+ const genealogy = genealogies.find((item) => String(item.id) === String(genealogyId))
+ if (genealogy) {
+ genealogy.memberCount = (genealogy.memberCount || 0) + 1
+ genealogy.activeCount = (genealogy.activeCount || 0) + 1
+ }
+ return person
+ },
+ async getFeeds(genealogyId) {
+ return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/feeds` }) : familyFeeds
+ },
+ async createFeed(genealogyId, payload) {
+ if (hasRemoteConfig()) return request({ url: `/genealogy/app/genealogies/${genealogyId}/feeds`, method: 'POST', data: payload })
+ const feed = { id: Date.now(), genealogyId, author: currentUser.name, title: '家族动态', content: payload.feedContent, date: '刚刚', type: '族务' }
+ familyFeeds.unshift(feed)
+ return feed
+ },
+ async getArticles(genealogyId) {
+ return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/articles` }) : familyContent.article
+ },
+ async getAlbums(genealogyId) {
+ return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/albums` }) : familyContent.album
+ },
+ async getCeremonies(genealogyId) {
+ return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/ceremonies` }) : familyContent.ceremony
+ },
+ async getGrowthRecords(genealogyId) {
+ return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/growth-records` }) : familyContent.record
+ },
+ async getContent(type, genealogyId) {
+ const loaders = { article: this.getArticles, album: this.getAlbums, ceremony: this.getCeremonies, record: this.getGrowthRecords }
+ if (!loaders[type]) throw new Error('未知内容类型')
+ return loaders[type].call(this, genealogyId)
+ },
+ async getNotifications() {
+ return hasRemoteConfig() ? request({ url: '/genealogy/app/notifications' }) : notifications
+ },
+ async markNotificationRead(notificationId) {
+ if (hasRemoteConfig()) return request({ url: `/genealogy/app/notifications/${notificationId}/read`, method: 'POST' })
+ const notification = notifications.find((item) => String(item.id) === String(notificationId))
+ if (notification) notification.unread = false
+ return { success: true }
+ },
+ async markAllNotificationsRead() {
+ if (hasRemoteConfig()) return request({ url: '/genealogy/app/notifications/read-all', method: 'POST' })
+ notifications.forEach((item) => { item.unread = false })
+ return { success: true }
+ },
+ async getPublicGenealogies() {
+ return hasRemoteConfig() ? request({ url: '/genealogy/app/genealogies/public' }) : genealogies.filter((item) => item.visibility === '公开可申请')
+ },
+ async applyToJoin(genealogyId, payload) {
+ if (hasRemoteConfig()) return request({ url: `/genealogy/app/genealogies/${genealogyId}/join-applies`, method: 'POST', data: payload })
+ joinApplications.unshift({
+ id: Date.now(),
+ genealogyId,
+ name: currentUser.name,
+ phone: currentUser.phone,
+ relation: payload.message || '申请加入家谱',
+ appliedAt: '刚刚',
+ status: 'PENDING'
+ })
+ return { success: true }
+ },
+ async getPendingApplications(genealogyId) {
+ return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/join-applies/pending` }) : joinApplications.filter((item) => String(item.genealogyId || 1001) === String(genealogyId))
+ },
+ async auditApplication(genealogyId, applicationId, approved) {
+ return hasRemoteConfig() ? request({ url: `/genealogy/app/genealogies/${genealogyId}/join-applies/${applicationId}/audit`, method: 'PUT', data: { approved } }) : { success: true }
+ }
+}
diff --git a/utils/config.js b/utils/config.js
new file mode 100644
index 0000000..9b6d362
--- /dev/null
+++ b/utils/config.js
@@ -0,0 +1,9 @@
+export const runtimeConfig = {
+ mode: 'mock',
+ baseUrl: 'http://182.61.18.23:8080',
+ clientId: '428a8310cd442757ae699df5d894f051',
+ tenantId: '000000'
+}
+
+export const isMockMode = () => runtimeConfig.mode === 'mock'
+export const hasRemoteConfig = () => runtimeConfig.mode === 'remote' && Boolean(runtimeConfig.baseUrl && runtimeConfig.clientId)
diff --git a/utils/genealogy-context.js b/utils/genealogy-context.js
new file mode 100644
index 0000000..1cb5f08
--- /dev/null
+++ b/utils/genealogy-context.js
@@ -0,0 +1,7 @@
+const CURRENT_GENEALOGY_KEY = 'jiapu_current_genealogy_id'
+
+export const genealogyContext = {
+ getCurrentGenealogyId: () => uni.getStorageSync(CURRENT_GENEALOGY_KEY) || '',
+ setCurrentGenealogyId: (genealogyId) => uni.setStorageSync(CURRENT_GENEALOGY_KEY, String(genealogyId)),
+ clearCurrentGenealogyId: () => uni.removeStorageSync(CURRENT_GENEALOGY_KEY)
+}
diff --git a/utils/md5.js b/utils/md5.js
new file mode 100644
index 0000000..8982c26
--- /dev/null
+++ b/utils/md5.js
@@ -0,0 +1,72 @@
+const safeAdd = (x, y) => {
+ const lsw = (x & 0xffff) + (y & 0xffff)
+ const msw = (x >> 16) + (y >> 16) + (lsw >> 16)
+ return (msw << 16) | (lsw & 0xffff)
+}
+
+const bitRotateLeft = (value, count) => (value << count) | (value >>> (32 - count))
+const common = (q, a, b, x, s, t) => safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b)
+const ff = (a, b, c, d, x, s, t) => common((b & c) | ((~b) & d), a, b, x, s, t)
+const gg = (a, b, c, d, x, s, t) => common((b & d) | (c & (~d)), a, b, x, s, t)
+const hh = (a, b, c, d, x, s, t) => common(b ^ c ^ d, a, b, x, s, t)
+const ii = (a, b, c, d, x, s, t) => common(c ^ (b | (~d)), a, b, x, s, t)
+
+const toWords = (value) => {
+ const words = []
+ for (let index = 0; index < value.length * 8; index += 8) {
+ words[index >> 5] |= (value.charCodeAt(index / 8) & 0xff) << (index % 32)
+ }
+ return words
+}
+
+const toHex = (words) => {
+ const alphabet = '0123456789abcdef'
+ let output = ''
+ for (let index = 0; index < words.length * 4; index += 1) {
+ output += alphabet.charAt((words[index >> 2] >> ((index % 4) * 8 + 4)) & 0x0f)
+ output += alphabet.charAt((words[index >> 2] >> ((index % 4) * 8)) & 0x0f)
+ }
+ return output
+}
+
+const coreMd5 = (words, bitLength) => {
+ words[bitLength >> 5] |= 0x80 << (bitLength % 32)
+ words[(((bitLength + 64) >>> 9) << 4) + 14] = bitLength
+
+ let a = 1732584193
+ let b = -271733879
+ let c = -1732584194
+ let d = 271733878
+
+ for (let index = 0; index < words.length; index += 16) {
+ const oldA = a
+ const oldB = b
+ const oldC = c
+ const oldD = d
+
+ a = ff(a, b, c, d, words[index], 7, -680876936); d = ff(d, a, b, c, words[index + 1], 12, -389564586); c = ff(c, d, a, b, words[index + 2], 17, 606105819); b = ff(b, c, d, a, words[index + 3], 22, -1044525330)
+ a = ff(a, b, c, d, words[index + 4], 7, -176418897); d = ff(d, a, b, c, words[index + 5], 12, 1200080426); c = ff(c, d, a, b, words[index + 6], 17, -1473231341); b = ff(b, c, d, a, words[index + 7], 22, -45705983)
+ a = ff(a, b, c, d, words[index + 8], 7, 1770035416); d = ff(d, a, b, c, words[index + 9], 12, -1958414417); c = ff(c, d, a, b, words[index + 10], 17, -42063); b = ff(b, c, d, a, words[index + 11], 22, -1990404162)
+ a = ff(a, b, c, d, words[index + 12], 7, 1804603682); d = ff(d, a, b, c, words[index + 13], 12, -40341101); c = ff(c, d, a, b, words[index + 14], 17, -1502002290); b = ff(b, c, d, a, words[index + 15], 22, 1236535329)
+ a = gg(a, b, c, d, words[index + 1], 5, -165796510); d = gg(d, a, b, c, words[index + 6], 9, -1069501632); c = gg(c, d, a, b, words[index + 11], 14, 643717713); b = gg(b, c, d, a, words[index], 20, -373897302)
+ a = gg(a, b, c, d, words[index + 5], 5, -701558691); d = gg(d, a, b, c, words[index + 10], 9, 38016083); c = gg(c, d, a, b, words[index + 15], 14, -660478335); b = gg(b, c, d, a, words[index + 4], 20, -405537848)
+ a = gg(a, b, c, d, words[index + 9], 5, 568446438); d = gg(d, a, b, c, words[index + 14], 9, -1019803690); c = gg(c, d, a, b, words[index + 3], 14, -187363961); b = gg(b, c, d, a, words[index + 8], 20, 1163531501)
+ a = gg(a, b, c, d, words[index + 13], 5, -1444681467); d = gg(d, a, b, c, words[index + 2], 9, -51403784); c = gg(c, d, a, b, words[index + 7], 14, 1735328473); b = gg(b, c, d, a, words[index + 12], 20, -1926607734)
+ a = hh(a, b, c, d, words[index + 5], 4, -378558); d = hh(d, a, b, c, words[index + 8], 11, -2022574463); c = hh(c, d, a, b, words[index + 11], 16, 1839030562); b = hh(b, c, d, a, words[index + 14], 23, -35309556)
+ a = hh(a, b, c, d, words[index + 1], 4, -1530992060); d = hh(d, a, b, c, words[index + 4], 11, 1272893353); c = hh(c, d, a, b, words[index + 7], 16, -155497632); b = hh(b, c, d, a, words[index + 10], 23, -1094730640)
+ a = hh(a, b, c, d, words[index + 13], 4, 681279174); d = hh(d, a, b, c, words[index], 11, -358537222); c = hh(c, d, a, b, words[index + 3], 16, -722521979); b = hh(b, c, d, a, words[index + 6], 23, 76029189)
+ a = hh(a, b, c, d, words[index + 9], 4, -640364487); d = hh(d, a, b, c, words[index + 12], 11, -421815835); c = hh(c, d, a, b, words[index + 15], 16, 530742520); b = hh(b, c, d, a, words[index + 2], 23, -995338651)
+ a = ii(a, b, c, d, words[index], 6, -198630844); d = ii(d, a, b, c, words[index + 7], 10, 1126891415); c = ii(c, d, a, b, words[index + 14], 15, -1416354905); b = ii(b, c, d, a, words[index + 5], 21, -57434055)
+ a = ii(a, b, c, d, words[index + 12], 6, 1700485571); d = ii(d, a, b, c, words[index + 3], 10, -1894986606); c = ii(c, d, a, b, words[index + 10], 15, -1051523); b = ii(b, c, d, a, words[index + 1], 21, -2054922799)
+ a = ii(a, b, c, d, words[index + 8], 6, 1873313359); d = ii(d, a, b, c, words[index + 15], 10, -30611744); c = ii(c, d, a, b, words[index + 6], 15, -1560198380); b = ii(b, c, d, a, words[index + 13], 21, 1309151649)
+ a = ii(a, b, c, d, words[index + 4], 6, -145523070); d = ii(d, a, b, c, words[index + 11], 10, -1120210379); c = ii(c, d, a, b, words[index + 2], 15, 718787259); b = ii(b, c, d, a, words[index + 9], 21, -343485551)
+
+ a = safeAdd(a, oldA); b = safeAdd(b, oldB); c = safeAdd(c, oldC); d = safeAdd(d, oldD)
+ }
+ return [a, b, c, d]
+}
+
+export const calcMD5 = (value) => {
+ const utf8Value = unescape(encodeURIComponent(String(value)))
+ return toHex(coreMd5(toWords(utf8Value), utf8Value.length * 8))
+}
diff --git a/utils/session.js b/utils/session.js
new file mode 100644
index 0000000..090ae5a
--- /dev/null
+++ b/utils/session.js
@@ -0,0 +1,7 @@
+const TOKEN_KEY = 'jiapu_token'
+
+export const session = {
+ getToken: () => uni.getStorageSync(TOKEN_KEY) || '',
+ saveToken: (token) => uni.setStorageSync(TOKEN_KEY, token),
+ clear: () => uni.removeStorageSync(TOKEN_KEY)
+}