fix: sync G01 role shortcuts

This commit is contained in:
rain
2026-07-21 13:06:36 +08:00
parent 8468cc0f89
commit 668b4ef55f
4 changed files with 212 additions and 2 deletions
@@ -0,0 +1,166 @@
# G01 Role And Shortcut State 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:** Make G01 display the selected genealogy's real role and hide the application-review shortcut for ordinary members.
**Architecture:** Keep `membership` on the selected genealogy as the sole role source. Derive the role label and visible shortcut list inside G01 so initial render and switcher changes share the same reactive rule.
**Tech Stack:** Vue 3 Composition API, UniApp, PowerShell contract tests, existing G01 browser runtime smoke, MuMu Android manual verification.
## Global Constraints
- `membership === "created"` displays “管理员” and four shortcuts.
- `membership === "joined"` displays “成员” and only “世系图、成员、字辈诗”.
- Do not add a replacement shortcut or an invisible application-review hit target for members.
- Preserve the existing switcher, list scroll reset, assets, layout, and routes.
- MuMu screenshots are the visual acceptance source; browser runtime is logic regression evidence only.
---
### Task 1: Drive G01 Role And Shortcuts From Membership
**Files:**
- Create: `tests/g01-role-shortcut-contract.ps1`
- Modify: `tests/g01-switch-dialog-runtime-smoke.js`
- Modify: `pages/genealogy/g01-my-genealogies.vue`
**Interfaces:**
- Consumes: `currentGenealogy.value.membership`, where the supported values are `created` and `joined`.
- Produces: `currentRoleLabel: ComputedRef<string>` and `visibleShortcuts: ComputedRef<Array<{ key: string, label: string, icon: string }>>` for the existing G01 template.
- [x] **Step 1: Write the failing source contract**
```powershell
$ErrorActionPreference = 'Stop'
$page = Get-Content -Raw 'pages/genealogy/g01-my-genealogies.vue'
function Assert-Contains([string]$Content, [string]$Pattern, [string]$Message) {
if (-not $Content.Contains($Pattern)) { throw $Message }
}
Assert-Contains $page 'v-for="item in visibleShortcuts"' 'G01 must render the membership-filtered shortcut list'
Assert-Contains $page '{{ currentRoleLabel }}' 'G01 must render the membership-derived role label'
Assert-Contains $page 'currentGenealogy.value?.membership === "created"' 'G01 must derive owner state from membership'
Assert-Contains $page 'item.key !== "applications"' 'G01 must remove application review for joined members'
Write-Output 'G01-ROLE-SHORTCUT-CONTRACT PASS'
```
- [x] **Step 2: Run the source contract and verify RED**
Run:
```powershell
powershell -ExecutionPolicy Bypass -File tests/g01-role-shortcut-contract.ps1
```
Expected: FAIL with `G01 must render the membership-filtered shortcut list` because G01 still renders `shortcuts` directly.
- [x] **Step 3: Extend the runtime smoke with role-state assertions**
Immediately after selecting the second switcher item, add:
```js
assert(
(await valueOf(send, "document.querySelector('.current-meta')?.textContent"))?.includes('成员'),
'Joined genealogy did not display the member role'
)
assert(
(await valueOf(send, "document.querySelectorAll('.shortcut-item').length")) === 3,
'Joined genealogy did not hide the application-review shortcut'
)
assert(
!(await valueOf(send, "document.querySelector('.shortcut-grid')?.textContent"))?.includes('申请审核'),
'Joined genealogy still exposed application review'
)
```
Immediately after restoring the first switcher item, add:
```js
assert(
(await valueOf(send, "document.querySelector('.current-meta')?.textContent"))?.includes('管理员'),
'Created genealogy did not restore the administrator role'
)
assert(
(await valueOf(send, "document.querySelectorAll('.shortcut-item').length")) === 4,
'Created genealogy did not restore all four shortcuts'
)
```
- [x] **Step 4: Implement the minimal reactive role rule**
Change the shortcut loop and role text in the template:
```vue
<view
v-for="item in visibleShortcuts"
:key="item.key"
class="shortcut-item"
@click="openShortcut(item.key)"
>
```
```vue
<text class="current-meta-item-text">{{ currentRoleLabel }}</text>
```
After `currentGenealogy`, add:
```js
const isCurrentGenealogyOwner = computed(
() => currentGenealogy.value?.membership === "created",
);
const currentRoleLabel = computed(() =>
isCurrentGenealogyOwner.value ? "管理员" : "成员",
);
```
After the existing `shortcuts` array, add:
```js
const visibleShortcuts = computed(() =>
isCurrentGenealogyOwner.value
? shortcuts
: shortcuts.filter((item) => item.key !== "applications"),
);
```
- [x] **Step 5: Run focused automated verification**
Run:
```powershell
powershell -ExecutionPolicy Bypass -File tests/g01-role-shortcut-contract.ps1
powershell -ExecutionPolicy Bypass -File tests/g01-genealogy-context-contract.ps1
powershell -ExecutionPolicy Bypass -File tests/g01-empty-state-contract.ps1
powershell -ExecutionPolicy Bypass -File tests/g01-error-state-contract.ps1
powershell -ExecutionPolicy Bypass -File tests/g01-visual-contract.ps1
git diff --check
```
Expected: every contract prints `PASS`; `git diff --check` exits 0.
If the H5 debug page and Chrome remote-debugging port `9222` are available, also run:
```powershell
node tests/g01-switch-dialog-runtime-smoke.js
```
Expected: `PASS G-01 switch dialog runtime smoke`.
- [x] **Step 6: Verify both roles in MuMu Android**
1. Open G01 with the created genealogy selected and capture the administrator state.
2. Open the switcher and select the joined genealogy.
3. Confirm the top role reads “成员”, exactly three shortcuts are visible, and there is no blank fourth slot or hidden application-review target.
4. Switch back to the created genealogy and confirm “管理员” plus four shortcuts return.
5. Confirm the list scroll resets to the top and the bottom tabbar remains unobstructed.
- [x] **Step 7: Commit the tested fix**
```powershell
git add tests/g01-role-shortcut-contract.ps1 tests/g01-switch-dialog-runtime-smoke.js pages/genealogy/g01-my-genealogies.vue docs/superpowers/plans/2026-07-21-g01-role-shortcut.md
git commit -m "fix: sync G01 role shortcuts"
```
+13 -2
View File
@@ -82,14 +82,14 @@
src="/static/assets/foundation/transparent/meta-admin.png"
mode="aspectFit"
/>
<text class="current-meta-item-text">管理员</text>
<text class="current-meta-item-text">{{ currentRoleLabel }}</text>
</view>
</view>
</view>
<view class="shortcut-grid">
<view
v-for="item in shortcuts"
v-for="item in visibleShortcuts"
:key="item.key"
class="shortcut-item"
@click="openShortcut(item.key)"
@@ -420,6 +420,12 @@ const currentGenealogy = computed(
(item) => item.id === selectedGenealogyId.value,
) || availableGenealogies.value[0],
);
const isCurrentGenealogyOwner = computed(
() => currentGenealogy.value?.membership === "created",
);
const currentRoleLabel = computed(() =>
isCurrentGenealogyOwner.value ? "管理员" : "成员",
);
// 仅用于页面样式阶段覆盖加入申请的关键状态,不作为接口数据。
const applicationRecords = [
@@ -468,6 +474,11 @@ const shortcuts = [
icon: "/static/assets/modules/genealogy/transparent/shortcut-application.png",
},
];
const visibleShortcuts = computed(() =>
isCurrentGenealogyOwner.value
? shortcuts
: shortcuts.filter((item) => item.key !== "applications"),
);
const openGenealogy = (genealogy) => {
genealogyContext.setCurrentGenealogyId(genealogy.id);
+13
View File
@@ -0,0 +1,13 @@
$ErrorActionPreference = 'Stop'
$page = Get-Content -Raw 'pages/genealogy/g01-my-genealogies.vue'
function Assert-Contains([string]$Content, [string]$Pattern, [string]$Message) {
if (-not $Content.Contains($Pattern)) { throw $Message }
}
Assert-Contains $page 'v-for="item in visibleShortcuts"' 'G01 must render the membership-filtered shortcut list'
Assert-Contains $page '{{ currentRoleLabel }}' 'G01 must render the membership-derived role label'
Assert-Contains $page 'currentGenealogy.value?.membership === "created"' 'G01 must derive owner state from membership'
Assert-Contains $page 'item.key !== "applications"' 'G01 must remove application review for joined members'
Write-Output 'G01-ROLE-SHORTCUT-CONTRACT PASS'
+20
View File
@@ -261,10 +261,30 @@ const run = async () => {
await valueOf(send, "document.querySelectorAll('.switcher-item')[1].click()")
await waitFor(send, "!document.querySelector('.genealogy-switcher')", 'Selecting the second genealogy did not close the switcher')
assert((await valueOf(send, "document.querySelector('.current-slip')?.textContent"))?.includes('山东'), 'Selecting the second genealogy did not update the current genealogy')
assert(
(await valueOf(send, "document.querySelector('.current-meta')?.textContent"))?.includes('成员'),
'Joined genealogy did not display the member role'
)
assert(
(await valueOf(send, "document.querySelectorAll('.shortcut-item').length")) === 3,
'Joined genealogy did not hide the application-review shortcut'
)
assert(
!(await valueOf(send, "document.querySelector('.shortcut-grid')?.textContent"))?.includes('申请审核'),
'Joined genealogy still exposed application review'
)
await openSwitcher(send)
await valueOf(send, "document.querySelectorAll('.switcher-item')[0].click()")
await waitFor(send, "!document.querySelector('.genealogy-switcher')", 'Restoring the first genealogy did not close the switcher')
assert(
(await valueOf(send, "document.querySelector('.current-meta')?.textContent"))?.includes('管理员'),
'Created genealogy did not restore the administrator role'
)
assert(
(await valueOf(send, "document.querySelectorAll('.shortcut-item').length")) === 4,
'Created genealogy did not restore all four shortcuts'
)
await openSwitcher(send)
await clearClones(send)