fix: sync G01 role shortcuts
This commit is contained in:
@@ -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"
|
||||
```
|
||||
@@ -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);
|
||||
|
||||
@@ -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'
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user