From e46df62c0b86457d577a1b05920bccdf7e39a9ff Mon Sep 17 00:00:00 2001 From: chenxing Date: Thu, 8 Oct 2026 15:28:44 +0800 Subject: [PATCH] =?UTF-8?q?refactor(header):=20=E7=94=A8=E6=88=B7=E5=85=A5?= =?UTF-8?q?=E5=8F=A3=E6=95=B4=E5=90=88=E4=B8=BA=E5=8D=95=E5=8D=A1=E7=89=87?= =?UTF-8?q?=E2=80=94=E2=80=94=E4=BA=BA=E5=83=8F=E5=9B=BE=E6=A0=87=E6=89=BF?= =?UTF-8?q?=E8=BD=BD=E5=88=87=E6=8D=A2=E8=BA=AB=E4=BB=BD+=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E4=BF=A1=E6=81=AF+=E5=90=8E=E5=8F=B0=E7=AE=A1?= =?UTF-8?q?=E7=90=86+=E9=80=80=E5=87=BA=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 铃铛与齿轮两枚图标下线,人像图标成为唯一入口,点击展开一张卡片: 切换身份列表(9条实测溢出滚动)+ 个人信息 + 后台管理(仅管理员)+ 退出登录。 删除无功能损失的冗余入口:原齿轮菜单的 sys/logo/sidebar/statset/backend 五项 已于 2026-09-30 迁入 /settings(Settings.vue:620 ALL_PANES),齿轮成为纯冗余。 消息中心仍走左侧菜单 /msg,LeftMenu 未读角标不受影响。 改动要点: - isUserInfoShow + isRoleInfoShow 合并为 isUserCardShow,9 处函数体同步改名; 删除 onUserinfoClickRoles、storeToRefs import 与 unreadCount 解构(模板已无 Badge) - showUserInfo 改跳 /settings?pane=profile,不再 emit showUserInfoModal (该事件仍有 AppHeader/BschIframe/harness-main 三处消费方,不可删) - :key 用 roleId-schId-childId 复合键,避免同校多身份时 roleId 重复 - header.scss 删 .roleinfo-wrap 与随之失效的 @use "sass:color",新增 .user-card (身份列表 max-height:310px + overflow-y:scroll + 6px 自绘滚动条) - settingsWallpaper 等 6 个入口函数保留:对应 Modal 仍挂模板尾部且 logoList 是 header 徽标数据源,只改内部变量名并加注释 验证:未声明标识符全量扫 47 个 .vue 零问题;vite build 通过;mock 取证实测 scrollHeight 683 > clientHeight 310 出滚动条、1 条时 75=75 不滚动、 menu-rows 管理员 2/普通用户 1、bell-count 与 gear-count 均为 0。 另:.gitignore 忽略卡片取证三件套(本地临时用,与 harness-* 同类不进 git)。 --- .gitignore | 7 ++ src/assets/styles/header.scss | 185 ++++++++++++++++++++++++++------- src/components/Header.vue | 190 +++++++++++++--------------------- 3 files changed, 222 insertions(+), 160 deletions(-) diff --git a/.gitignore b/.gitignore index 7dbd273..4275bcc 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,13 @@ dist dist-ssr *.local +# 视觉取证 harness(本地临时用,不进 git;与 harness-* 同类) +.vite-card-harness +dist_card_harness +card-harness.html +card-harness.vite.config.ts +src/card-harness-main.ts + yarn.lock .npmrc # Editor directories and files diff --git a/src/assets/styles/header.scss b/src/assets/styles/header.scss index 9fa2b63..45d16ac 100644 --- a/src/assets/styles/header.scss +++ b/src/assets/styles/header.scss @@ -1,4 +1,3 @@ -@use "sass:color"; .default-header { width: 100%; height: var(--headerH); @@ -220,59 +219,165 @@ } } -.roleinfo-wrap { - svg { - width: 20px; - height: 20px; - } - h3.title { - height: 50px; - padding: 12px 16px; - color: #272829; - font-weight: 600; - font-size: 16px; - border-bottom: 1px solid #e8eaec; - display: flex; - align-items: center; - } - .roles-ul { - max-height: 310px; - } - .roles-ul li { - background-color: #f9f9f9; - display: flex; - align-items: center; - height: 62px; - cursor: pointer; - user-select: none; +/* ===== 用户卡片(人像图标唯一入口,2026-10-08)===== + 规格来源:design/05-Header用户卡片整合-切换身份+个人信息+后台管理+退出登录.html + 全部复用既有变量,仅新增卡片骨架与滚动条 */ +.user-card { + width: 260px; + background: #fff; - &:hover { - // background-color: darken(#f9f9f9, 5%); - background-color: color.adjust($color: #f9f9f9, $lightness: -5%); - } + .user-card-hd { + height: 50px; + padding: 0 16px; + display: flex; + align-items: center; + border-bottom: 1px solid #e8eaec; svg { - margin: 0 16px; + width: 20px; + height: 20px; + margin-right: 8px; + } + .ttl { + font-size: 16px; + font-weight: 600; + color: #272829; + } + .close { + margin-left: auto; + width: 22px; + height: 22px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + color: #9aa0a8; + font-size: 14px; + line-height: 1; + cursor: pointer; + transition: all 0.2s; + + &:hover { + background: #f2f3f5; + color: #5a5f68; + } + } + } + + /* 身份列表:有最大高度,超出显示滚动条 */ + .user-card-roles { + max-height: 310px; + overflow-y: scroll; + overscroll-behavior: contain; + + &::-webkit-scrollbar { + width: 6px; + } + &::-webkit-scrollbar-track { + background: transparent; + } + &::-webkit-scrollbar-thumb { + background: #d4d7dd; + border-radius: 3px; + } + &::-webkit-scrollbar-thumb:hover { + background: #b9bec7; + } + } + + .u-role { + display: flex; + align-items: center; + padding: 9px 16px; + cursor: pointer; + user-select: none; + transition: background 0.15s; + + > svg { + width: 16px; + height: 16px; + margin-right: 16px; + flex: 0 0 16px; } - .user-wrap { - padding: 9px 0; - + .who { + flex: 1; + min-width: 0; + padding: 8px 0; border-bottom: 1px solid #e9e9e9; - flex: auto; - .schname-box { + + .nm { + display: flex; + align-items: center; + font-size: 15px; + color: #323233; + line-height: 20px; + + .t { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-right: 8px; + } + } + .sch { margin-top: 2px; font-size: 12px; color: #656667; + line-height: 17px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } } - &:last-child .user-wrap { + + &:last-child .who { border-bottom: none; } + &:hover { + background: #f9f9f9; + } + &.active { + cursor: default; + background: #f9f9f9; + + .nm { + font-weight: 500; + } + } } - .roles-ul li.acitve { - cursor: default; - background-color: #f9f9f9; + + .user-card-menu { + border-top: 1px solid #e9e9e9; + + .arw { + width: 16px; + height: 16px; + margin-left: auto; + } + } + + .user-card-logout { + padding: 10px 20px 16px; + + .logout-btn { + width: 100%; + height: 38px; + border-radius: 19px; + background: #fff; + border: 1px solid #e2e5ea; + color: #323233; + font-size: 14px; + font-family: inherit; + cursor: pointer; + transition: all 0.18s; + + &:hover { + border-color: #577afb; + background: #f7f9ff; + color: #577afb; + } + } } } diff --git a/src/components/Header.vue b/src/components/Header.vue index 69f211e..46156db 100644 --- a/src/components/Header.vue +++ b/src/components/Header.vue @@ -20,110 +20,64 @@ - +
  • -
    - - - -
    -
  • -
  • - -
    + +
    -
    -
  • -
  • - -
    - -
    -
  • @@ -234,7 +188,6 @@ import SvgIcon from "@/components/SvgIcon.vue"; import vuescroll from "vuescroll"; import { ref, reactive, computed, onMounted, onUnmounted, inject, watch } from "vue"; import { useDefaultStore } from "@/store"; -import { storeToRefs } from "pinia"; import { useRoute, useRouter } from "vue-router"; import { requireImg } from "@/utils/hook/useImg"; import ContextMenu from "@/components/ContextMenu.vue"; @@ -257,8 +210,8 @@ const refMsgApproval = ref(); const { $conf } = useCurrentInstance(); const baseUrl = computed(() => $conf.fileHost); -/** 未读消息数上提 pinia store(消息对接 2026-09-30):Header 角标与 /msg 页左栏、LeftMenu 同源 */ -const { unreadCount } = storeToRefs(store); +/** 未读消息数上提 pinia store(消息对接 2026-09-30):Header 铃铛角标已于 2026-10-08 下线, + * 但摘要拉取与 WS 推送仍需保留 —— /msg 页左栏与 LeftMenu 角标与本store 同源 */ const refSearchInput = ref(); const refStatsBarModal = ref(); @@ -272,8 +225,7 @@ const logoUrl = computed(() => { const sysLogo = logoList.value.find(x => x.type == 0); return sysLogo ? sysLogo.url : ""; }); -const isUserInfoShow = ref(false), - isRoleInfoShow = ref(false), +const isUserCardShow = ref(false), isWallpaperShow = ref(false), isLogoShow = ref(false), isSearching = ref(false), @@ -431,7 +383,7 @@ function getIsCheckRoles(item: RoleInfo) { async function changeRole(role: RoleInfo) { if (!!getIsCheckRoles(role)) return; setTimeout(() => { - isRoleInfoShow.value = false; + isUserCardShow.value = false; }, 150); store.setCurRoleInfo(role); await BaseApi.savUserRole(role); @@ -440,13 +392,6 @@ async function changeRole(role: RoleInfo) { await loadLogoList(); } -function onUserinfoClickRoles() { - isUserInfoShow.value = false; - setTimeout(() => { - isRoleInfoShow.value = true; - }, 0); -} - /** 消息类型名(UserMsg msgType:0系统通知/1业务提醒/2审批待办);WS 帧缺 appName 时的降级标签 */ function msgTypeLabel(msgType: number | null) { return { 0: "系统通知", 1: "业务提醒", 2: "审批待办" }[msgType as 0 | 1 | 2] || "系统通知"; @@ -463,25 +408,28 @@ async function getUserWallpaperList() { // }); wallpaperList.value = orderBy(list, ["type", "id"], ["asc", "asc"]); } +/** 以下入口函数自2026-10-08 齿轮菜单下线后已无 UI 调用方,但对应 Modal 组件仍挂在模板尾部、 + * 且 logoList 是header 徽标的数据源,故保留函数体不删,仅把已并轨的 isUserInfoShow 换成 isUserCardShow。 + * 恢复入口时直接绑到卡片菜单即可。 */ async function settingsWallpaper() { await getUserWallpaperList(); - isUserInfoShow.value = false; + isUserCardShow.value = false; isWallpaperShow.value = true; } /** 边栏设置入口改跳设置中心 pane(原 settingsMenu 开旧 MenuBarModal 的能力已于 2026-09-30 迁入 Settings.vue sidebar pane) */ function linkSidebarSetting() { - isUserInfoShow.value = false; + isUserCardShow.value = false; router.push("/settings?pane=sidebar"); } async function settingsStats() { - isUserInfoShow.value = false; + isUserCardShow.value = false; refStatsBarModal.value.show(); } async function settingsTeacherCopilot() { - isUserInfoShow.value = false; + isUserCardShow.value = false; refTeacherCopilotModal.value.show(); } @@ -523,27 +471,29 @@ async function toggleWidgetShow() { } function linkSysSetting() { - isUserInfoShow.value = false; + isUserCardShow.value = false; router.push("/syssetting"); } function linkBackendManage() { - isUserInfoShow.value = false; + isUserCardShow.value = false; const url = `${$conf.baseHost}/userbase/front/index.html`; window.open(url, "_blank"); } async function linkLogoSetting() { await loadLogoList(); - isUserInfoShow.value = false; + isUserCardShow.value = false; isLogoShow.value = true; } function showSeachBox() { } +/** 卡片菜单:个人信息 → 设置中心 profile pane(与通知端/用户中心同一地址)。 + * 注意:mittBus 的 showUserInfoModal 事件仍由 AppHeader/BschIframe 使用,此处不再触发 */ function showUserInfo() { - isUserInfoShow.value = false; - mittBus.emit("showUserInfoModal"); + isUserCardShow.value = false; + router.push("/settings?pane=profile"); } function logout() {