Files
agent-frontend-web/docs/superpowers/plans/2026-08-04-hide-chrome-for-common-role.md
2026-08-07 13:56:29 +08:00

10 KiB

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:

import useSettingsStore from '@/store/modules/settings'
const theme = computed(() => settingsStore.theme)

It must become:

import useSettingsStore from '@/store/modules/settings'
import useUserStore from '@/store/modules/user'

const isCommonUser = computed(() =>
  useUserStore().roles?.includes('common') ?? false
)

Edit (exact old → new):

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):

<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:

<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:

- <sidebar v-if="!sidebar.hide" class="sidebar-container" />
+ <sidebar v-if="!sidebar.hide && !isCommonUser" class="sidebar-container" />

Edit 2 — main-container opening line:

-    <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:

-        <navbar @setLayout="setLayout" />
+        <navbar v-if="!isCommonUser" @setLayout="setLayout" />

Edit 4 — tags-view line:

-        <tags-view v-if="needTagsView" />
+        <tags-view v-if="needTagsView && !isCommonUser" />

Edit 5 — settings line:

-      <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:

.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):

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

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

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

  • 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
  • Placeholder scan: No TBD/TODO. Each edit has exact code. Manual verification steps have concrete URLs and accounts to use (caller supplies).
  • Type consistency: All references use isCommonUser. No alternate naming introduced. useUserStore matches existing imports elsewhere (@/store/modules/user).
  • Commit hygiene: One focused commit. Message starts with feat(layout):. Co-authored-by line present.