Review changes batch 3 of 6

This commit is contained in:
2026-07-20 06:52:23 +08:00
parent c848653758
commit 3e3251a4fc
9 changed files with 1644 additions and 171 deletions
+590
View File
@@ -30,6 +30,95 @@
final result: passed
---
# Design QA — M01M10 全状态视觉候选(2026-07-17
- source visual truth path: `docs/superpowers/specs/2026-07-17-m-series-all-states-visual-design.md`
- runtime evidence: `docs/design/screens/runtime/2026-07-17/m01/``m10/`
- responsive evidence: `docs/design/screens/runtime/2026-07-17/responsive-series/`
- primary viewport: 412×915
- states: M01 2 态、M02M10 共 36 态、3 个设置项提示,共 41 张内部状态截图
## Findings
- M01 原无内容红框已替换为已确认的真实“谱”印资产;身份、待办、菜单和错误说明的字号与对比度已提高。
- M02–M10 的表单、设置、详情、服务状态及空/成功/错误态均未见文字截断、按钮遮挡或背景错位。
- M03、M06、M10 设置项点击提示复用 A 系列 `AppToast`,内容与点击项目一致。
- 另以 320×568、360×640、360×800、412×915 对 F/R/N/M 的六类公共母版和四个专属根页执行真实运行与几何检查;30 张补充截图未见横向裁切,320×568 保留自然纵向滚动。
## Evidence limits
- 当前仅为 H5 内部候选,不代表用户逐页验收或 Android/HBuilderX 真机验收;接口仍未接入。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — N01N02 全状态视觉候选(2026-07-17
- source visual truth path: `docs/superpowers/specs/2026-07-17-n-series-all-states-visual-design.md`
- runtime evidence: `docs/design/screens/runtime/2026-07-17/n01/``n02/`
- viewport: 412×915
- states: N01 6 态、N02 4 态,共 10 张内部截图
## Findings
- N01 消息标签、摘要和状态说明已提高可读性;原先左侧悬空的次按钮已改为内容区整行规格。
- 单条已读、全部已读和全部已读 `AppToast` 均由真实点击触发,列表状态更新正确。
- N02 四态与通知模块背景一致,详情、空态、成功、错误均未出现文字或按钮冲突。
## Evidence limits
- 当前仅为 H5 内部候选,不代表用户逐页验收或 Android/HBuilderX 真机验收;接口仍未接入。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — R01R11 全状态视觉候选(2026-07-17
- source visual truth path: `docs/superpowers/specs/2026-07-17-r-series-all-states-visual-design.md`
- runtime evidence: `docs/design/screens/runtime/2026-07-17/r01/``r11/`
- viewport: 412×915
- states: 44 个页面状态,加 5 个列表点击提示,共 49 张内部截图
## Findings
- R01–R11 的列表、详情、四字段表单与时间线均保持档案模块独立的书卷笔墨背景。
- 默认、空、成功、错误四态均使用真实标题和操作文案,未见标题折断、表单越界、按钮遮挡或底部背景断层。
- 五个可点击列表的 `AppToast` 均复用 A 系列已确认样式,提示内容与所点记录一致。
## Evidence limits
- 当前仅为 H5 内部候选,不代表用户逐页验收或 Android/HBuilderX 真机验收;接口仍未接入。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — F01F10 全状态视觉候选(2026-07-17
- source visual truth path: `docs/superpowers/specs/2026-07-17-f-series-all-states-visual-design.md`
- runtime evidence: `docs/design/screens/runtime/2026-07-17/f01/``f10/`
- viewport: 412×915
- states: F01 4 态、F02 4 态、F03F10 各 4 态,另含 F04/F07 列表点击提示,共 42 张内部截图
## Findings
- F01 的动态标签、摘要、作者和空/错态说明已提高字号与对比度,信息流卡片未出现拥挤或越界。
- F02 的表单说明、输入提示、成功/错误说明已提高可读性;空内容提示复用已确认的全局 `AppToast`
- F03–F10 的表单、列表、详情、状态说明由 `ModulePage.vue` 统一管理文字规格,保留既有浅色国风背景、金色内容框和 A 系列按钮资产。
- 42 个状态均在同一个可控 Chrome 标签页真实运行并逐张检查;未见文字截断、按钮遮挡、背景错位或状态残留。
- 截图过程曾出现一次 reload 与 DOM 检查竞争;捕获脚本改为等待 `Page.loadEventFired` 后再检查页面,没有放宽截图或测试阈值。
## Evidence limits
- 本节仅为 H5 内部候选证据,不代表用户逐页验收或 Android/HBuilderX 真机验收。
- F 系列接口仍未接入,成功/错误内容为视觉流程候选数据。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — 全模块基础视觉系统(无截图验证)
@@ -735,6 +824,507 @@ final result: passed
final result: passed (internal candidate QA only)
---
# Design QA — F01 快捷入口两列布局(已淘汰,2026-07-19)
- source screenshot: `docs/design/screens/runtime/2026-07-19/f01-baseline-redesign/01-list-412x915.png`
- implementation screenshot: `docs/design/screens/runtime/2026-07-19/f01-baseline-redesign/02-list-shortcuts-grid-412x915.png`
- combined comparison: `docs/design/screens/runtime/2026-07-19/f01-baseline-redesign/03-shortcuts-before-after.png`
- responsive evidence: `docs/design/screens/runtime/2026-07-19/f01-baseline-redesign/04-list-320x568.png`
- viewport: 412×915;补查 320×568
## Findings
复审发现该候选存在 P1 视觉层级问题,已淘汰。
- 四组卷轴端头和装饰线重复堆叠,视觉重量超过标题并挤压内容卡层级。
- 两列中间形成明显断口,呈现为四条被切开的装饰线,不像自然的导航入口。
- 该候选随后由“单个全宽卷轴导航条”替代,不作为通过证据。
## Evidence limits
该段只记录淘汰原因;Android/HBuilderX 真机或模拟器仍未验证。
final result: failed (superseded)
---
# Design QA — F01 单框四列快捷入口(2026-07-19
- rejected screenshot: `docs/design/screens/runtime/2026-07-19/f01-shortcut-reaudit/01-current-412x915.png`
- implementation screenshot: `docs/design/screens/runtime/2026-07-19/f01-shortcut-single-strip/01-list-412x915.png`
- combined comparison: `docs/design/screens/runtime/2026-07-19/f01-shortcut-single-strip/02-rejected-vs-single-strip.png`
- responsive evidence: `docs/design/screens/runtime/2026-07-19/f01-shortcut-single-strip/07-list-320x568-clean.png`
- viewport: 412×915;补查 320×568
## Findings
当前没有快捷入口相关的 H5 P0/P1/P2 问题。
- 四个入口共用一张真实 A 系列次级卷轴资产,只保留一套端头与外框,装饰密度明显下降。
- 四项文字在同一完整导航条内等宽排列,标题区到第一张动态卡的节奏恢复连续。
- 每项点击高度为 48px,不低于 44px;320px 宽度下没有文字裁切或横向溢出。
- 快捷名称、点击事件、目标路由以及页面其他内容均未改变。
## Evidence limits
F01 正常列表、加载、空和失败四态已于 2026-07-19 获得用户明确通过;Android/HBuilderX 真机或模拟器仍未验证。
final result: passed (user accepted H5; Android pending)
---
# Design QA — G01 可拉伸“切换当前家谱”弹层(2026-07-19)
- source visual truth path: `docs/superpowers/specs/2026-07-19-g01-switch-genealogy-dialog-design.md`
- source asset: `static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png`
- implementation screenshot path: `docs/design/screens/runtime/2026-07-19/g01-approval/06-switch-dialog-stretchable-412x915.png`
- full-view comparison evidence: `docs/design/screens/runtime/2026-07-19/g01-approval/06-switch-dialog-before-vs-stretchable.png`
- responsive evidence: `06-switch-dialog-stretchable-320x568.png``06-switch-dialog-stretchable-360x640.png``06-switch-dialog-stretchable-360x800.png``06-switch-dialog-stretchable-412x915.png`
- stress evidence: `06-switch-dialog-stretchable-six-items-412x915.png`
- viewport: 320×568、360×640、360×800、412×915
- state: G01 正常列表态上的“切换当前家谱”弹层;最终审批现场为 412×915、两条真实数据
## Findings
没有遗留的 H5 P0/P1/P2 问题。
- 字体与层级:标题使用现有楷体红色标题体系;家谱名称、地区/人数与“当前/选择”形成三级层级。四档原尺寸截图中文字均完整,无换行、截断或装饰压字。
- 间距与布局:两条数据时外框为 `600rpx + 2px` 边框的最低高度并居中;末项紧贴列表内容,不再出现旧版约 193px 的无效底部空白。六条时弹层由约 332px 自然增至约 553px;十二条时在 412×915 封顶约 848px,列表从 `665px` 可视高度进入独立滚动。
- 色彩与视觉令牌:继续使用项目既有朱红、墨色、金色与宣纸色,没有引入新的色板;遮罩保持 `rgba(34, 20, 12, 0.58)`,当前项使用低透明朱红底与细边框。
- 图片与资产:完整复用 `a01-scroll-dialog-v3.png`,以 `border-image-slice: 300 260 360 260 fill` 九宫格切片;顶部祥云和底部山水保持固定装饰区,仅中部宣纸随内容拉伸。关闭入口复用现有 `g01-dialog-close.png`,没有 CSS 绘图、文本符号或临时占位。
- 文案与内容:保留“切换当前家谱”、两条真实家谱名称、地区、人数及“当前/选择”,未增加解释性废话。
- 可用性:关闭图标使用 80rpx 点击画布并带 `aria-label`;实测内部点击不关闭、关闭图标关闭、遮罩关闭、选择第二项更新当前家谱并关闭,随后恢复第一项。Android 返回优先关闭切换弹层的代码合同已覆盖。
- 响应式与压力态:四档均无横向溢出,两条数据不滚动;六条自然增高且不滚动;十二条封顶后列表可从顶部滚到底部,标题与关闭入口不随列表滚动。
- 运行质量:9222 中仅有一个 `localhost:5173` 项目标签页;本轮刷新、四档、压力态和交互过程中未捕获运行时异常或 4xx/5xx 资源响应。
## Comparison history
- 初始审查发现:标题压住顶部边框/祥云;约 193px 底部空白导致内容重心上浮;没有显式关闭入口;固定 `aspectFit` 整图不能支持后续更多条目。
- 修正:移除固定比例整图节点,改为单张完整 PNG 九宫格背景;标题移入安全区,增加图片关闭图标;列表高度按内容增长并在安全上限滚动。
- 修正后证据:同尺寸前后对比见 `06-switch-dialog-before-vs-stretchable.png`;412×915 两条和六条原图分别见 `06-switch-dialog-stretchable-412x915.png``06-switch-dialog-stretchable-six-items-412x915.png`。未发现需要继续修正的 P0/P1/P2。
## Focused region evidence
未另做裁切图:412×915 原尺寸实现截图已经能清楚读取标题、关闭图标、两行名称/元信息、当前态边框及四角/底部装饰,继续裁切不会增加可判断信息;六条压力原图用于单独检查中段拉伸和底部山水。
## Evidence limits
- 本节只代表 H5 内部候选设计 QA;用户尚未明确通过当前“切换家谱弹层”,因此没有将 G01 标记为验收或冻结。
- Android/HBuilderX 真机或模拟器、系统字体、返回手势、触摸精度、GPU 对九宫格边缘的采样仍未验证。
- 十二条压力态通过同一标签页的临时 DOM 克隆验证布局与滚动,未修改真实模拟数据,也不代表接口对接完成。
final result: passed
---
# Design QA — G01 添加家谱弹层最低高度、内容居中、按钮间距与等宽(2026-07-19)
- source visual truth path: `docs/superpowers/specs/2026-07-19-g01-add-genealogy-bottom-sheet-design.md`(用户确认内容整体居中、`24rpx` 按钮间距,并选择以红色主按钮的可见宽度为三个按钮的统一基准)
- before screenshot: `docs/design/screens/runtime/2026-07-19/g01-approval/05-add-dialog-centered-min-gap24-412x915.png`
- implementation screenshot path: `docs/design/screens/runtime/2026-07-19/g01-approval/05-add-dialog-equal-width-412x915.png`
- responsive evidence: `docs/design/screens/runtime/2026-07-19/g01-approval/05-add-dialog-equal-width-320x568.png`
- full-view comparison evidence: `docs/design/screens/runtime/2026-07-19/g01-approval/05-add-dialog-gap24-unequal-vs-equal-width.png`
- pressure evidence: `docs/design/screens/runtime/2026-07-19/g01-approval/05-add-dialog-equal-width-six-buttons-412x915.png`
- viewport: `412×915`
- state: G01 正常列表上的“添加家谱”底部弹层
## Findings
没有遗留的 H5 P0/P1/P2 问题。
- 第一轮 P2:说明文字与首个按钮之间只有约 `2.20px`;首次调整到 `10.98px` 后,弹层仍只有约 `280.31px`,用户确认整体偏矮。
- 第二轮 P2:弹层提高到约 `342.58px` 后,用户用箭头明确要求可见弧形上沿继续上移,并确认标题、说明和按钮作为整体居中。
- 第三轮修复:三按钮状态使用 `780rpx` 最小高度,新增 `add-dialog__body` 通过上下自动边距安全居中;关闭图标改为跟随标题定位。最大高度和内容滚动合同保持不变。
- 第四轮 P2:用户指出三个按钮之间的 `6rpx` 间距仍显拥挤,并在 `16rpx / 20rpx / 24rpx` 三档中选择第三档;相邻按钮间距只调整为 `24rpx`,未改变按钮资产、宽度或弹层高度合同。
- 第五轮 P2:三个按钮根容器均为 `388.03px`,但主按钮资产为 `1866×276`、次按钮资产为 `1866×300`,相同高度下使用 `aspectFit` 导致红色皮肤可见宽度约 `326.84px`、米色皮肤约 `300.61px`。用户选择以红色主按钮为等宽基准。
- 修复后证据:仅在 G01 动作组内使用 `595rpx × 96rpx` 响应式按钮容器;412×915 下三个按钮根容器及皮肤可见宽度均为 `326.84px`left/right 偏差为 0。弹层仍约 `430.47px`;内容上下空间约 `79.36px``79.34px`,中心偏差约 `0.02px`;相邻按钮间距均约 `13.17px`
- 字体与排版:标题、说明和按钮字形、字号、层级均沿用现有设计,无新增漂移。
- 间距与布局:标题、说明和按钮组整体居中,关闭图标与标题同步移动;`320×568` 下三个按钮宽度均为 `253.86px`,上下空间均约 `61.45px`,相邻按钮间距均约 `10.23px`,完整可见且无横向溢出。
- 色彩与视觉令牌:遮罩、红金按钮、宣纸底色与既有候选一致。
- 图片质量与资产:继续使用完整可拉伸底板、现有关闭图标及两张原始按钮 PNG;按钮皮肤保持 `aspectFit`,未横向拉伸、重绘或生成替代资产。
- 文案与内容:标题、说明及三个操作文案未变。
- 内容扩展:六按钮压力状态自然增长到约 `576.95px`,上下空间同为约 `53.73px`,六个按钮宽度均为 `326.84px`,五处间距均约 `13.17px`18 按钮状态封顶约 `871.05px`,初始滚动位置为 0、标题可达,滚动范围约 `495px`,末项可到达可视区。
- 聚焦区域无需另做裁切:并排证据中弹层占据画面下半部,标题、说明、间距、关闭图标和三个按钮均可直接辨认。
## Comparison History
1. 旧候选的相邻按钮间距为 `6rpx`,在当前最低高度和按钮纹样下显得拥挤。
2. 用户要求继续增大间隔,并在三档方案中选择第三档 `24rpx`;实现只修改相邻按钮间距。
3. 修复后的同尺寸并排证据显示按钮分组更清晰,同时最低高度、安全居中、按钮资产和关闭布局均保持不变。
4. 用户指出三个按钮可见宽度不一致;运行时确认根容器等宽但资产纵横比不同,`aspectFit` 产生约 `26.23px` 可见宽度差。
5. 用户选择以红色主按钮宽度为准;G01 局部容器比例调整后,同尺寸并排证据显示三个卷轴端点对齐,公共按钮组件、原始资产和其他页面未改变。
## Interaction Evidence
- 复用 9222 上唯一的 `localhost:5173` 项目标签页。
- 验证弹层内部点击不关闭、关闭图标关闭、遮罩关闭,并重新打开供用户审批。
- 本轮新鲜 reload 后由 `.create-action` 重新打开弹层;无 4xx/5xx 资源响应,控制台异常为空。
## Evidence Limits
- 当前仅为 H5 内部候选证据,尚未获得用户对本次弹层的明确“通过”。
- Android/HBuilderX 真机或模拟器仍未验证,不代表 G01 已冻结或整页完成。
final result: passed
---
# Design QA — G01 添加家谱完整可伸缩底板(2026-07-19)
- source visual truth path: `docs/design/mockups/2026-07-19/g01-add-dialog-paper-sheet-target.png`
- implementation screenshot path: `docs/design/screens/runtime/2026-07-19/g01-approval/05-add-dialog-complete-sheet-412x915-approved-candidate.png`
- pressure screenshot path: `docs/design/screens/runtime/2026-07-19/g01-approval/05-add-dialog-complete-sheet-pressure-412x915-v2.png`
- responsive screenshot path: `docs/design/screens/runtime/2026-07-19/g01-approval/05-add-dialog-complete-sheet-320x568.png`
- full-view comparison evidence: `docs/design/screens/runtime/2026-07-19/g01-approval/05-add-dialog-complete-sheet-comparison.png`
- viewport: `412×915`;另查 `320×568`
- state: G01“添加家谱”正常三按钮状态与六按钮长内容压力状态
- focused region comparison evidence: 并排图中的弹层区域可清楚核对顶部轮廓、如意结、标题说明、关闭图标和三个按钮;压力图单独核对背景纵向扩展,无需额外裁切放大。
## Findings
当前没有仍需处理的 P0/P1/P2 差异。
- 字体与文案:标题、说明和三个操作文案与目标一致;标题与顶部金线之间已留出稳定间距,不再压住如意结或圆肩。
- 间距与布局:完整底板全宽贴底;正常态与目标稿的顶部轮廓、标题、说明和按钮纵向节奏接近。按钮可见宽度保留现有 `AppButton` 资产比例,这是用户明确要求复用现有按钮样式产生的预期差异。
- 颜色与表面:遮罩、暖白宣纸、朱砂标题和古金边线与目标一致;纸面、曲线和结饰来自同一张 `g01-add-sheet-background-v3.png`,不再存在矩形纸面与顶部过渡图接缝。
- 图片质量:完整底板为真实透明 PNG,透明外轮廓干净;未使用 CSS 图形、文字符号或手绘 SVG 冒充装饰。关闭图标继续使用独立真实 PNG。
- 伸缩:六按钮压力态的弹层高度由 `270.42px` 增至 `425.25px`,计算样式仍使用同一底板、`220 0 1 fill` 切片和固定顶部宽度;只扩展中间宣纸区域,顶部曲线与如意结未随高度拉长。
- 响应式:`320×568` 下三个按钮全部落在视口内,图片加载完成,无横向溢出。
- 交互与控制台:同一唯一 9222 项目标签页验证关闭图标、内部点击、遮罩关闭与恢复正常状态;未捕获运行时或控制台错误。
## Comparison history
1. 旧实现用矩形宣纸和单独顶部过渡图拼接,产生硬直边、纹理断层和明显的“截一部分”感。
2. 用户确认改为单张完整底板,并要求未来内容增加时高度可扩展;选定单资产九宫格伸缩,只固定顶部装饰并拉伸中间纸面。
3. 首轮完整底板运行图中标题和关闭图标压近顶部金线;增加顶部内容安全间距并重新定位关闭热区后,正常态和压力态均清晰分层。
4. 最终并排复核未发现可执行 P0/P1/P2 问题;页面恢复为 `412×915` 正常三按钮状态,等待用户审批。
## Evidence limits
本节只表示 H5 内部候选通过设计对照门,不代表用户已经通过当前弹层,也不代表 G01 已冻结。Android/HBuilderX 真机或模拟器的 `border-image` 渲染、系统字体和触摸仍未验证。
final result: passed
---
# Design QA — G01 添加家谱纸张底部弹层(2026-07-19)
- source visual truth path: `docs/design/mockups/2026-07-19/g01-add-dialog-paper-sheet-target.png`
- implementation screenshot path: `docs/design/screens/runtime/2026-07-19/g01-approval/05-add-dialog-paper-sheet-412x915-final-clean.png`
- full-view comparison evidence: `docs/design/screens/runtime/2026-07-19/g01-approval/05-add-dialog-paper-sheet-comparison-v2.png`
- viewport: `412×915`;响应式补查 `320×568`
- state: G01 正常列表态之上的“添加家谱”弹层
- focused region comparison evidence: 全屏并排图中弹层区域已足够清晰,可直接核对顶部纸边、标题说明、关闭图标和三个按钮,无需另做局部放大。
## Findings
当前没有仍需处理的 P0/P1/P2 差异。
- 字体与文案:标题、说明和三个操作名称与选定目标一致,层级清楚;按钮文字继续使用项目现有 `AppButton` 规格。
- 间距与布局:弹层全宽贴底,顶部纸张曲线和中央如意结完整显示;`320×568` 下三个按钮均在首屏内,页面无横向溢出。
- 颜色与表面:遮罩、暖白宣纸、朱砂标题、古金边线与 G01 既有视觉体系一致,移除了上一版无效山水留白。
- 图片与图标:纸张顶部使用现有真实过渡资产,关闭入口使用真实透明 PNG;未使用文字叉号、CSS 图形或手绘 SVG。透明画布留白已通过显示位置与画布尺寸补偿,未见绿色残边。
- 按钮:按用户明确决定,三个按钮直接复用项目现有主/次 `AppButton` 资产,不按概念稿重新绘制,因此宽度和纹样以现有组件为准。
- 交互与可访问性:关闭热区为 `80rpx × 80rpx`,带“关闭”可访问名称;320 档实测关闭图标可关闭弹层,重新打开后 412 档保持正常。此前同一实现已验证点击弹层内容不关闭、点击遮罩关闭、列表位置不丢失。
- 浏览器证据:唯一 9222 项目标签页中完成刷新、两档尺寸与关闭交互;图片均加载完成,未捕获运行时或控制台错误。
## Comparison history
1. 首轮实现的纸边资产透明留白未被补偿,真正曲线被容器裁掉,只显示生硬直边;关闭 PNG 也因透明画布过大而仅剩约 8px 可见叉号。
2. 修正纸边图的垂直定位,并按实际透明占比调整关闭图显示画布;重新捕获后曲线、如意结和关闭图标均清晰可见。
3. 选定目标与实现并排复核:按钮宽度存在由“复用现有组件”带来的预期差异,其余未发现可执行的 P0/P1/P2 问题。
## Evidence limits
本节仅表示该弹层达到内部 H5 候选质量门,不代表用户已经通过 G01 当前状态,也不代表 G01 已冻结。Android/HBuilderX 真机或模拟器验证仍未完成。
final result: passed
---
# Design QA — G08 十状态闭环(2026-07-17
- evidence folder: `docs/design/screens/runtime/2026-07-17/g08/`
- states: 搜索申请默认/校验/提交中/失败/成功,邀请码直加入默认/校验/提交中/失败/成功
- viewport: 十态均为 `412×915` 真实交互截图;流程 smoke 另覆盖 `320×568``360×800``412×915`
## Findings
- 两种来源保持各自准确语义:搜索来源进入管理员审核,邀请码来源直接加入;默认、提交中和结果态没有混用文案。
- 表单、结果框、主操作继续复用既有宣纸面板、G 系列背景和 A 系列红金卷轴,不重复生成已确认资产。
- 十态逐张检查后,仅字段校验文字在插画面板上偏弱;已从 `20rpx` 提升为 `24rpx / 34rpx`,搜索与邀请码校验态重新截图后未挤压输入框或主按钮。
- 失败与成功均保留同页明确恢复/下一步操作;没有原生 Toast、Modal、Loading 或 ActionSheet。
## Evidence limits
- 当前为 H5 自主设计内部候选,未获得用户逐态确认;真实提交接口、Android/HBuilderX 软键盘、安全区、返回手势和字体缩放仍待复核。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — T07T08 七状态闭环(2026-07-17
- T07 evidence folder: `docs/design/screens/runtime/2026-07-17/t07/`(列表、加载、空、失败)
- T08 evidence folder: `docs/design/screens/runtime/2026-07-17/t08/`(隐私、纪念、无权限)
- viewport: 七态最终截图均为 `412×915`
## Findings
- T07 保留成员状态卡与搜索结构,提升搜索、人数、成员代际/支系、档案状态和空/失败说明字号;三张成员卡仍完整显示。
- T08 保留三标签与上下双面板结构,提升标签、状态摘要和说明条目字号;隐私、纪念、无权限三态未出现摘要卡溢出。
- 两页均复用现有 T 系列背景与状态卡,不生成重复资产,不接入原生系统提示。
## Evidence limits
- 当前为 H5 自主设计内部候选;真实搜索、权限、Android/HBuilderX 输入法、字体缩放和触摸目标仍待验证。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — T03T06 全状态闭环(2026-07-17
- T03 evidence folder: `docs/design/screens/runtime/2026-07-17/t03/`(详情、加载、失败)
- T04 evidence folder: `docs/design/screens/runtime/2026-07-17/t04/`(表单、成功、冲突、失败)
- T05 evidence folder: `docs/design/screens/runtime/2026-07-17/t05/`(表单、成功、冲突、失败)
- T06 evidence folder: `docs/design/screens/runtime/2026-07-17/t06/`(表单、成功、冲突、失败、冲突说明)
- viewport: 16 态最终截图均为 `412×915`
## Findings
- T03 保留成员印牌、资料行、亲属关系和主操作,提升成员元数据、资料值、关系与失败说明字号。
- T04T05T06 虽复用 `TreeMemberForm`,仍分别按新增亲属、编辑成员、关系维护的真实文案截图;说明、字段、输入和底部提示提升到 `2224rpx`
- 成功、冲突和失败结果保持同一宣纸面板层级;T06 冲突说明继续使用全局 A 系列 `AppDialog`,未使用原生系统弹层。
- 改前/改后截图未见四行表单、双操作或弹窗发生重叠,未生成重复资产。
## Evidence limits
- 当前为 H5 自主设计内部候选;真实成员保存、关系校验、Android/HBuilderX 软键盘和触摸目标仍待验证。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — T01 五状态闭环(2026-07-17
- evidence folder: `docs/design/screens/runtime/2026-07-17/t01/`
- states: 世系树、加载、横向阅读提示、空、失败
- viewport: 五态最终截图均为 `412×915`
## Findings
- 横向世系画布、代际布局、成员节点和固定成员操作卡保持原结构;右侧支系自然位于横向滚动区域,不作为裁切缺陷处理。
- 首轮底部“查看资料/添加亲属”受卷轴中心装饰影响而难读;已改为现有 A 系列 v2 主/次按钮资产并使用清晰无衬线按钮字。
- 工具栏辅助信息、代际标签、节点关系/年份、状态说明和成员元数据提升字号;改前/改后同尺寸截图未新增横向画布之外的溢出。
- 加载、提示、空与失败继续使用同一 T 系列面板和公共 Loading;不新增重复资产。
## Evidence limits
- 当前为 H5 自主设计内部候选;真实手势横拖、惯性滚动、Android/HBuilderX 字体与触摸目标仍待验证。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — T07 模块基准页候选(2026-07-19
- source visual truth path: `docs/superpowers/specs/2026-07-19-module-baseline-accelerated-visual-review-design.md`
- source screenshot: `docs/design/screens/runtime/2026-07-19/module-baseline-audit/04-t07.png`
- implementation screenshot path: `docs/design/screens/runtime/2026-07-19/t07-baseline/01-list-412x915.png`
- full-view comparison evidence: `docs/design/screens/runtime/2026-07-19/t07-baseline/06-before-vs-after.png`
- responsive evidence: `docs/design/screens/runtime/2026-07-19/t07-baseline/05-responsive-contact-sheet.png`
- viewport: 320×568、360×640、360×800、412×915
- state: T07 列表、加载、搜索无结果空态、失败与恢复;最终审批现场为 412×915 列表态
## Findings
没有遗留的 H5 P0/P1/P2 问题。
- 字体与层级:新增“汤氏家谱/主支 · 3 位成员”上下文行;家谱名、页面摘要、成员名、世代关系和档案状态形成清晰层级。四档原尺寸截图中没有换行挤压或截断。
- 间距与布局:上下文、搜索、摘要和三张成员卡沿统一水平边距排列;320×568 至 412×915 均无横向溢出,末张卡完整可达。
- 色彩与视觉令牌:继续使用 T 模块长背景、朱砂标题栏、墨色正文和古金卡片,没有引入新色板。
- 图片与资产:继续复用现有 `g06-search-input-wide.png``application-status-card.png` 和公共 Loading/按钮资产,没有 CSS 绘图、文本图标或临时占位资产。
- 文案与内容:保留三位真实感 Mock 成员、世代、字辈、支系和档案状态;失败态提供“重新查看”,空态保留搜索入口。
- 可用性:搜索操作改为具名按钮语义和 88×72rpx 最低触控区;失败态隐藏不可用搜索并使用 `AppButton` 恢复。实测无结果搜索、清空恢复三项、失败恢复列表均成功。
- 运行质量:9222 中只有一个 `localhost:5173` 项目标签页;运行检查未捕获异常或 4xx/5xx 资源响应。
## Comparison history
- 初始审查发现:页面没有明确当前谱名;失败态仍显示可误操作搜索框且没有恢复动作;“查找”为普通文字点击区,缺少可访问按钮语义。
- 修正:加入当前谱上下文;搜索仅在列表/空态出现;失败态加入公共次按钮;查找操作增加按钮角色、标签与触控区;页面允许自然纵向滚动并禁止横向溢出。
- 修正后证据:前后同尺寸对比见 `06-before-vs-after.png`,四状态与四尺寸均已打开原图检查。
## Focused region evidence
未另做裁切图:412×915 原尺寸截图能够清楚读取上下文、搜索、三张成员卡和失败恢复按钮;四档联系表用于检查缩放与整体密度,继续裁切不会增加判断信息。
## Evidence limits
- 当前只代表 H5 内部候选 QA;用户尚未逐态通过 T07,因此尚未把它标记为 T 模块基准或冻结。
- Android/HBuilderX、系统字体、软键盘、读屏和真实数据量仍未验证。
- 当前谱名与三位成员为视觉阶段 Mock,不表示接口或成员搜索功能已对接。
final result: passed
---
# Design QA — G11G12 十六状态闭环(2026-07-17
- G11 evidence folder: `docs/design/screens/runtime/2026-07-17/g11/`
- G11 states: 表单、加载、成功、失败、无权限、校验、保存反馈
- G12 evidence folder: `docs/design/screens/runtime/2026-07-17/g12/`
- G12 states: 列表、加载、空、编辑、失败、无权限、编辑校验、保存失败、保存反馈
- viewport: 16 态最终截图均为 `412×915`
## Findings
- 两页保留现有 G03 宣纸面板、G 系列背景、公共 Loading、输入框和 A 系列按钮,没有生成重复资产。
- G11 的说明、字段、公开范围、帮助提示和字段校验从偏小规格提升到 `2224rpx`;七态均未出现按钮、输入框或说明重叠。
- G12 的页面说明、世代编号、字辈状态、编辑规则、预览和校验提示提升到 `2224rpx`;列表四行和编辑双按钮仍完整落在面板内。
- 保存成功均使用项目自定义卷轴反馈;校验与保存失败就近显示,不使用原生 Toast、Modal、Loading 或 ActionSheet。
- 运行 smoke 曾复现页面 reload 尚未完成时即执行 DOM 检查的调试协议竞态;已改为等待 `Page.loadEventFired`,连续 5 次运行通过,未通过放宽断言或盲目重试掩盖。
## Evidence limits
- 当前为 H5 自主设计内部候选;真实保存接口、权限、Android/HBuilderX 软键盘、系统字体和触摸目标仍待验证。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — G10 十状态闭环(2026-07-17
- evidence folder: `docs/design/screens/runtime/2026-07-17/g10/`
- states: 列表、加载、空、失败、无权限、审核说明、通过确认、通过完成、拒绝确认、拒绝完成
- viewport: 十态最终截图均为 `412×915`G08G10 流程 smoke 覆盖真实审核交互
## Findings
- 首轮列表的操作文字被 `application-record-card.png` 自带假按钮槽干扰,审核完成后还残留空槽;最终列表改用无假按钮的 `application-status-card.png`,旧展示路径从 G10 合同中移除。
- 待审核操作使用 A 系列 v2 主/次按钮资产,扩大点击容器并改为清晰的无衬线按钮字“拒绝申请/通过申请”;两项在 412px 下均完整显示。
- 审核完成使用一条完整 A 系列卷轴覆盖操作区,分别显示“已通过/已拒绝”,不再保留第二个空按钮槽。
- 手机号、时间、关系、页面说明和状态说明同步提高可读性;公共 Loading、状态卡、审核说明和确认弹窗继续复用现有组件资产。
## Evidence limits
- 当前为 H5 自主设计内部候选;真实权限、并发审核、服务端写入、消息通知和 Android/HBuilderX 仍待验证。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — G09 六状态闭环(2026-07-17
- evidence folder: `docs/design/screens/runtime/2026-07-17/g09/`
- states: 列表、加载、空、失败、撤回确认、撤回完成
- viewport: 六态均为 `412×915`G08G10 流程 smoke 覆盖撤回、已通过跳转和拒绝后重提
## Findings
- 列表三张卡完整覆盖审核中、已通过、未通过;撤回后在原卡片内更新为“已撤回”,不依赖 Toast。
- 保留既有申请状态卡、公共 Loading、A 系列卷轴按钮和全局 `AppDialog`,没有重复生产资产。
- 首轮截图中说明、时间、关系、底部提示和操作文字偏弱;已统一提升到 `2325rpx` 并加深提示色,改前/改后同尺寸截图未出现状态、日期或操作重叠。
- 空与失败状态继续使用同一状态卡构图,恢复动作明确;确认弹窗沿用 A 系列全局样式。
## Evidence limits
- 当前为 H5 自主设计内部候选;真实审核同步、撤回接口、消息通知和 Android/HBuilderX 仍待验证。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — G01 全状态闭环(2026-07-17
- normal evidence: `docs/design/screens/runtime/2026-07-17/01-g01-normal-review-412x900.png`
- empty evidence: `docs/design/screens/runtime/2026-07-17/02-g01-empty-review-no-underline-412x900.png`
- loading evidence: `docs/design/screens/runtime/2026-07-17/03-g01-loading-red-seal-412x900.png`
- error before/after: `docs/design/screens/runtime/2026-07-17/04-g01-error-before-412x900.png``04-g01-error-after-412x900.png`
- responsive evidence: loading 与 error 均检查 `320×568``360×640``360×800``412×915`
## Findings
- 正常列表:用户本轮确认当前状态合适;固定标题、当前家谱、快捷入口和独立滚动列表保持不变。
- 空状态:用户本轮确认整体状态;“创建家谱”残留文字下划线已移除,透明金框、三种入口和连续背景保持不变。
- 加载状态:旧 CSS 细线方框被判定为临时感过强,已按用户批准的 A 方案替换为真实 `brand-seal.png` 红金印牌与 `auth-divider-knot.png` 如意结;页面级四档和 G06 区域级实际截图均无裁切。
- 失败状态:旧不透明白色面板与其他状态不一致,已改为复用空状态透明金框、红金印牌、金色分隔线和 A 系列主卷轴按钮;四档截图中主要内容均可达,`320×568` 保持允许的自然纵向滚动。
- 所有状态继续使用同一 G 系列长背景与固定底栏,没有新增横向溢出或原生系统弹层。
## Evidence limits
- 本节为 H5 内部候选闭环。正常态和空状态获得了用户当前对话确认;加载方案方向获得用户确认,最终运行图与失败态由用户授权自主选择并完成内部设计 QA。
- 尚未获得 G01 整页正式验收,不更新为冻结;Android/HBuilderX 真机或模拟器、系统字体、状态栏、安全区和 4GB 设备长图性能仍待复核。
final result: passed (internal H5 candidate; Android and whole-page acceptance pending)
---
# Design QA — G03 十状态闭环(2026-07-17
- evidence folder: `docs/design/screens/runtime/2026-07-17/g03/`
- states: 创建默认、创建校验、重复建谱提醒、创建提交中、创建失败、首代默认、首代校验、首代提交中、首代失败、创建成功弹窗
- viewport: 10 类状态均为 `412×915` 真实交互截图;`g03-create-flow-runtime-smoke.js` 另覆盖基础响应式尺寸与两步主流程
## Findings
- 保留现有宣纸表单面板、红金主按钮和 A 系列提醒/成功弹窗;这些资产已经形成一致视觉,不重复生成。
- 自定义标题栏已移除“返回”文字,只保留真实向左箭头;左右槽位同步缩为 `72rpx`,标题仍居中。
- 表单说明、访问规则、字段校验和提交失败文案提高字号;修正后十态截图未见字段线、按钮或弹窗发生重叠。
- 创建与首代提交中继续使用主按钮内文字变化,避免短操作同时出现页面 Loading;失败信息留在对应表单语境中,恢复路径清楚。
- 重复提醒和创建成功均使用项目自定义弹窗,没有原生 Modal、Toast 或 Loading。
## Evidence limits
- 本节为自主设计 QA 的 H5 内部候选,未获得用户逐状态确认,也不表示 G03 已正式验收或冻结。
- Android/HBuilderX 的软键盘、输入法顶起、系统字体、返回手势和触摸目标仍待复核。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — G06 八状态闭环(2026-07-17
- evidence folder: `docs/design/screens/runtime/2026-07-17/g06/`
- states: 搜索初始、搜索加载、搜索结果、搜索空结果、搜索失败、邀请码初始、邀请码无效、邀请码有效
- viewport: 八态 `412×915`;运行 smoke 另覆盖响应式与搜索/邀请码真实交互
## Findings
- 标题条、双模式、输入/验证按钮、结果卡片、空状态与邀请码状态均保持现有 G 视觉资产;没有重复生成已经确认的图片。
- 公共区域级 Loading 在页面实际结构中重新验证,印牌、分隔线和说明文字没有与搜索控件重叠。
- 搜索失败态原“重新搜索”纯文字操作不符合统一按钮体系,已替换为 A 系列红金主卷轴按钮;其他状态无需重设计。
- 新增失败和无效邀请码的真实截图触发能力,不再只检查结果/空/有效邀请码。
## Evidence limits
- 当前为 H5 内部候选;真实网络搜索、邀请码后端校验、软键盘和 Android/HBuilderX 仍待验证。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — G05 七状态闭环(2026-07-17
- evidence folder: `docs/design/screens/runtime/2026-07-17/g05/`
- states: 管理员总览、成员总览、公开预览、加载、空、失败、无权限
- viewport: 七态 `412×915``g05-overview-runtime-smoke.js` 另覆盖 `320×568``360×800``412×915`
## Findings
- 管理员总览保留原设计;成员态原先两个空功能格已补为“仅管理员可用”的非交互说明,不再像数据缺失。
- 公开预览将第五项“所属上级谱”放入整行身份区,公开说明归入底部说明面板,主按钮保持独立,不再跨越金色分隔线。
- 加载、空、失败和无权限状态不再叠压旧四格总览底图;统一使用连续 G 背景、透明金框、红金印牌、公共 Loading 和 A 系列主卷轴按钮。
- 七态运行 smoke 保持权限动作数量、公开申请入口和响应式宽度合同。
## Evidence limits
- 当前为 H5 自主设计内部候选,未获得用户逐态确认;Android/HBuilderX 和真实权限/网络接口仍待复核。
final result: passed (internal H5 candidate; Android and user acceptance pending)
---
# Design QA — G01 申请次要文字可读性
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

+562 -112
View File
@@ -14,18 +14,41 @@
<view class="genealogy-content">
<view v-if="isLoading" class="state-panel state-panel--loading">
<view class="loading-seal"></view>
<text class="state-title">正在整理家谱</text>
<text class="state-copy">请稍候家族记忆正在归卷</text>
<AppLoading
text="正在整理家谱"
description="请稍候,家族记忆正在归卷。"
/>
</view>
<view v-else-if="hasError" class="state-panel state-panel--error">
<image class="state-panel__skin" src="/static/assets/modules/genealogy/opaque/g01-empty-panel.png" mode="scaleToFill" />
<view class="state-panel__content">
<image
class="error-panel__frame"
src="/static/assets/modules/genealogy/transparent/g01-empty-panel-frame.png"
mode="scaleToFill"
/>
<view class="error-panel__content">
<image
class="error-panel__seal"
src="/static/assets/foundation/transparent/brand-seal.png"
mode="aspectFit"
/>
<text class="state-title">暂时无法读取家谱</text>
<text class="state-copy">网络或服务暂不可用请稍后重新查看</text>
<view class="state-retry" hover-class="action-hover" @click="retryLoad">
<image class="state-retry__skin" src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" />
<image
class="error-panel__divider"
src="/static/assets/modules/genealogy/transparent/section-divider.png"
mode="scaleToFill"
/>
<view
class="state-retry"
hover-class="action-hover"
@click="retryLoad"
>
<image
class="state-retry__skin"
src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png"
mode="aspectFit"
/>
<text class="state-retry__copy">重新加载</text>
</view>
</view>
@@ -142,15 +165,31 @@
hover-class="action-hover"
@click="openApplication(item)"
>
<image class="application-record__skin" src="/static/assets/modules/genealogy/transparent/list-slip-frame.png" mode="scaleToFill" />
<image
class="application-record__skin"
src="/static/assets/modules/genealogy/transparent/list-slip-frame.png"
mode="scaleToFill"
/>
<view class="application-record__main">
<view class="application-record__title-row">
<text class="application-record__name">{{ item.name }}</text>
<text class="application-record__status" :class="`application-record__status--${item.tone}`">{{ item.statusLabel }}</text>
<text class="application-record__name">{{
item.name
}}</text>
<text
class="application-record__status"
:class="`application-record__status--${item.tone}`"
>{{ item.statusLabel }}</text
>
</view>
<text class="application-record__copy">{{ item.description }}</text>
<text class="application-record__copy">{{
item.description
}}</text>
</view>
<image class="application-record__chevron" src="/static/assets/foundation/transparent/chevron-right.png" mode="aspectFit" />
<image
class="application-record__chevron"
src="/static/assets/foundation/transparent/chevron-right.png"
mode="aspectFit"
/>
</view>
</view>
@@ -179,8 +218,8 @@
<view v-else class="genealogy-empty-state">
<view class="empty-panel">
<image
class="empty-panel__skin"
src="/static/assets/modules/genealogy/opaque/g01-empty-panel.png"
class="empty-panel__frame"
src="/static/assets/modules/genealogy/transparent/g01-empty-panel-frame.png"
mode="scaleToFill"
/>
<view class="empty-panel__content">
@@ -193,7 +232,9 @@
<text class="empty-seal__text">家谱</text>
</view>
<text class="empty-title">还没有加入任何家谱</text>
<text class="empty-copy">先搜索家族是否已有家谱再选择加入或创建</text>
<text class="empty-copy"
>先搜索家族是否已有家谱再选择加入或创建</text
>
<image
class="empty-divider"
src="/static/assets/modules/genealogy/transparent/section-divider.png"
@@ -209,7 +250,10 @@
src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png"
mode="aspectFit"
/>
<text class="empty-search-action__copy empty-search-action__copy--primary">搜索家谱</text>
<text
class="empty-search-action__copy empty-search-action__copy--primary"
>搜索家谱</text
>
</view>
<view
class="empty-invite-action"
@@ -223,48 +267,114 @@
/>
<text class="empty-invite-action__copy">邀请码加入</text>
</view>
<view class="empty-create-action" hover-class="action-hover" @click="createGenealogy">
<view
class="empty-create-action"
hover-class="action-hover"
@click="createGenealogy"
>
<text class="empty-create-action__copy">创建家谱</text>
</view>
<text class="empty-create-note">确认没有现有家谱后再创建避免重复建谱</text>
<text class="empty-create-note"
>确认没有现有家谱后再创建避免重复建谱</text
>
</view>
</view>
</view>
</view>
<view v-if="addDialogVisible" class="add-dialog-layer" @click="closeAddDialog">
<view
v-if="addDialogVisible"
class="add-dialog-layer"
@click="closeAddDialog"
>
<view class="add-dialog" @click.stop>
<image class="add-dialog__skin" src="/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png" mode="aspectFit" />
<view class="add-dialog__content">
<view class="add-dialog__body">
<view class="add-dialog__heading">
<text class="dialog-title">添加家谱</text>
<text class="dialog-copy">建议先搜索已有家谱避免重复创建</text>
<view
class="add-dialog__close"
role="button"
aria-label="关闭"
hover-class="action-hover"
@click="closeAddDialog"
>
<image
class="add-dialog__close-icon"
src="/static/assets/modules/genealogy/transparent/g01-dialog-close.png"
mode="aspectFit"
/>
</view>
</view>
<view class="add-dialog__actions">
<AppButton block label="搜索家谱" @click="applyToJoin" />
<AppButton block type="secondary" label="邀请码加入" @click="joinByInvite" />
<AppButton block type="secondary" label="确认没有现有家谱,继续创建" @click="createGenealogy" />
<view class="dialog-close" @click="closeAddDialog">取消</view>
<AppButton
block
type="secondary"
label="邀请码加入"
@click="joinByInvite"
/>
<AppButton
block
type="secondary"
label="继续创建家谱"
@click="createGenealogy"
/>
</view>
</view>
</view>
</view>
</view>
<view v-if="switcherVisible" class="genealogy-switcher-layer" @click="closeSwitcher">
<view class="genealogy-switcher" @click.stop>
<image class="genealogy-switcher__skin" src="/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png" mode="aspectFit" />
<view
v-if="switcherVisible"
class="genealogy-switcher-layer"
@click="closeSwitcher"
>
<view
class="genealogy-switcher"
role="dialog"
aria-modal="true"
aria-label="切换当前家谱"
@click.stop
>
<view class="genealogy-switcher__content">
<text class="dialog-title">切换当前家谱</text>
<view
class="genealogy-switcher__close"
role="button"
aria-label="关闭"
hover-class="action-hover"
@click="closeSwitcher"
>
<image
class="genealogy-switcher__close-icon"
src="/static/assets/modules/genealogy/transparent/g01-dialog-close.png"
mode="aspectFit"
/>
</view>
<scroll-view class="genealogy-switcher__list" scroll-y>
<view
v-for="item in availableGenealogies"
:key="item.id"
class="switcher-item"
:class="{ 'switcher-item--active': item.id === selectedGenealogyId }"
:class="{
'switcher-item--active': item.id === selectedGenealogyId,
}"
@click="selectGenealogy(item)"
>
<view>
<text class="switcher-item__name">{{ item.name }}</text>
<text class="switcher-item__meta">{{ item.location }} · {{ item.memberCount }} 位成员</text>
<text class="switcher-item__meta"
>{{ item.location }} · {{ item.memberCount }} 位成员</text
>
</view>
<text class="switcher-item__state">{{ item.id === selectedGenealogyId ? '当前' : '选择' }}</text>
<text class="switcher-item__state">{{
item.id === selectedGenealogyId ? "当前" : "选择"
}}</text>
</view>
<view class="dialog-close" @click="closeSwitcher">关闭</view>
</scroll-view>
</view>
</view>
</view>
@@ -275,7 +385,8 @@
<script setup>
import { computed, nextTick, ref } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { onBackPress, onLoad, onShow } from "@dcloudio/uni-app";
import AppLoading from "@/components/AppLoading.vue";
import AppTabbar from "@/components/AppTabbar.vue";
import AppButton from "@/components/AppButton.vue";
import GenealogyCard from "@/components/GenealogyCard.vue";
@@ -322,7 +433,9 @@ onShow(() => {
const unreadCount = computed(
() => notifications.filter((item) => item.unread).length,
);
const hasGenealogies = computed(() => !forceEmptyState.value && list.value.length > 0);
const hasGenealogies = computed(
() => !forceEmptyState.value && list.value.length > 0,
);
const isListLayout = computed(
() => !isLoading.value && !hasError.value && hasGenealogies.value,
);
@@ -333,15 +446,36 @@ const joinedGenealogies = computed(() =>
list.value.filter((item, index) => index > 0),
);
const availableGenealogies = computed(() => list.value);
const currentGenealogy = computed(() =>
availableGenealogies.value.find((item) => item.id === selectedGenealogyId.value) || availableGenealogies.value[0],
const currentGenealogy = computed(
() =>
availableGenealogies.value.find(
(item) => item.id === selectedGenealogyId.value,
) || availableGenealogies.value[0],
);
// 仅用于页面样式阶段覆盖加入申请的关键状态,不作为接口数据。
const applicationRecords = [
{ id: "pending", name: "汤氏南阳宗谱", statusLabel: "审核中", tone: "pending", description: "申请已提交,等待管理员审核" },
{ id: "rejected", name: "汤氏清河家谱", statusLabel: "被拒绝", tone: "rejected", description: "可修改关系说明后重新申请" },
{ id: "removed", name: "汤氏汝南支谱", statusLabel: "已退出", tone: "muted", description: "如需恢复成员身份,可重新申请加入" },
{
id: "pending",
name: "汤氏南阳宗谱",
statusLabel: "审核中",
tone: "pending",
description: "申请已提交,等待管理员审核",
},
{
id: "rejected",
name: "汤氏清河家谱",
statusLabel: "被拒绝",
tone: "rejected",
description: "可修改关系说明后重新申请",
},
{
id: "removed",
name: "汤氏汝南支谱",
statusLabel: "已退出",
tone: "muted",
description: "如需恢复成员身份,可重新申请加入",
},
];
const shortcuts = [
@@ -368,18 +502,43 @@ const shortcuts = [
];
const openGenealogy = (genealogy) =>
uni.navigateTo({ url: `/pages/genealogy/g05-genealogy-overview?genealogyId=${genealogy.id}` });
uni.navigateTo({
url: `/pages/genealogy/g05-genealogy-overview?genealogyId=${genealogy.id}`,
});
const createGenealogy = () =>
uni.navigateTo({ url: "/pages/genealogy/g03-create-genealogy" });
const applyToJoin = () => uni.navigateTo({ url: "/pages/genealogy/g06-search-genealogies" });
const joinByInvite = () => uni.navigateTo({ url: "/pages/genealogy/g06-search-genealogies?mode=invite" });
const applyToJoin = () =>
uni.navigateTo({ url: "/pages/genealogy/g06-search-genealogies" });
const joinByInvite = () =>
uni.navigateTo({
url: "/pages/genealogy/g06-search-genealogies?mode=invite",
});
const toNotifications = () =>
uni.navigateTo({ url: "/pages/notification/n01-message-center" });
const openAddDialog = () => { addDialogVisible.value = true; };
const closeAddDialog = () => { addDialogVisible.value = false; };
const openSwitcher = () => { switcherVisible.value = true; };
const closeSwitcher = () => { switcherVisible.value = false; };
const openAddDialog = () => {
addDialogVisible.value = true;
};
const closeAddDialog = () => {
addDialogVisible.value = false;
};
onBackPress(() => {
if (switcherVisible.value) {
closeSwitcher();
return true;
}
if (addDialogVisible.value) {
closeAddDialog();
return true;
}
return false;
});
const openSwitcher = () => {
switcherVisible.value = true;
};
const closeSwitcher = () => {
switcherVisible.value = false;
};
const handleListScroll = (event) => {
currentListScrollTop.value = Number(event?.detail?.scrollTop || 0);
};
@@ -396,7 +555,9 @@ const selectGenealogy = async (genealogy) => {
};
const openApplication = (record) => {
const statusQuery = record.id === "pending" ? "pending" : record.id;
uni.navigateTo({ url: `/pages/genealogy/g09-my-applications?status=${statusQuery}` });
uni.navigateTo({
url: `/pages/genealogy/g09-my-applications?status=${statusQuery}`,
});
};
const retryLoad = () => {
hasError.value = false;
@@ -530,7 +691,7 @@ const openShortcut = (key) => {
flex: 0 0 auto;
margin-left: auto;
color: $brand-red;
font-size: 23rpx;
font-size: 28rpx;
}
.current-info-divider {
position: relative;
@@ -560,14 +721,15 @@ const openShortcut = (key) => {
margin: 0;
white-space: nowrap;
.current-meta-item-text{
.current-meta-item-text {
white-space: nowrap;
}
}
.current-meta-icon {
width: 32rpx;
height: 32rpx;
width: 36rpx;
height: 36rpx;
margin-right: 8rpx;
opacity: 1;
}
.shortcut-grid {
@@ -648,11 +810,35 @@ const openShortcut = (key) => {
box-sizing: border-box;
padding: 22rpx 26rpx;
}
.application-record + .application-record { margin-top: 12rpx; }
.application-record__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
.application-record__main { position: relative; z-index: 1; min-width: 0; flex: 1; }
.application-record__title-row { display: flex; align-items: center; justify-content: space-between; }
.application-record__name { overflow: hidden; color: $ink; font-family: "STKaiti", "KaiTi", serif; font-size: 32rpx; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
.application-record + .application-record {
margin-top: 12rpx;
}
.application-record__skin {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.application-record__main {
position: relative;
z-index: 1;
min-width: 0;
flex: 1;
}
.application-record__title-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.application-record__name {
overflow: hidden;
color: $ink;
font-family: "STKaiti", "KaiTi", serif;
font-size: 32rpx;
font-weight: 700;
text-overflow: ellipsis;
white-space: nowrap;
}
.application-record__status {
flex: 0 0 auto;
margin-left: 16rpx;
@@ -660,8 +846,12 @@ const openShortcut = (key) => {
font-size: 27rpx;
font-weight: 600;
}
.application-record__status--rejected { color: #a7160c; }
.application-record__status--muted { color: #62584c; }
.application-record__status--rejected {
color: #a7160c;
}
.application-record__status--muted {
color: #62584c;
}
.application-record__copy {
display: block;
margin-top: 10rpx;
@@ -670,7 +860,13 @@ const openShortcut = (key) => {
font-weight: 500;
line-height: 1.4;
}
.application-record__chevron { position: relative; z-index: 1; width: 32rpx; height: 32rpx; margin-left: 14rpx; }
.application-record__chevron {
position: relative;
z-index: 1;
width: 32rpx;
height: 32rpx;
margin-left: 14rpx;
}
.create-action {
display: flex;
@@ -744,25 +940,80 @@ const openShortcut = (key) => {
line-height: 1.7;
}
.loading-seal {
border: 2rpx solid $gold;
.state-panel--error {
height: 1120rpx;
min-height: 1120rpx;
padding: 0;
}
.loading-seal {
width: 92rpx;
height: 92rpx;
border-radius: 50%;
.error-panel__frame {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.error-panel__content {
position: relative;
z-index: 1;
display: flex;
width: 100%;
height: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: 50rpx 28rpx 46rpx;
}
.error-panel__seal {
width: 132rpx;
height: 136rpx;
}
.state-panel--error .state-title {
display: block;
margin-top: 30rpx;
color: #392719;
font-family: "STKaiti", "KaiTi", serif;
font-size: 42rpx;
font-weight: 700;
letter-spacing: 2rpx;
}
.state-panel--error .state-copy {
display: block;
min-height: 72rpx;
margin-top: 16rpx;
color: #786654;
font-size: 27rpx;
line-height: 1.65;
}
.error-panel__divider {
display: block;
width: 100%;
height: 30rpx;
margin-top: 28rpx;
}
.state-panel--error .state-retry {
width: 560rpx;
height: 124rpx;
margin-top: 22rpx;
}
.genealogy-empty-state {
min-height: 856rpx;
min-height: 1120rpx;
}
.empty-panel {
position: relative;
height: 856rpx;
height: 1120rpx;
}
.empty-panel__skin {
.empty-panel__frame {
position: absolute;
inset: 0;
width: 100%;
@@ -777,16 +1028,17 @@ const openShortcut = (key) => {
height: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: 58rpx 60rpx 54rpx;
padding: 50rpx 28rpx 46rpx;
text-align: center;
}
.empty-seal {
position: relative;
display: flex;
width: 92rpx;
height: 142rpx;
width: 120rpx;
height: 184rpx;
align-items: center;
justify-content: center;
}
@@ -811,47 +1063,47 @@ const openShortcut = (key) => {
.empty-title {
display: block;
margin-top: 24rpx;
margin-top: 30rpx;
color: #392719;
font-family: "STKaiti", "KaiTi", serif;
font-size: 40rpx;
font-size: 48rpx;
font-weight: 700;
letter-spacing: 2rpx;
}
.empty-copy {
display: block;
min-height: 74rpx;
margin-top: 14rpx;
min-height: 84rpx;
margin-top: 18rpx;
color: #786654;
font-size: 24rpx;
line-height: 1.75;
font-size: 28rpx;
line-height: 1.65;
}
.empty-divider {
display: block;
width: 100%;
height: 30rpx;
margin-top: 16rpx;
margin-top: 26rpx;
}
.empty-search-action,
.empty-invite-action {
position: relative;
display: flex;
width: 468rpx;
height: 104rpx;
width: 560rpx;
height: 124rpx;
align-items: center;
justify-content: center;
overflow: hidden;
}
.empty-search-action {
margin-top: 20rpx;
margin-top: 26rpx;
}
.empty-invite-action {
margin-top: 14rpx;
margin-top: 22rpx;
}
.empty-search-action__skin,
@@ -868,42 +1120,240 @@ const openShortcut = (key) => {
z-index: 1;
color: #7b4e24;
font-family: "STKaiti", "KaiTi", serif;
font-size: 29rpx;
font-size: 34rpx;
font-weight: 700;
letter-spacing: 3rpx;
}
.empty-search-action__copy--primary { color: #fff9ec; }
.empty-create-action { display: flex; align-items: center; justify-content: center; min-height: 64rpx; margin-top: 8rpx; color: $brand-red; }
.empty-create-action__copy { font-family: "STKaiti", "KaiTi", serif; font-size: 27rpx; text-decoration: underline; text-underline-offset: 8rpx; }
.empty-create-note { display: block; color: $ink-muted; font-size: 20rpx; line-height: 30rpx; }
.empty-search-action__copy--primary {
color: #fff9ec;
}
.empty-create-action {
display: flex;
align-items: center;
justify-content: center;
min-height: 76rpx;
margin-top: 14rpx;
color: $brand-red;
}
.empty-create-action__copy {
font-family: "STKaiti", "KaiTi", serif;
font-size: 31rpx;
}
.empty-create-note {
display: block;
color: $ink-muted;
font-size: 26rpx;
line-height: 38rpx;
}
.state-panel__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
.state-panel__content { position: relative; z-index: 1; display: flex; min-height: 540rpx; flex-direction: column; align-items: center; justify-content: center; padding: 42rpx; box-sizing: border-box; }
.state-retry { position: relative; display: flex; width: 360rpx; height: 96rpx; align-items: center; justify-content: center; margin-top: 30rpx; overflow: hidden; }
.state-retry__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
.state-retry__copy { position: relative; z-index: 1; color: #fff9ec; font-family: "STKaiti", "KaiTi", serif; font-size: 29rpx; }
.state-retry {
position: relative;
display: flex;
width: 360rpx;
height: 96rpx;
align-items: center;
justify-content: center;
margin-top: 30rpx;
overflow: hidden;
}
.state-retry__skin {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.state-retry__copy {
position: relative;
z-index: 1;
color: #fff9ec;
font-family: "STKaiti", "KaiTi", serif;
font-size: 29rpx;
}
.add-dialog-layer,
.genealogy-switcher-layer { position: fixed; z-index: 40; inset: 0; display: flex; align-items: center; justify-content: center; box-sizing: border-box; padding: 40rpx; background: rgba(34, 20, 12, 0.58); }
.add-dialog,
.genealogy-switcher { position: relative; width: 100%; max-width: 670rpx; min-height: 720rpx; }
.add-dialog__skin,
.genealogy-switcher__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
.add-dialog__content,
.genealogy-switcher__content { position: relative; z-index: 1; display: flex; min-height: 720rpx; flex-direction: column; align-items: center; box-sizing: border-box; padding: 78rpx 58rpx 50rpx; }
.dialog-title { color: $brand-red; font-family: "STKaiti", "KaiTi", serif; font-size: 42rpx; font-weight: 700; letter-spacing: 4rpx; }
.dialog-copy { margin-top: 18rpx; color: $ink-muted; font-size: 23rpx; }
.add-dialog__content > .app-button:first-of-type { margin-top: 34rpx; }
.add-dialog__content > .app-button + .app-button { margin-top: 10rpx; }
.dialog-close { display: flex; min-height: 72rpx; align-items: center; justify-content: center; margin-top: auto; color: $ink-muted; font-size: 24rpx; }
.switcher-item { display: flex; width: 100%; min-height: 108rpx; align-items: center; justify-content: space-between; box-sizing: border-box; padding: 18rpx 12rpx; border-bottom: 1rpx solid rgba(181, 138, 75, 0.42); }
.switcher-item:first-of-type { margin-top: 28rpx; }
.add-dialog-layer {
position: fixed;
z-index: 40;
inset: 0;
display: flex;
align-items: flex-end;
justify-content: center;
box-sizing: border-box;
background: rgba(34, 20, 12, 0.68);
}
.add-dialog {
position: relative;
width: 100%;
min-height: 780rpx;
max-height: calc(100vh - 80rpx);
box-sizing: border-box;
border: 1px solid transparent;
border-image-source: url("/static/assets/modules/genealogy/transparent/g01-add-sheet-background-v3.png");
border-image-slice: 220 0 1 0 fill;
border-image-width: 118rpx 0 1rpx;
border-image-repeat: stretch;
}
.add-dialog__content {
position: relative;
z-index: 2;
display: flex;
min-height: 780rpx;
max-height: calc(100vh - 80rpx);
flex-direction: column;
align-items: stretch;
box-sizing: border-box;
padding: 96rpx 52rpx calc(96rpx + env(safe-area-inset-bottom));
overflow-y: auto;
}
.add-dialog__body {
margin: auto 0;
}
.add-dialog__heading {
position: relative;
padding-right: 96rpx;
}
.add-dialog .dialog-title,
.add-dialog .dialog-copy {
display: block;
text-align: left;
}
.add-dialog__close {
position: absolute;
z-index: 3;
top: 0;
right: -22rpx;
display: flex;
width: 80rpx;
height: 80rpx;
align-items: center;
justify-content: center;
}
.add-dialog__close-icon {
width: 80rpx;
height: 80rpx;
}
.add-dialog__actions {
display: flex;
flex-direction: column;
align-items: center;
margin: 62rpx -32rpx 0;
}
.add-dialog__actions > .app-button {
width: 595rpx;
max-width: 100%;
min-height: 96rpx;
}
.add-dialog__actions > .app-button + .app-button {
margin-top: 24rpx;
}
.genealogy-switcher-layer {
position: fixed;
z-index: 40;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: 40rpx;
background: rgba(34, 20, 12, 0.58);
}
.genealogy-switcher {
position: relative;
width: 670rpx;
max-width: 100%;
min-height: 600rpx;
max-height: calc(100vh - 120rpx);
box-sizing: border-box;
border: 1px solid transparent;
border-image-source: url("/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png");
border-image-slice: 300 260 360 260 fill;
border-image-width: 110rpx 48rpx 132rpx 48rpx;
border-image-repeat: stretch;
}
.genealogy-switcher__content {
position: relative;
z-index: 1;
display: flex;
min-height: 600rpx;
max-height: calc(100vh - 120rpx);
flex-direction: column;
align-items: center;
box-sizing: border-box;
padding: 120rpx 58rpx 140rpx;
}
.genealogy-switcher__close {
position: absolute;
z-index: 3;
top: 78rpx;
right: 34rpx;
display: flex;
width: 80rpx;
height: 80rpx;
align-items: center;
justify-content: center;
}
.genealogy-switcher__close-icon {
width: 80rpx;
height: 80rpx;
}
.genealogy-switcher__list {
width: 100%;
min-height: 0;
max-height: calc(100vh - 456rpx);
margin-top: 24rpx;
overflow-y: auto;
}
.dialog-title {
color: $brand-red;
font-family: "STKaiti", "KaiTi", serif;
font-size: 42rpx;
font-weight: 700;
letter-spacing: 4rpx;
}
.dialog-copy {
margin-top: 12rpx;
color: $ink-muted;
font-size: 25rpx;
line-height: 1.5;
}
.switcher-item {
display: flex;
width: 100%;
min-height: 112rpx;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding: 18rpx 16rpx;
border: 1rpx solid transparent;
border-bottom-color: rgba(181, 138, 75, 0.42);
}
.switcher-item__name,
.switcher-item__meta { display: block; }
.switcher-item__name { color: $ink; font-family: "STKaiti", "KaiTi", serif; font-size: 31rpx; font-weight: 700; }
.switcher-item__meta { margin-top: 6rpx; color: $ink-muted; font-size: 21rpx; }
.switcher-item__state { color: $brand-red; font-size: 23rpx; }
.switcher-item--active .switcher-item__name { color: $brand-red; }
.switcher-item__meta {
display: block;
}
.switcher-item__name {
color: $ink;
font-family: "STKaiti", "KaiTi", serif;
font-size: 32rpx;
font-weight: 700;
}
.switcher-item__meta {
margin-top: 6rpx;
color: #62584c;
font-size: 24rpx;
}
.switcher-item__state {
color: $brand-red;
font-size: 25rpx;
font-weight: 600;
}
.switcher-item--active {
border-color: rgba(159, 23, 15, 0.22);
background: rgba(159, 23, 15, 0.055);
}
.switcher-item--active .switcher-item__name {
color: $brand-red;
}
.action-hover {
opacity: 0.82;
@@ -921,8 +1371,8 @@ const openShortcut = (key) => {
font-size: 23rpx;
}
.current-meta-icon {
width: 28rpx;
height: 28rpx;
width: 32rpx;
height: 32rpx;
}
.create-action {
width: 320rpx;
+436 -3
View File
@@ -1,4 +1,437 @@
<!-- 页面编号M-01用途个人中心首页提醒与服务导航 -->
<template><view class="profile-page" :class="{'profile-state--ready':profileState==='ready','profile-state--error':profileState==='error'}"><ModulePageBackground module="profile"/><view class="profile-page__header"><PageHeader title="我的" action="资料" @action="toProfile"/></view><view class="profile-content"><view v-if="profileState==='ready'" class="profile-panel"><image class="profile-panel__skin" src="/static/assets/modules/genealogy/opaque/g03-create-flow-panel.png" mode="scaleToFill"/><view class="profile-panel__body"><view class="profile-heading"><image src="/static/assets/modules/genealogy/transparent/current-seal-frame.png" mode="scaleToFill"/><view><text>汤文清</text><text>139****6421 · 家谱成员</text></view></view><view class="profile-todo" @click="toNotifications"><image src="/static/assets/modules/genealogy/opaque/application-status-card.png" mode="scaleToFill"/><view><text>待你处理</text><text>2 条家谱提醒与审核通知</text></view></view><view v-for="item in menuItems" :key="item.label" class="profile-menu" @click="openItem(item)"><image src="/static/assets/modules/genealogy/opaque/g06-search-input-wide.png" mode="scaleToFill"/><view><text>{{item.label}}</text><text>{{item.note}}</text></view><text>查看</text></view></view></view><view v-else class="profile-error"><image src="/static/assets/modules/genealogy/opaque/application-status-card.png" mode="scaleToFill"/><view><text>个人资料暂不可用</text><text>请稍后重新进入,账号和家谱资料不会受到影响。</text></view></view></view><AppTabbar active="profile"/></view></template>
<script setup>import{ref}from'vue';import{onLoad}from'@dcloudio/uni-app';import ModulePageBackground from'@/components/ModulePageBackground.vue';import PageHeader from'@/components/PageHeader.vue';import AppTabbar from'@/components/AppTabbar.vue';const profileState=ref('ready');const menuItems=[{label:'账号与安全',note:'密码、手机与设备',url:'/pages/profile/m03-security-settings'},{label:'帮助与反馈',note:'使用说明与问题反馈',url:'/pages/profile/m06-help-center'},{label:'关于家谱',note:'协议、隐私与版本',url:'/pages/profile/m10-about-settings'}];onLoad(query=>{profileState.value=query.state==='error'?'error':'ready'});const toProfile=()=>uni.navigateTo({url:'/pages/profile/m02-edit-profile'});const toNotifications=()=>uni.navigateTo({url:'/pages/notification/n01-message-center'});const openItem=item=>uni.navigateTo({url:item.url})</script>
<style scoped lang="scss">.profile-page{position:relative;min-height:100vh;overflow:hidden;background:$paper}.profile-page__header,.profile-content{position:relative;z-index:2}.profile-content{padding:18rpx 24rpx 190rpx}.profile-panel{position:relative;width:100%;height:min(650px,calc((100vw - 24px)*1.5))}.profile-panel__skin{position:absolute;inset:0;width:100%;height:100%}.profile-panel__body{position:absolute;inset:7% 8%}.profile-heading{display:flex;align-items:center;gap:18rpx}.profile-heading>image{width:76rpx;height:98rpx}.profile-heading text{display:block}.profile-heading text:first-child{color:$ink;font-family:STKaiti,KaiTi,serif;font-size:35rpx;font-weight:700}.profile-heading text:last-child{margin-top:7rpx;color:$ink-muted;font-size:19rpx}.profile-todo{position:relative;height:124rpx;margin-top:18rpx}.profile-todo>image,.profile-menu>image,.profile-error>image{position:absolute;inset:0;width:100%;height:100%}.profile-todo>view{position:absolute;inset:22% 9%;z-index:1}.profile-todo text{display:block}.profile-todo text:first-child{color:$brand-red;font-size:22rpx;font-weight:700}.profile-todo text:last-child{margin-top:7rpx;color:$ink-muted;font-size:19rpx}.profile-menu{position:relative;height:76rpx;margin-top:11rpx}.profile-menu>view{position:absolute;top:14rpx;left:22rpx;z-index:1}.profile-menu>view text{display:block}.profile-menu>view text:first-child{color:$ink;font-size:21rpx;font-weight:700}.profile-menu>view text:last-child{margin-top:3rpx;color:$ink-muted;font-size:17rpx}.profile-menu>text{position:absolute;top:29rpx;right:22rpx;z-index:1;color:$brand-red;font-size:18rpx}.profile-error{position:relative;height:230rpx;margin-top:70rpx}.profile-error>view{position:absolute;inset:27% 12%;z-index:1;text-align:center}.profile-error text{display:block}.profile-error text:first-child{color:$ink;font-family:STKaiti,KaiTi,serif;font-size:30rpx;font-weight:700}.profile-error text:last-child{margin-top:13rpx;color:$ink-muted;font-size:20rpx}</style>
<template>
<view
class="profile-page"
:class="{
'profile-state--ready': profileState === 'ready',
'profile-state--error': profileState === 'error',
}"
>
<ModulePageBackground module="profile" />
<view class="profile-page__header">
<PageHeader
title="我的"
:action="profileState === 'ready' ? '资料' : ''"
@action="toProfile"
/>
</view>
<view class="profile-content">
<template v-if="profileState === 'ready'">
<view class="profile-hero">
<image
class="profile-hero__frame"
src="/static/assets/modules/profile/transparent/m01-profile-summary-card.png"
mode="scaleToFill"
/>
<image
class="profile-hero__hall"
src="/static/assets/foundation/transparent/root-header-hall.png"
mode="aspectFit"
/>
<image
class="profile-hero__cloud"
src="/static/assets/foundation/transparent/auth-title-cloud.png"
mode="aspectFit"
/>
<view class="profile-hero__label"
><text></text><text></text><text></text><text></text></view
>
<view class="profile-identity">
<image
class="profile-identity__seal"
src="/static/assets/foundation/transparent/brand-seal.png"
mode="aspectFit"
/>
<view class="profile-identity__copy">
<text class="profile-identity__name">汤文清</text>
<text class="profile-identity__role">汤氏家谱 · 家谱成员</text>
<text class="profile-identity__phone">139****6421</text>
</view>
</view>
</view>
<view
class="profile-scroll-notice"
role="button"
aria-label="查看待处理消息"
@click="toNotifications"
>
<image
class="profile-scroll-notice__skin"
src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png"
mode="scaleToFill"
/>
<view class="profile-scroll-notice__copy">
<text>待你处理</text>
<text>2 条家谱提醒与审核通知</text>
</view>
</view>
<view class="profile-services">
<view class="profile-section-heading">
<image
src="/static/assets/foundation/transparent/auth-divider-knot.png"
mode="aspectFit"
/>
<text>服务与设置</text>
<image
src="/static/assets/foundation/transparent/auth-divider-knot.png"
mode="aspectFit"
/>
</view>
<view class="profile-menu-list">
<view
v-for="item in menuItems"
:key="item.label"
class="profile-menu"
role="button"
:aria-label="`查看${item.label}`"
@click="openItem(item)"
>
<image
class="profile-menu__icon"
:src="item.icon"
mode="aspectFit"
/>
<view class="profile-menu__copy">
<text class="profile-menu__label">{{ item.label }}</text>
<text class="profile-menu__note">{{ item.note }}</text>
</view>
<image
class="profile-menu__chevron"
src="/static/assets/foundation/transparent/chevron-right.png"
mode="aspectFit"
/>
</view>
</view>
</view>
</template>
<view v-else class="profile-error">
<image
class="profile-error__skin"
src="/static/assets/modules/profile/transparent/m01-profile-summary-card.png"
mode="scaleToFill"
/>
<view class="profile-error__copy">
<text class="profile-error__title">个人资料暂不可用</text>
<text class="profile-error__description"
>请稍后重新进入账号和家谱资料不会受到影响</text
>
</view>
<AppButton
block
type="secondary"
label="重新查看"
@click="restoreProfile"
/>
</view>
</view>
<AppTabbar active="profile" />
</view>
</template>
<script setup>
import { ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppTabbar from "@/components/AppTabbar.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
const profileState = ref("ready");
const menuItems = [
{
label: "账号与安全",
note: "密码、手机与设备",
icon: "/static/assets/modules/auth/transparent/a01-icon-lock-v1.png",
url: "/pages/profile/m03-security-settings",
},
{
label: "帮助与反馈",
note: "使用说明与问题反馈",
icon: "/static/assets/foundation/transparent/notice.png",
url: "/pages/profile/m06-help-center",
},
{
label: "关于家谱",
note: "协议、隐私与版本",
icon: "/static/assets/foundation/transparent/brand-seal.png",
url: "/pages/profile/m10-about-settings",
},
];
onLoad((query) => {
profileState.value = query.state === "error" ? "error" : "ready";
});
const restoreProfile = () => {
profileState.value = "ready";
};
const toProfile = () =>
uni.navigateTo({ url: "/pages/profile/m02-edit-profile" });
const toNotifications = () =>
uni.navigateTo({ url: "/pages/notification/n01-message-center" });
const openItem = (item) => uni.navigateTo({ url: item.url });
</script>
<style scoped lang="scss">
.profile-page {
position: relative;
min-height: 100vh;
overflow-x: hidden;
background: $paper;
}
.profile-page__header,
.profile-content {
position: relative;
z-index: 2;
}
.profile-content {
padding: 26rpx 30rpx 190rpx;
}
.profile-hero {
position: relative;
width: 100%;
min-height: 252rpx;
overflow: hidden;
}
.profile-hero__frame {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.profile-hero__hall {
position: absolute;
right: -18rpx;
bottom: -30rpx;
width: 330rpx;
height: 122rpx;
opacity: 0.12;
}
.profile-hero__cloud {
position: absolute;
top: 22rpx;
right: 30rpx;
width: 96rpx;
height: 52rpx;
opacity: 0.32;
}
.profile-hero__label {
position: absolute;
top: 50%;
left: 40rpx;
z-index: 2;
display: flex;
width: 42rpx;
min-height: 152rpx;
box-sizing: border-box;
flex-direction: column;
align-items: center;
justify-content: center;
border: 2rpx solid #c9964c;
border-radius: 4rpx;
background: linear-gradient(180deg, #9f241f, #bd3427);
box-shadow: inset 0 0 0 3rpx rgba(255, 222, 147, 0.24);
color: #ffe6a9;
font-family: STKaiti, KaiTi, serif;
font-size: 21rpx;
line-height: 1.12;
transform: translateY(-50%);
}
.profile-identity {
position: relative;
z-index: 1;
display: flex;
min-height: 252rpx;
box-sizing: border-box;
align-items: center;
gap: 24rpx;
padding: 34rpx 52rpx 32rpx 112rpx;
}
.profile-identity__seal {
width: 92rpx;
height: 106rpx;
flex: 0 0 auto;
}
.profile-identity__name,
.profile-identity__role,
.profile-identity__phone {
display: block;
}
.profile-identity__name {
color: $ink;
font-family: STKaiti, KaiTi, serif;
font-size: 38rpx;
font-weight: 700;
letter-spacing: 3rpx;
}
.profile-identity__role {
margin-top: 10rpx;
color: #6f5942;
font-size: 23rpx;
}
.profile-identity__phone {
margin-top: 7rpx;
color: $ink-muted;
font-size: 21rpx;
letter-spacing: 2rpx;
}
.profile-scroll-notice {
position: relative;
width: 88%;
min-height: 112rpx;
margin: 22rpx auto 0;
}
.profile-scroll-notice__skin {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.profile-scroll-notice__copy {
position: relative;
z-index: 1;
display: flex;
min-height: 112rpx;
box-sizing: border-box;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 13rpx 70rpx;
color: #fff4d3;
text-align: center;
}
.profile-scroll-notice__copy text:first-child {
font-family: STKaiti, KaiTi, serif;
font-size: 26rpx;
font-weight: 700;
letter-spacing: 3rpx;
}
.profile-scroll-notice__copy text:last-child {
margin-top: 4rpx;
color: #ffe4a8;
font-size: 18rpx;
}
.profile-services {
margin-top: 26rpx;
padding: 0 12rpx;
}
.profile-section-heading {
display: flex;
align-items: center;
justify-content: center;
gap: 20rpx;
padding-bottom: 12rpx;
}
.profile-section-heading image {
width: 88rpx;
height: 28rpx;
opacity: 0.78;
}
.profile-section-heading image:last-child {
transform: scaleX(-1);
}
.profile-section-heading text {
color: $ink;
font-family: STKaiti, KaiTi, serif;
font-size: 31rpx;
font-weight: 700;
letter-spacing: 4rpx;
}
.profile-menu-list {
border-top: 1px solid rgba(181, 137, 63, 0.55);
}
.profile-menu {
display: flex;
min-height: 116rpx;
align-items: center;
padding: 0 8rpx;
border-bottom: 1px solid rgba(181, 137, 63, 0.4);
}
.profile-menu__icon {
width: 52rpx;
height: 52rpx;
margin-right: 22rpx;
flex: 0 0 auto;
}
.profile-menu__copy {
min-width: 0;
flex: 1;
}
.profile-menu__label,
.profile-menu__note {
display: block;
}
.profile-menu__label {
color: $ink;
font-size: 25rpx;
font-weight: 700;
}
.profile-menu__note {
margin-top: 5rpx;
color: $ink-muted;
font-size: 21rpx;
}
.profile-menu__chevron {
width: 30rpx;
height: 30rpx;
margin-left: 18rpx;
opacity: 0.75;
}
.profile-error {
position: relative;
margin-top: 38rpx;
text-align: center;
}
.profile-error__skin {
position: absolute;
top: 0;
right: 0;
left: 0;
width: 100%;
height: 220rpx;
min-height: 118px;
}
.profile-error__copy {
position: relative;
z-index: 1;
display: flex;
height: 220rpx;
min-height: 118px;
box-sizing: border-box;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0 54rpx;
}
.profile-error__title,
.profile-error__description {
display: block;
}
.profile-error__title {
color: $ink;
font-family: STKaiti, KaiTi, serif;
font-size: 31rpx;
font-weight: 700;
}
.profile-error__description {
margin-top: 13rpx;
color: $ink-muted;
font-size: 23rpx;
line-height: 1.5;
}
.profile-error .app-button {
margin: 20rpx auto 0;
}
@media (min-width: 400px) {
.profile-content {
padding-right: 38rpx;
padding-left: 38rpx;
}
}
/* M01 medium-ornate profile folio styles remain page-scoped and extensible. */
</style>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB