Files
WonderQ-Project/docs/superpowers/plans/2026-08-26-ruoyi-admin-shell-visual-design.md
duanshuwen fbf2c772a8 refactor: 清理废弃模块并重构管理端与全端代码
- 移除所有废弃的旧首页配置模块,包括destinationHero、demand相关、HomeExperience、vehicleService等表结构与代码,新增数据库迁移删除遗留表
- 重构管理端后台布局为标准RuoYi风格,新增Sidebar、Navbar、SettingsDrawer等组件,替换旧的过渡菜单实现
- 调整移动端响应断点为768px,更新布局相关测试用例,新增布局组件测试
- 删除OperationsTools发布/重置页面,移除/tools菜单入口,清理对应的API调用与测试代码
- 重构玩法模块编辑器:替换旧的图片手动输入框为图片上传组件,移除冗余的取消按钮
- 新增线索查询offset参数支持,完善查询参数处理逻辑
- 优化rbac菜单构建逻辑,新增可见性控制参数
- 修正管家编辑器提示文案,优化系统资源编辑器表单初始化逻辑
- 清理小程序端废弃的体验推荐组件与相关测试代码
- 更新文档与种子脚本,匹配新的API契约与使用说明
2026-08-26 23:24:28 +08:00

22 KiB
Raw Blame History

WonderQ Admin UI RuoYi 默认后台壳还原 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: 在不改变 WonderQ 业务页面、权限、路由和 API 的前提下,将 WonderQ-Admin-UI-Vue 的后台壳还原为本地 RuoYi-Vue3 的默认布局与交互。

Architecture: 保留现有 AdminLayout 作为路由壳,在其下拆分 Sidebar、Navbar、Breadcrumb、TagsView、SettingsDrawer 和 UserMenu。布局状态、页签状态和本地布局设置分别由 Pinia store 与纯函数模块管理;业务页面只接收新的内容区域尺寸和滚动上下文,不改变其数据流。

Tech Stack: Vue 3 script setup lang="ts"、Pinia、Vue Router 4、Element Plus、Vite、Vitest、原生 SVG 图标和 CSS 变量。不新增依赖,不修改后端和 API 契约。


文件结构锁定

Create

  • WonderQ-Admin-UI-Vue/src/components/layout/LayoutIcon.vue:统一渲染布局图标的内联 SVG。
  • WonderQ-Admin-UI-Vue/src/components/layout/Sidebar.vue:RuoYi 风格侧栏、移动端抽屉和菜单容器。
  • WonderQ-Admin-UI-Vue/src/components/layout/SidebarItem.vue:递归目录/页面菜单项。
  • WonderQ-Admin-UI-Vue/src/components/layout/Navbar.vue:50px 顶栏及工具入口。
  • WonderQ-Admin-UI-Vue/src/components/layout/Breadcrumb.vue:基于当前路由和权限菜单生成面包屑。
  • WonderQ-Admin-UI-Vue/src/components/layout/TagsView.vue:34px 页签、关闭按钮和右键菜单。
  • WonderQ-Admin-UI-Vue/src/components/layout/SettingsDrawer.vue:右侧布局配置抽屉。
  • WonderQ-Admin-UI-Vue/src/components/layout/UserMenu.vue:用户下拉菜单和退出登录操作。
  • WonderQ-Admin-UI-Vue/src/components/layout/TopMenu.vue:顶部/混合导航模式的根菜单。
  • WonderQ-Admin-UI-Vue/src/components/layout/Footer.vue:可选的 RuoYi 风格底部版权。
  • WonderQ-Admin-UI-Vue/src/stores/layout.ts:侧栏、设备类型和设置抽屉状态。
  • WonderQ-Admin-UI-Vue/src/stores/settings.ts:布局设置响应式状态和主题应用。
  • WonderQ-Admin-UI-Vue/src/stores/tags.ts:已访问页签状态和操作。
  • WonderQ-Admin-UI-Vue/src/lib/layout-settings.ts:默认布局配置、归一化和本地存储。
  • WonderQ-Admin-UI-Vue/src/lib/layout-settings.test.ts:布局配置纯函数测试。
  • WonderQ-Admin-UI-Vue/src/lib/tags.ts:页签纯函数与上下文菜单操作。
  • WonderQ-Admin-UI-Vue/src/lib/tags.test.ts:页签操作测试。
  • WonderQ-Admin-UI-Vue/src/lib/menu-display.ts:菜单图标、标题和可导航菜单辅助函数。
  • WonderQ-Admin-UI-Vue/src/lib/menu-display.test.ts:菜单展示辅助函数测试。

