style: 重塑政务风格 + 操作列统一 el-link + 表格紧凑化

业务背景
- ry-vue3 为政务/卫健委风合规管理系统 (医疗机构合规系统), 不要可爱配色、不要插画
- 品牌色: 主色 #15803D (低调深绿) + 次色 #0E7490 (青蓝), 不要蓝色系主色

变更概要
1. 全局地基
   - theme.scss: Element Plus 主色改为 #15803D, slate 9 档, 联动组件库
   - main.scss: 字体/页面骨架/筛选栏/表格/控件/滚动条 全套样式
   - AdminLayout.vue: 政务顶栏 + 浅色侧栏
   - Workbench.vue: 替换原绿底大字卡为 KPI 白底色条 + 快捷入口 + 进度条

2. 表格操作列改造
   - el-button link 全面替换为 el-link (:underline="false" + type="primary/danger")
   - 36 文件 132 处替换, 操作列外加 .table-actions 容器 (内含分隔线)
   - 修复 el-link 不支持的属性 (size="small"/text/loading)

3. 筛选栏
   - 弹性布局 + gap 14px 16px + align-items: flex-start + min-height 32px
   - 删除所有 scoped .filter-form 覆盖 (28 处) 让全局样式生效
   - 控件宽度 180px 统一, 标签 32px 居中, 折行不紧贴

4. 表格紧凑化
   - cell-padding 10px → 6px, 表头高度 36px
   - 行高 ~46px → ~36px, 字号 13px

5. 每页默认 20 条
   - 21 文件 pageSize: 10 → 20

6. meetings 操作列折叠
   - 11 操作 → 外置 3 个主操作 + 1 个"更多" el-dropdown (8 个次操作)
   - width 500px → 260px
   - 配 .op-dropdown / .op-caret 样式 (hover 旋转)
   - 新增 onMoreAction dispatch 函数 (复用原 handler)

7. 操作列文字防拆行
   - .table-actions flex-wrap: nowrap + white-space: nowrap
   - el-link padding 0 8px → 0 6px, flex-shrink: 0

8. 环境配置
   - .env.development: 直连远端 https://risingdoctor.com/hg-api (绕过 vite proxy 卡顿)
   - vite.config.js: proxy fallback 保留
