This commit is contained in:
2026-08-07 13:56:29 +08:00
commit 152d5273a5
306 changed files with 38651 additions and 0 deletions
@@ -0,0 +1,270 @@
# Hide Layout Chrome for `common` Role 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:** Conditionally hide sidebar, navbar, tags-view, and the floating Settings button — and stretch `<app-main />` to fill the viewport — when the logged-in user's role list contains `"common"`.
**Architecture:** Add a single `isCommonUser` computed to `src/layout/index.vue` that derives from the existing Pinia `useUserStore`. Use that computed as a `v-if` gate on every chrome element and as a class binding on `.main-container`. Add one scoped SCSS rule for the fullscreen layout. No new files, no store/router/permission changes.
**Tech Stack:** Vue 3 (Composition API, `<script setup>`), Pinia, Vue Router, Element Plus, SCSS, Vuex-pattern Vue project (this is the `vue-element-plus-admin` template family).
**Spec:** `docs/superpowers/specs/2026-08-04-hide-chrome-for-common-role-design.md`
---
## File Structure
Only one file changes:
| File | Change | Responsibility |
|---|---|---|
| `src/layout/index.vue` | Edit (script + template + style) | Add `isCommonUser` computed, gate chrome with `v-if`, add `fullscreen` class binding, add `fullscreen` SCSS rule |
No new files. No test files (the project has no frontend test infrastructure — see spec § Testing; verification is manual).
---
## Task 1: Apply all `src/layout/index.vue` edits
**Files:**
- Modify: `src/layout/index.vue:1-115` (entire file)
This task is one edit cycle but split into named sub-steps so the implementer can verify the diff top-to-bottom. Use the Edit tool with exact string matching against the current file.
### Step 1.1 — Add `useUserStore` import
In `<script setup>`, the current line is:
```js
import useSettingsStore from '@/store/modules/settings'
const theme = computed(() => settingsStore.theme)
```
It must become:
```js
import useSettingsStore from '@/store/modules/settings'
import useUserStore from '@/store/modules/user'
const isCommonUser = computed(() =>
useUserStore().roles?.includes('common') ?? false
)
```
Edit (exact old → new):
```js
old:
import useSettingsStore from '@/store/modules/settings'
new:
import useSettingsStore from '@/store/modules/settings'
import useUserStore from '@/store/modules/user'
const isCommonUser = computed(() =>
useUserStore().roles?.includes('common') ?? false
)
```
### Step 1.2 — Gate the four chrome elements + add `fullscreen` class
Current template body (lines 1-14):
```vue
<template>
<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme, '--current-color-light': theme + '1a', '--current-color-dark-bg': theme + '33' }">
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
<div :class="{ 'fixed-header': fixedHeader }">
<navbar @setLayout="setLayout" />
<tags-view v-if="needTagsView" />
</div>
<app-main />
<settings ref="settingRef" />
</div>
</div>
</template>
```
Must become:
```vue
<template>
<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme, '--current-color-light': theme + '1a', '--current-color-dark-bg': theme + '33' }">
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
<sidebar v-if="!sidebar.hide && !isCommonUser" class="sidebar-container" />
<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide, fullscreen: isCommonUser }" class="main-container">
<div :class="{ 'fixed-header': fixedHeader }">
<navbar v-if="!isCommonUser" @setLayout="setLayout" />
<tags-view v-if="needTagsView && !isCommonUser" />
</div>
<app-main />
<settings v-if="!isCommonUser" ref="settingRef" />
</div>
</div>
</template>
```
Five edits inside the template — do them one at a time with Edit if needed, or use Edit `replace_all=false` on each line above (each old/new pair is unique enough).
Edit 1 — sidebar line:
```diff
- <sidebar v-if="!sidebar.hide" class="sidebar-container" />
+ <sidebar v-if="!sidebar.hide && !isCommonUser" class="sidebar-container" />
```
Edit 2 — main-container opening line:
```diff
- <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
+ <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide, fullscreen: isCommonUser }" class="main-container">
```
Edit 3 — navbar line:
```diff
- <navbar @setLayout="setLayout" />
+ <navbar v-if="!isCommonUser" @setLayout="setLayout" />
```
Edit 4 — tags-view line:
```diff
- <tags-view v-if="needTagsView" />
+ <tags-view v-if="needTagsView && !isCommonUser" />
```
Edit 5 — settings line:
```diff
- <settings ref="settingRef" />
+ <settings v-if="!isCommonUser" ref="settingRef" />
```
### Step 1.3 — Add `.fullscreen` SCSS rule
Inside the existing `<style lang="scss" scoped>` block, append after the `.mobile .fixed-header` rule at line ~115:
```scss
.main-container.fullscreen {
position: absolute;
inset: 0;
width: 100% !important;
height: 100% !important;
margin-left: 0 !important;
}
```
Use Edit with surrounding context to make the match unique — for example, anchor on `.mobile .fixed-header` block ending with `width: 100%;` followed by `}`.
### Step 1.4 — Self-check the diff
Re-read the whole file (`src/layout/index.vue`) and confirm:
- `useUserStore` import is present and `isCommonUser` computed is declared in `<script setup>`.
- `<sidebar>`, `<navbar>`, `<tags-view>`, `<settings>` all carry `v-if` gating on `!isCommonUser`.
- The `<div class="main-container">` has `fullscreen: isCommonUser` in its class binding.
- `.main-container.fullscreen` SCSS rule is inside the scoped `<style>` block.
- [ ] **Step 1 completed**
---
## Task 2: Manual verification
The project has no automated test suite (`package.json` scripts only include `dev` and `build`). Verify by running the dev server and exercising both role paths.
### Step 2.1 — Start the dev server
Run (from `D:/数科智联/agent-web/agent-web`):
```bash
yarn dev
```
Expected: Vite/Element-Plus dev server starts; URL is logged (typically `http://localhost:5173` or similar). Leave running.
### Step 2.2 — Verify admin role (no `common` in roles)
1. Open the dev URL in a browser.
2. Log in as a user whose `roles` array does **not** include `"common"` (e.g. admin / editor test accounts).
3. Open browser DevTools → Vue DevTools (or just inspect the DOM). Confirm all four are present in `#app`:
- `<aside>` containing the left sidebar.
- Top `<header>` / navbar.
- Tags-view row beneath the navbar.
- `<app-main />` to the right.
4. Confirm the floating Settings button is visible at the bottom-right.
5. Click a couple of navigation links; tags-view and sidebar behavior should be unchanged from prior baseline.
Expected: identical to pre-change behavior. If anything looks off (extra padding, missing element), stop and re-read the file diff.
### Step 2.3 — Verify `common` role (chromeless)
1. Log out, then log in as a user whose `roles` array contains `"common"` (e.g. `['common']` or `['common', 'something-else']`).
2. Reload the page if needed.
3. Confirm visually:
- No left sidebar.
- No top navbar.
- No tags-view row.
- No Settings floating button.
- `<app-main />` fills the entire viewport edge-to-edge (no sidebar gutter, no top whitespace from where navbar was).
4. Resize the window narrower to test responsive: `.fullscreen` should remain full screen at all widths.
5. Inside `<app-main />`, exercise the page's own controls (links, buttons, forms) to confirm interaction still works.
Expected: clean chromeless experience. If you see a gap or scrollbar that wasn't there before, the SCSS rule did not apply — check that the class binding in the template is correct and that `<style scoped>` did not strip the selector (use `:deep` if necessary, though for a scoped class on a top-level element this should not be needed).
### Step 2.4 — Verify mixed role `['admin', 'common']`
Repeat Step 2.3 with a user whose roles list is `['admin', 'common']`. Because `Array.prototype.includes` semantics treat any inclusion as "match", chrome should be hidden. If you see chrome here, the `isCommonUser` computed is wrong.
### Step 2.5 — Stop the dev server
When verification is complete, stop the dev server (Ctrl-C in the terminal where it is running).
- [ ] **Step 2 completed**
---
## Task 3: Commit
### Step 3.1 — Stage and review
```bash
git status --short
git diff src/layout/index.vue
```
Expected: only `src/layout/index.vue` is modified. No stray changes from your edits.
### Step 3.2 — Commit
```bash
git add src/layout/index.vue
git commit -m "$(cat <<'EOF'
feat(layout): common 角色隐藏侧栏/顶栏/tagsview 并全屏渲染 app-main
根据 useUserStore().roles 是否包含 'common' 隐藏 sidebar、navbar、
tags-view、Settings 浮动按钮,并让 main-container 在该模式下全屏铺满。
仅修改 src/layout/index.vue。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
EOF
)"
```
Expected: one new commit on top of `b193fa1`. Working tree should be clean (modulo any pre-existing changes unrelated to this task — those are not part of this plan).
- [ ] **Step 3 completed**
---
## Self-Review Checklist
- [x] **Spec coverage:**
- Goal (hide chrome + fullscreen app-main for `common` role) → Task 1
- Trigger (`roles.includes('common')`) → Step 1.1
- All four chrome elements gated → Steps 1.2 edits 1, 3, 4, 5
- Fullscreen class binding on `main-container` → Step 1.2 edit 2
- Fullscreen SCSS rule → Step 1.3
- Manual verification scenarios (admin / common / mixed / page interaction) → Tasks 2.2, 2.3, 2.4
- Settings also hidden (confirmed in brainstorming) → Step 1.2 edit 5
- [x] **Placeholder scan:** No TBD/TODO. Each edit has exact code. Manual verification steps have concrete URLs and accounts to use (caller supplies).
- [x] **Type consistency:** All references use `isCommonUser`. No alternate naming introduced. `useUserStore` matches existing imports elsewhere (`@/store/modules/user`).
- [x] **Commit hygiene:** One focused commit. Message starts with `feat(layout):`. Co-authored-by line present.