Modify

  • WonderQ-Admin-UI-Vue/src/layouts/AdminLayout.vue:替换自定义浅色壳为新布局组合,保留 RouterView 和鉴权退出流程。
  • WonderQ-Admin-UI-Vue/src/components/MenuTree.vue:迁移为新递归菜单实现的兼容入口,或删除重复实现并更新唯一引用。
  • WonderQ-Admin-UI-Vue/src/styles.css:将壳层尺寸、颜色、层级、响应式和 RuoYi Element Plus 覆盖集中到布局样式;保留业务页面专用样式。
  • WonderQ-Admin-UI-Vue/src/router/index.ts:仅补充路由 meta 标题/固定页签所需的前端信息,不修改路径、权限和组件注册。

Test commands

Set-Location .\WonderQ-Admin-UI-Vue
yarn test
yarn build

Task 1: 建立可持久化的布局设置模型

Files:

  • Create: WonderQ-Admin-UI-Vue/src/lib/layout-settings.ts

  • Test: WonderQ-Admin-UI-Vue/src/lib/layout-settings.test.ts

  • Step 1: 先写布局设置失败测试

覆盖默认值、非法值回退、未知字段过滤、保存/读取和重置:

import { describe, expect, it } from "vitest";
import {
  DEFAULT_LAYOUT_SETTINGS,
  LAYOUT_SETTINGS_KEY,
  loadLayoutSettings,
  normalizeLayoutSettings,
  saveLayoutSettings,
} from "./layout-settings";

describe("layout settings", () => {
  it("uses RuoYi defaults", () => {
    expect(DEFAULT_LAYOUT_SETTINGS).toMatchObject({
      navType: "left",
      sideTheme: "theme-dark",
      theme: "#409EFF",
      tagsView: true,
      fixedHeader: false,
      sidebarLogo: true,
    });
  });

  it("normalizes invalid persisted values without leaking unknown keys", () => {
    expect(normalizeLayoutSettings({ navType: "invalid", theme: 12, unknown: true })).toEqual(DEFAULT_LAYOUT_SETTINGS);
  });

  it("round-trips settings through the provided storage", () => {
    const storage = new Map<string, string>();
    const fakeStorage = {
      getItem: (key: string) => storage.get(key) ?? null,
      setItem: (key: string, value: string) => storage.set(key, value),
      removeItem: (key: string) => storage.delete(key),
    } as Storage;
    const next = { ...DEFAULT_LAYOUT_SETTINGS, tagsIcon: true };
    saveLayoutSettings(next, fakeStorage);
    expect(fakeStorage.getItem(LAYOUT_SETTINGS_KEY)).toContain('"tagsIcon":true');
    expect(loadLayoutSettings(fakeStorage)).toEqual(next);
  });
});
  • Step 2: 运行测试确认当前失败

Run: yarn test src/lib/layout-settings.test.ts

Expected: FAIL because layout-settings.ts does not exist.

  • Step 3: 实现配置类型、默认值和归一化

实现以下公开接口:

export type NavigationType = "left" | "mix" | "top";
export type SideTheme = "theme-dark" | "theme-light";
export type TagsViewStyle = "card" | "chrome";
export type LayoutSettings = {
  navType: NavigationType;
  sideTheme: SideTheme;
  theme: string;
  tagsView: boolean;
  tagsViewPersist: boolean;
  tagsIcon: boolean;
  tagsViewStyle: TagsViewStyle;
  fixedHeader: boolean;
  sidebarLogo: boolean;
  dynamicTitle: boolean;
  footerVisible: boolean;
};

export const LAYOUT_SETTINGS_KEY = "wonderq-admin-layout-setting";
export const DEFAULT_LAYOUT_SETTINGS: LayoutSettings = {
  navType: "left",
  sideTheme: "theme-dark",
  theme: "#409EFF",
  tagsView: true,
  tagsViewPersist: false,
  tagsIcon: false,
  tagsViewStyle: "card",
  fixedHeader: false,
  sidebarLogo: true,
  dynamicTitle: false,
  footerVisible: false,
};

export function normalizeLayoutSettings(input: unknown): LayoutSettings;
export function loadLayoutSettings(storage: Storage = window.localStorage): LayoutSettings;
export function saveLayoutSettings(settings: LayoutSettings, storage: Storage = window.localStorage): void;
export function resetLayoutSettings(storage: Storage = window.localStorage): LayoutSettings;

