1
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user