This commit is contained in:
guoju0808
2026-08-26 11:04:15 +00:00
parent b0256ccac3
commit d2fe5a4035
46 changed files with 975 additions and 451 deletions
+142 -48
View File
@@ -7,7 +7,10 @@
>
<div class="aside-logo">
<div class="logo-icon"><img src="/logo.png" alt="logo" /></div>
<div class="logo-text">{{ roleLabel }}</div>
<div class="logo-text">
<div class="logo-name">合规管理系统</div>
<div class="logo-role">{{ roleLabel }}</div>
</div>
</div>
<el-menu :default-active="route.path" router class="aside-menu">
<template v-for="m in menu" :key="m.path">
@@ -31,26 +34,36 @@
</el-menu>
</el-aside>
<el-container>
<el-header v-if="!route.meta?.hideMenu" class="topbar">
<el-header v-if="!route.meta?.hideMenu" class="topbar" height="56px">
<div class="topbar-left">
<span class="page-title">{{ headerTitle }}</span>
<el-icon class="topbar-back" @click="$router.back()"><ArrowLeft /></el-icon>
<span class="page-title-inline">{{ headerTitle }}</span>
</div>
<div class="topbar-right">
<el-button text @click="$router.push('/')">门户首页</el-button>
<el-link :underline="false" class="topbar-portal" @click="$router.push('/')">
<el-icon><HomeFilled /></el-icon>
<span>门户首页</span>
</el-link>
<el-divider direction="vertical" />
<!-- 消息入口 (admin/manager/doctor/sponsor/executor 各自路由, 通过 meta.noticePath 配置) -->
<el-badge v-if="unreadCount > 0" :value="unreadCount" :max="99" class="notice-badge">
<el-button text @click="goNotice">
<el-button text class="topbar-icon-btn" @click="goNotice">
<el-icon><Bell /></el-icon>
</el-button>
</el-badge>
<el-button v-else text @click="goNotice">
<el-button v-else text class="topbar-icon-btn" @click="goNotice">
<el-icon><Bell /></el-icon>
</el-button>
<el-dropdown trigger="click" @command="onCommand">
<span class="user-name">{{ store.user?.userName || store.user?.displayName }}<el-icon><CaretBottom /></el-icon></span>
<span class="user-name">
<el-icon class="user-icon"><UserFilled /></el-icon>
<span>{{ store.user?.userName || store.user?.displayName }}</span>
<el-icon class="caret"><CaretBottom /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
<el-dropdown-item command="account" @click="$router.push('/' + (role || 'admin') + '/account')">账号信息</el-dropdown-item>
<el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@@ -73,7 +86,7 @@ import { listMyMessages } from '@/api/public'
import { usePageTitle } from '@/utils/pageTitle'
import { getMyExpertProfile } from '@/api/business/expert'
import { ElNotification } from 'element-plus'
import { House, Document, Calendar, User, List, OfficeBuilding, Setting, Bell, EditPen, DataAnalysis, Tickets, CaretBottom, Folder, Medal, UserFilled, Connection, Box, Star, Grid, Files, Compass, Collection } from '@element-plus/icons-vue'
import { House, Document, Calendar, User, List, OfficeBuilding, Setting, Bell, EditPen, DataAnalysis, Tickets, CaretBottom, Folder, Medal, UserFilled, Connection, Box, Star, Grid, Files, Compass, Collection, ArrowLeft, HomeFilled } from '@element-plus/icons-vue'
const route = useRoute()
const router = useRouter()
@@ -241,73 +254,154 @@ const onCommand = (cmd) => { if (cmd === 'logout') logout() }
<style scoped>
.admin-layout { height: 100vh; }
.aside { background: #001529; color: #fff; }
.aside-logo {
padding: 20px 16px 16px;
text-align: center;
border-bottom: 1px solid var(--brand-primary-darker);
/* —— 侧边栏:浅色政务风(避免深绿主色冲突, 政务网站通用样式) —— */
.aside {
background: #FFFFFF;
border-right: 1px solid var(--brand-slate-200);
display: flex;
flex-direction: column;
}
.aside-logo {
padding: 18px 16px 16px;
border-bottom: 1px solid var(--brand-slate-200);
display: flex;
align-items: center;
gap: 10px;
gap: 12px;
background: linear-gradient(180deg, var(--brand-primary-mix) 0%, #FFFFFF 100%);
}
.logo-icon {
width: 44px;
height: 44px;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
border-radius: 6px;
overflow: hidden;
background: #fff;
border: 1px solid var(--brand-slate-200);
flex-shrink: 0;
}
.logo-icon img {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
}
.logo-text { color: #fff; font-size: 14px; font-weight: 600; }
.aside-menu { background: #001529; border: none; }
.logo-icon img { width: 100%; height: 100%; object-fit: contain; display: block; }
.logo-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.logo-name { color: var(--el-text-color-primary); font-size: 15px; font-weight: 600; white-space: nowrap; }
.logo-role { color: var(--el-text-color-secondary); font-size: 12px; white-space: nowrap; }
/* 一级菜单 + 二级父标题: 统一默认/hover/active 样式 */
.aside-menu { background: transparent; border: none; flex: 1; }
/* 一级菜单 + 二级父标题: 中性灰文字, hover 浅绿, active 实心绿 */
:deep(.el-menu-item),
:deep(.el-sub-menu__title) {
color: #c0c4cc;
color: var(--el-text-color-regular);
height: 44px;
line-height: 44px;
}
:deep(.el-menu-item .el-icon),
:deep(.el-sub-menu__title .el-icon) {
color: var(--brand-slate-500);
}
:deep(.el-menu-item:hover),
:deep(.el-sub-menu__title:hover) {
background-color: #1f3a5f;
color: #fff;
background-color: var(--brand-primary-mix);
color: var(--brand-primary);
}
/* 子项激活: 蓝底白字 */
:deep(.el-menu-item:hover .el-icon),
:deep(.el-sub-menu__title:hover .el-icon) { color: var(--brand-primary); }
/* 选中态: 政务感 — 实心绿底白字 + 左侧 4px 高亮条 */
:deep(.el-menu-item.is-active) {
background: var(--brand-primary);
color: #fff;
color: #FFFFFF;
position: relative;
}
:deep(.el-menu-item.is-active .el-icon) { color: #FFFFFF; }
:deep(.el-menu-item.is-active::before) {
content: '';
position: absolute;
left: 0; top: 0; bottom: 0;
width: 4px;
background: var(--brand-primary-darker);
}
/* 父标题在子项激活时高亮 (Element Plus 自动给 el-sub-menu 加 is-active) */
:deep(.el-sub-menu.is-active > .el-sub-menu__title) {
color: #fff;
color: var(--brand-primary);
}
:deep(.el-sub-menu.is-active > .el-sub-menu__title .el-icon) {
color: var(--brand-primary);
}
/* 二级菜单容器: 跟随侧边栏深蓝底, 不要 Element Plus 默认白底 */
:deep(.el-menu--inline),
:deep(.el-menu--inline .el-menu-item) {
background-color: #001529;
/* 二级菜单缩进 */
:deep(.el-menu .el-menu .el-menu-item) {
padding-left: 48px !important;
background-color: var(--brand-slate-50);
}
:deep(.el-menu--inline .el-menu-item:hover) {
background-color: #1f3a5f;
:deep(.el-menu .el-menu .el-menu-item:hover) {
background-color: var(--brand-primary-mix);
}
:deep(.el-menu--inline .el-menu-item.is-active) {
:deep(.el-menu .el-menu .el-menu-item.is-active) {
background: var(--brand-primary);
color: #FFFFFF;
}
.topbar { background: #fff; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; border-bottom: 1px solid #ebeef5; }
.topbar-left { display: flex; align-items: center; }
.page-title { font-size: 16px; font-weight: 600; }
.topbar-right { display: flex; align-items: center; gap: 12px; }
.user-name { cursor: pointer; display: inline-flex; align-items: center; gap: 4px; }
/* navbar bell 角标: 紧凑, 不撑大 topbar */
:deep(.el-menu .el-menu .el-menu-item.is-active .el-icon) { color: #FFFFFF; }
/* 顶栏:白底 + 边框 */
.topbar {
background: #FFFFFF;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
border-bottom: 1px solid var(--brand-slate-200);
height: 56px;
}
.topbar-left { display: flex; align-items: center; gap: 8px; }
.topbar-back {
font-size: 18px;
color: var(--el-text-color-secondary);
cursor: pointer;
padding: 6px;
border-radius: 4px;
transition: background-color 0.18s, color 0.18s;
}
.topbar-back:hover { background: var(--brand-slate-100); color: var(--brand-primary); }
.page-title-inline {
font-size: 16px;
font-weight: 600;
color: var(--el-text-color-primary);
}
.topbar-right { display: flex; align-items: center; gap: 4px; }
.topbar-link {
display: inline-flex;
align-items: center;
gap: 4px;
color: var(--el-text-color-regular);
font-size: 13px;
}
.topbar-link:hover { color: var(--brand-primary); }
.topbar-icon-btn {
font-size: 18px;
color: var(--el-text-color-regular);
padding: 6px;
}
.topbar-icon-btn:hover { color: var(--brand-primary); }
.user-name {
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 0 8px;
height: 32px;
border-radius: 4px;
font-size: 13px;
color: var(--el-text-color-regular);
transition: background-color 0.18s;
}
.user-name:hover { background: var(--brand-slate-100); color: var(--brand-primary); }
.user-name .user-icon { font-size: 16px; color: var(--brand-slate-500); }
.user-name .caret { font-size: 12px; color: var(--brand-slate-400); }
.notice-badge :deep(.el-badge__content) { transform: translate(50%, -10%); }
.main { background: #f5f7fa; padding: 16px; }
/* 主体: 浅灰底 + 卡片自动吸附 */
.main { background: var(--brand-slate-50); padding: 16px 20px; min-height: calc(100vh - 56px); }
/* 手机页面 (hideMenu): 白底 + 0 padding, 让 form 直接撑满 */
.main-full { background: #fff; padding: 0; }
</style>