验证 enum、boolean 和六位十六进制色值;读取异常 JSON 时回退默认值;每次返回新对象,避免 store 修改导出的默认值。

  • Step 4: 运行测试确认通过

Run: yarn test src/lib/layout-settings.test.ts

Expected: all layout settings tests PASS.

  • Step 5: Commit
git add -- WonderQ-Admin-UI-Vue/src/lib/layout-settings.ts WonderQ-Admin-UI-Vue/src/lib/layout-settings.test.ts
git commit -m "feat(admin-ui): add persisted layout settings model"

Task 2: 建立侧栏和设备状态 store

Files:

  • Create: WonderQ-Admin-UI-Vue/src/stores/layout.ts

  • Step 1: 定义 store 的状态和行为契约

export type DeviceType = "desktop" | "mobile";
export type SidebarToggleOptions = { withoutAnimation?: boolean };

export const useLayoutStore = defineStore("layout", () => {
  const device = ref<DeviceType>("desktop");
  const sidebarOpened = ref(true);
  const settingsDrawerOpen = ref(false);
  const withoutAnimation = ref(false);

  function syncViewport(width = window.innerWidth): void;
  function toggleSidebar(options?: SidebarToggleOptions): void;
  function openSidebar(options?: SidebarToggleOptions): void;
  function closeSidebar(options?: SidebarToggleOptions): void;
  function openSettings(): void;
  function closeSettings(): void;
  function startViewportWatcher(): () => void;

  return { device, sidebarOpened, settingsDrawerOpen, withoutAnimation, syncViewport, toggleSidebar, openSidebar, closeSidebar, openSettings, closeSettings, startViewportWatcher };
});

syncViewport 在 992px 以下进入 mobile,进入 mobile 时关闭桌面状态;mobile 抽屉仍使用 200px 宽度,不复用桌面 54px 折叠宽度。

  • Step 2: 运行基线测试

Run: yarn test

Expected: existing tests PASS.

  • Step 3: 实现 setup store 和可清理的 resize watcher

使用命名 resize 回调并返回 unsubscribe;store 不读取 localStorage、不读取 Token、不调用后端。

  • Step 4: Commit
git add -- WonderQ-Admin-UI-Vue/src/stores/layout.ts
git commit -m "feat(admin-ui): add responsive layout state"

Task 3: 建立菜单展示辅助函数和布局图标

Files:

  • Create: WonderQ-Admin-UI-Vue/src/lib/menu-display.ts

  • Test: WonderQ-Admin-UI-Vue/src/lib/menu-display.test.ts

  • Create: WonderQ-Admin-UI-Vue/src/components/layout/LayoutIcon.vue

  • Step 1: 写菜单展示失败测试

import { describe, expect, it } from "vitest";
import { findMenuTitle, getMenuIconName, visibleNavigableMenus } from "./menu-display";

const menus = [
  { id: "ops", name: "运营管理", type: "directory" as const, children: [
    { id: "play", name: "玩法管理", type: "page" as const, path: "/play", icon: "Route" },
  ] },
  { id: "hidden", name: "隐藏", type: "page" as const, path: "/hidden", isVisible: false },
];

describe("menu display helpers", () => {
  it("removes hidden and button entries from navigable lists", () => {
    expect(visibleNavigableMenus(menus).map((item) => item.id)).toEqual(["ops"]);
  });
  it("finds page and parent titles for breadcrumbs", () => {
    expect(findMenuTitle(menus, "/play")).toEqual(["运营管理", "玩法管理"]);
  });
  it("falls back to a stable icon for unknown menu values", () => {
    expect(getMenuIconName({ id: "x", name: "未知", type: "page" })).toBe("Menu");
  });
});
  • Step 2: 运行测试确认失败

Run: yarn test src/lib/menu-display.test.ts

Expected: FAIL because the helper module is not present.

  • Step 3: 实现菜单辅助函数

visibleNavigableMenus 递归过滤 button 和 isVisible === false;findMenuTitle 归一化斜杠并返回父目录/页面标题;getMenuIconName 先按 WonderQ path/component 映射,再使用受支持的 AdminMenu.icon,否则回退 Menu。

  • Step 4: 实现 LayoutIcon.vue

使用固定 24x24 viewBox 的本地 inline SVG,为 Menu、Grid、Route、User、Collection、Setting、Search、FullScreen、Moon、Sunny、Refresh、Close、More、Back、Right 和 Chevron 提供路径。无 title 时设置 aria-hidden="true",禁止 emoji 图标。

  • Step 5: 测试并 Commit
yarn test src/lib/menu-display.test.ts
git add -- WonderQ-Admin-UI-Vue/src/lib/menu-display.ts WonderQ-Admin-UI-Vue/src/lib/menu-display.test.ts WonderQ-Admin-UI-Vue/src/components/layout/LayoutIcon.vue
git commit -m "feat(admin-ui): add RuoYi layout menu primitives"

Task 4: 建立页签纯函数和 Tags store

Files:

  • Create: WonderQ-Admin-UI-Vue/src/lib/tags.ts

  • Test: WonderQ-Admin-UI-Vue/src/lib/tags.test.ts

  • Create: WonderQ-Admin-UI-Vue/src/stores/tags.ts

  • Step 1: 写页签失败测试

import { describe, expect, it } from "vitest";
import { addVisitedTag, closeAllTags, closeTag, closeTagsToLeft, closeTagsToRight, type VisitedTag } from "./tags";

const home: VisitedTag = { path: "/dashboard", fullPath: "/dashboard", title: "首页概览", affix: true };
const play: VisitedTag = { path: "/play", fullPath: "/play", title: "玩法管理" };
const leads: VisitedTag = { path: "/leads", fullPath: "/leads", title: "需求线索" };

describe("tags helpers", () => {
  it("keeps one entry for a repeated fullPath", () => {
    expect(addVisitedTag([home], play)).toEqual([home, play]);
    expect(addVisitedTag([home, play], play)).toEqual([home, play]);
  });
  it("does not close an affix tag", () => {
    expect(closeTag([home, play], home)).toEqual([home, play]);
  });
  it("closes the selected tag and returns the previous fallback", () => {
    expect(closeTag([home, play, leads], play)).toEqual([home, leads]);
  });
  it("closes ranges without removing the selected tag", () => {
    expect(closeTagsToLeft([home, play, leads], leads)).toEqual([home, leads]);
    expect(closeTagsToRight([home, play, leads], home)).toEqual([home]);
  });
  it("returns the affix home tag after close all", () => {
    expect(closeAllTags([home, play, leads])).toEqual([home]);
  });
});
  • Step 2: 运行失败测试

Run: yarn test src/lib/tags.test.ts

Expected: FAIL because tags.ts is not present.

  • Step 3: 实现页签类型和纯函数

VisitedTag 包含 path、fullPath、title、可选 name/query/icon/affix;所有操作返回新数组;关闭当前优先选择前一个可用页签,否则后一个,最后回退首页。

  • Step 4: 实现 useTagsStore

提供 visitedViews、activePath、addView、updateView、closeSelectedTag、closeOthersTags、closeLeftTags、closeRightTags、closeAllTags、refreshSelectedTag 和 restorePersistedViews。仅在 tagsView 与 tagsViewPersist 都开启时持久化。

  • Step 5: 测试并 Commit
yarn test src/lib/tags.test.ts
git add -- WonderQ-Admin-UI-Vue/src/lib/tags.ts WonderQ-Admin-UI-Vue/src/lib/tags.test.ts WonderQ-Admin-UI-Vue/src/stores/tags.ts
git commit -m "feat(admin-ui): add tags view state"

Task 5: 实现 RuoYi 侧栏和递归菜单

Files:

  • Create: WonderQ-Admin-UI-Vue/src/components/layout/SidebarItem.vue

  • Create: WonderQ-Admin-UI-Vue/src/components/layout/Sidebar.vue

  • Create: WonderQ-Admin-UI-Vue/src/components/layout/TopMenu.vue

  • Modify: WonderQ-Admin-UI-Vue/src/components/MenuTree.vue only if the old duplicate is still referenced.

  • Step 1: 实现 SidebarItem.vue

接收 menu、basePath、collapsed 和 navigationType;目录使用 el-sub-menu,页面使用 el-menu-item,button 不渲染。标题同时渲染 LayoutIcon 和菜单名,路径使用现有 menu path。

  • Step 2: 实现 Sidebar.vue 和 TopMenu.vue

读取 useAuthStore().menus,调用 visibleNavigableMenus,使用 el-scrollbar 和 el-menu 渲染。桌面端使用 collapse、unique-opened 和当前路由 active;mobile 通过 transform 控制抽屉并渲染遮罩。Logo 保留 WonderQ 品牌,显示/隐藏由布局设置控制。

TopMenu 在 navType 为 top 时渲染根级页面/目录,mix 模式下只渲染根级菜单并让 Sidebar 显示当前根菜单的子树;left 模式不渲染 TopMenu。三种模式只改变展示位置,不改变 auth.menus、权限码和路由。

  • Step 3: 运行类型检查

Run: yarn build

Expected: Vue/TypeScript 编译通过;旧 MenuTree 如无引用则删除其引用,不删除业务菜单数据。

  • Step 4: Commit
git add -- WonderQ-Admin-UI-Vue/src/components/layout/SidebarItem.vue WonderQ-Admin-UI-Vue/src/components/layout/Sidebar.vue WonderQ-Admin-UI-Vue/src/components/MenuTree.vue
git commit -m "feat(admin-ui): implement RuoYi sidebar shell"

Task 6: 实现 Navbar、Breadcrumb、UserMenu 和工具栏

Files:

  • Create: WonderQ-Admin-UI-Vue/src/components/layout/Breadcrumb.vue

  • Create: WonderQ-Admin-UI-Vue/src/components/layout/UserMenu.vue

  • Create: WonderQ-Admin-UI-Vue/src/components/layout/Navbar.vue

  • Step 1: 实现面包屑

使用 useRoute、useAuthStore 和 findMenuTitle;首页显示“首页概览”,业务页面显示父目录/页面标题;无菜单匹配时回退到 route.meta.title 或 route name,不新增后端字段。

  • Step 2: 实现用户下拉

用户头像使用当前用户名称首字母或首个中文字符,菜单提供“布局设置”和“退出登录”。退出前调用 ElMessageBox.confirm("确定注销并退出系统吗?", "提示", ...),确认后执行 auth.logout() 并 router.replace({ name: "login" })。

  • Step 3: 实现 Navbar 工具栏

固定 50px 高度,包含汉堡、Breadcrumb、全屏、明暗主题、布局设置和 UserMenu。全屏只调用 document.documentElement.requestFullscreen() / document.exitFullscreen();失败用 ElMessage.warning,不读写敏感浏览器数据。主题只切换 html 的 dark class 和布局变量。

  • Step 4: Commit
git add -- WonderQ-Admin-UI-Vue/src/components/layout/Navbar.vue WonderQ-Admin-UI-Vue/src/components/layout/Breadcrumb.vue WonderQ-Admin-UI-Vue/src/components/layout/UserMenu.vue
git commit -m "feat(admin-ui): add RuoYi navbar and user tools"

Task 7: 实现 TagsView 和布局设置抽屉

Files:

  • Create: WonderQ-Admin-UI-Vue/src/components/layout/TagsView.vue

  • Create: WonderQ-Admin-UI-Vue/src/components/layout/SettingsDrawer.vue

  • Create: WonderQ-Admin-UI-Vue/src/stores/settings.ts

  • Create: WonderQ-Admin-UI-Vue/src/components/layout/Footer.vue

  • Step 1: 实现 TagsView

监听 router.afterEach 或布局层路由变化,调用 tags.addView(route)。页签使用 router-link,当前页 active;关闭按钮阻止冒泡;右键菜单使用 fixed 定位并在 body click 时关闭。刷新用现有 routeViewKey 或等价的组件 key 重建。

  • Step 2: 实现设置抽屉

使用 el-drawer 右侧 300px 宽度,显示左侧/混合/顶部菜单、深色/浅色侧栏、主色、TagsView、页签持久化/图标/样式、固定 Header、显示 Logo、动态标题和底部版权。依赖 TagsView 的选项在关闭时禁用;保存调用 saveLayoutSettings,重置调用 resetLayoutSettings。

useSettingsStore 以 layout-settings.ts 的默认值初始化响应式 settings,负责把 sideTheme、theme 和 dark class 应用到 document.documentElement,并提供 save、reset、toggleTheme 和 setSetting。Footer 只在 footerVisible 时显示 footerContent,默认关闭且不改变业务页面内容。

  • Step 3: 测试和编译

Run: yarn test && yarn build

Expected: all tests PASS and production build completes.

  • Step 4: Commit
git add -- WonderQ-Admin-UI-Vue/src/components/layout/TagsView.vue WonderQ-Admin-UI-Vue/src/components/layout/SettingsDrawer.vue WonderQ-Admin-UI-Vue/src/components/layout/Footer.vue WonderQ-Admin-UI-Vue/src/stores/settings.ts
git commit -m "feat(admin-ui): add tags and layout settings interactions"

Task 8: 组装 AdminLayout 并接入路由页签

Files:

  • Modify: WonderQ-Admin-UI-Vue/src/layouts/AdminLayout.vue

  • Modify: WonderQ-Admin-UI-Vue/src/router/index.ts

  • Modify: WonderQ-Admin-UI-Vue/src/styles.css

  • Step 1: 替换布局模板

<template>
  <div class="app-wrapper" :class="{ mobile: layout.device === 'mobile', hideSidebar: !layout.sidebarOpened }">
    <Sidebar />
    <div class="main-container">
      <div class="fixed-header" :class="{ 'without-tags': !settings.tagsView }">
        <Navbar />
        <TopMenu v-if="settings.navType !== 'left'" />
        <TagsView v-if="settings.tagsView" />
      </div>
      <main class="app-main">
        <RouterView v-slot="{ Component }">
          <component :is="Component" :key="currentViewKey" />
        </RouterView>
      </main>
      <Footer v-if="settings.footerVisible" />
    </div>
    <SettingsDrawer />
  </div>
</template>

只保留一个真实 logout 实现,避免用户下拉和布局层重复确认或重复请求。

  • Step 2: 补充静态路由 meta

给 dashboard、home、play、concierge、leads、media、tools 和 system resource 路由补充 title;dashboard 使用 affix: true。不修改 path、permission 或组件注册,动态菜单标题仍由 AdminMenu.name 提供。

  • Step 3: 替换壳层 CSS

设置以下变量并实现固定 Header、主容器左边距过渡、折叠状态、移动抽屉、遮罩、TagsView 层级和 Element Plus 弹层层级:

:root {
  --sidebar-width: 200px;
  --sidebar-collapsed-width: 54px;
  --navbar-height: 50px;
  --tags-height: 34px;
  --sidebar-bg: #304156;
  --sidebar-hover: #263445;
  --sidebar-submenu-bg: #1f2d3d;
  --sidebar-text: #bfcbd9;
  --sidebar-active: #409eff;
  --app-bg: #f5f7fa;
}

保留 .page-stack、业务网格、媒体和编辑器样式,移除旧壳的 244px、88px、浅色侧栏规则。

  • Step 4: 运行自动检查
yarn test
yarn build

Expected: all existing and new tests PASS; build completes without TypeScript errors.

  • Step 5: Commit
git add -- WonderQ-Admin-UI-Vue/src/layouts/AdminLayout.vue WonderQ-Admin-UI-Vue/src/router/index.ts WonderQ-Admin-UI-Vue/src/styles.css
git commit -m "feat(admin-ui): integrate RuoYi default admin shell"

Task 9: 浏览器视觉与交互验收

Files:

  • Modify only when verification finds a concrete defect: the relevant layout component or WonderQ-Admin-UI-Vue/src/styles.css.

  • Step 1: 确认服务和工作区状态

Run in separate terminals:

Set-Location .\WonderQ-Admin
.\.venv\Scripts\python.exe -m uvicorn app.main:app --host 0.0.0.0 --port 4000 --reload
Set-Location .\WonderQ-Admin-UI-Vue
yarn dev

Do not run Alembic, seed, database reset, or migration repair during UI verification.

  • Step 2: Verify desktop visual baseline at 1440px

Open http://localhost:5604/admin/ and verify sidebar 200px dark #304156, RuoYi blue active state, 50px Navbar, 34px TagsView, content below fixed header, and alignment of Logo, breadcrumb, tools, user dropdown and settings entry against the local RuoYi reference.

  • Step 3: Verify shell interactions

Toggle sidebar; visit /home, /play, /leads; close/refresh tags; use tag right-click actions; open user menu; save settings; toggle theme and TagsView; enter fullscreen; cancel and confirm logout. Expected: unchanged route paths/API calls, no duplicate tags, no console errors.

  • Step 4: Verify responsive states

At 1024px verify the 992px boundary. At 390px verify drawer, mask close, auto close after navigation, no horizontal scroll, and Element Plus dialogs/drawers above the mask.

  • Step 5: Run final checks and inspect diff
yarn test
yarn build
git diff --check HEAD~8..HEAD
git status --short

Expected: tests/build pass, no whitespace errors, only intended UI files are changed, and existing user changes remain untouched.