develop #30
@ -14,9 +14,9 @@ export default defineConfig({
|
||||
},
|
||||
proxy: {
|
||||
'/api/': {
|
||||
target: 'http://10.39.13.78:8001/',
|
||||
// target: 'http://10.39.13.78:8002/',
|
||||
// target: 'https://test-admin.linyikj.com.cn/',
|
||||
// target: 'https://admin.linyikj.com.cn/',
|
||||
target: 'https://admin.linyikj.com.cn/',
|
||||
changeOrigin: true,
|
||||
pathRewrite: { '^': '' },
|
||||
},
|
||||
|
||||
969
docs/superpowers/plans/2026-03-30-layout-redesign.md
Normal file
969
docs/superpowers/plans/2026-03-30-layout-redesign.md
Normal file
@ -0,0 +1,969 @@
|
||||
# Layout结构优化实施计划
|
||||
|
||||
> **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:** 将UmiJS layout从mix模式优化为side模式,实现左右分栏布局,白色主题风格,侧边栏可折叠。
|
||||
|
||||
**Architecture:**
|
||||
1. 修改UmiJS layout配置,从'mix'改为'side'模式
|
||||
2. 自定义Header渲染组件,保持用户信息功能
|
||||
3. 自定义Logo渲染组件,实现展开/折叠智能显示
|
||||
4. 更新token配置实现白色主题
|
||||
|
||||
**Tech Stack:** UmiJS Max, Ant Design Pro Layout, React, TypeScript
|
||||
|
||||
---
|
||||
|
||||
## 文件结构
|
||||
|
||||
**修改的文件:**
|
||||
- `src/common/libs/umi/layoutConfig.tsx` - 核心layout配置
|
||||
|
||||
**新建的文件:**
|
||||
- `src/common/components/layout/CustomHeader.tsx` - 自定义Header组件
|
||||
- `src/common/components/layout/CustomLogo.tsx` - 自定义Logo组件
|
||||
- `src/common/components/layout/index.ts` - 导出新组件
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 备份现有layout配置
|
||||
|
||||
**Files:**
|
||||
- Copy: `src/common/libs/umi/layoutConfig.tsx` → `src/common/libs/umi/layoutConfig.tsx.backup`
|
||||
|
||||
- [ ] **Step 1: 创建备份文件**
|
||||
|
||||
```bash
|
||||
cp src/common/libs/umi/layoutConfig.tsx src/common/libs/umi/layoutConfig.tsx.backup
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证备份成功**
|
||||
|
||||
```bash
|
||||
ls -lh src/common/libs/umi/layoutConfig.tsx.backup
|
||||
```
|
||||
|
||||
Expected: 输出文件大小和日期
|
||||
|
||||
- [ ] **Step 3: 提交备份**
|
||||
|
||||
```bash
|
||||
git add src/common/libs/umi/layoutConfig.tsx.backup
|
||||
git commit -m "chore: backup layoutConfig before refactoring"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 修改layout基础配置
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/common/libs/umi/layoutConfig.tsx:31-78`
|
||||
|
||||
- [ ] **Step 1: 修改layout模式和基础配置**
|
||||
|
||||
将 `layout: 'mix'` 改为 `layout: 'side'`,移除logo配置(稍后自定义):
|
||||
|
||||
```typescript
|
||||
export const LayoutConfig: RuntimeConfig['layout'] = () => {
|
||||
const { snap } = useMyState();
|
||||
|
||||
return {
|
||||
title: snap.session.campus?.name ?? '物业管理系统',
|
||||
logo: false, // 禁用默认logo,使用自定义
|
||||
layout: 'side', // 从 'mix' 改为 'side'
|
||||
colorPrimary: '#1890ff',
|
||||
siderWidth: 220,
|
||||
pure: history.location.pathname === '/login',
|
||||
avatarProps: {
|
||||
render: () => <AvatarProps user={snap.session.user} />,
|
||||
},
|
||||
collapsedButtonRender: false,
|
||||
// 保持其他配置不变...
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行应用查看变化**
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Expected: 应用启动,layout已切换为side模式
|
||||
|
||||
- [ ] **Step 3: 提交配置修改**
|
||||
|
||||
```bash
|
||||
git add src/common/libs/umi/layoutConfig.tsx
|
||||
git commit -m "refactor: change layout mode from mix to side"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: 更新主题token为白色主题
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/common/libs/umi/layoutConfig.tsx:50-61`
|
||||
|
||||
- [ ] **Step 1: 替换token配置**
|
||||
|
||||
将现有的深色主题token替换为白色主题:
|
||||
|
||||
```typescript
|
||||
export const LayoutConfig: RuntimeConfig['layout'] = () => {
|
||||
const { snap } = useMyState();
|
||||
|
||||
return {
|
||||
// ... 前面的配置保持不变
|
||||
|
||||
token: {
|
||||
bgLayout: '#f5f5f5', // 浅灰布局背景
|
||||
header: {
|
||||
colorBgHeader: '#ffffff', // 白色header背景
|
||||
colorHeaderTitle: '#262626', // 深色标题文字
|
||||
colorTextRightActionsItem: '#595959', // 深色操作文字
|
||||
heightLayoutHeader: 64, // Header高度64px
|
||||
},
|
||||
sider: {
|
||||
colorMenuBackground: '#ffffff', // 白色菜单背景
|
||||
colorMenuText: '#595959', // 深色菜单文字
|
||||
colorMenuTextSelected: '#1890ff', // 激活菜单文字蓝色
|
||||
colorMenuItemBgSelected: '#e6f7ff', // 激活菜单背景浅蓝
|
||||
colorMenuDivider: '#f0f0f0', // 分割线颜色
|
||||
},
|
||||
},
|
||||
|
||||
// ... 后面的配置保持不变
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 检查样式变化**
|
||||
|
||||
在浏览器中查看header和侧边栏背景是否已变为白色
|
||||
|
||||
Expected: Header和侧边栏背景为白色
|
||||
|
||||
- [ ] **Step 3: 提交token配置**
|
||||
|
||||
```bash
|
||||
git add src/common/libs/umi/layoutConfig.tsx
|
||||
git commit -m "style: update theme tokens to white color scheme"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: 创建CustomLogo组件
|
||||
|
||||
**Files:**
|
||||
- Create: `src/common/components/layout/CustomLogo.tsx`
|
||||
|
||||
- [ ] **Step 1: 创建CustomLogo组件文件**
|
||||
|
||||
```typescript
|
||||
import React from 'react';
|
||||
import { MyIcons, MyIconsType } from '@/common';
|
||||
|
||||
interface CustomLogoProps {
|
||||
collapsed: boolean;
|
||||
}
|
||||
|
||||
export const CustomLogo: React.FC<CustomLogoProps> = ({ collapsed }) => {
|
||||
return (
|
||||
<div className="custom-logo">
|
||||
<div className="logo-icon">
|
||||
<MyIcons.HomeOutlined style={{ fontSize: '32px', color: '#1890ff' }} />
|
||||
</div>
|
||||
{!collapsed && (
|
||||
<div className="logo-text">
|
||||
{collapsed ? '' : '物业管理系统'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 添加组件样式**
|
||||
|
||||
在文件末尾添加样式:
|
||||
|
||||
```typescript
|
||||
export const CustomLogo: React.FC<CustomLogoProps> = ({ collapsed }) => {
|
||||
// ... 组件代码保持不变
|
||||
};
|
||||
|
||||
// 添加样式定义
|
||||
export const customLogoStyles = `
|
||||
.custom-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 64px;
|
||||
padding: 0 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
}
|
||||
|
||||
.custom-logo .logo-icon {
|
||||
font-size: 32px;
|
||||
min-width: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.custom-logo .logo-text {
|
||||
margin-left: 12px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
white-space: nowrap;
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
/* 折叠状态下的样式调整 */
|
||||
.ant-layout-sider-collapsed .custom-logo {
|
||||
padding: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ant-layout-sider-collapsed .custom-logo .logo-text {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 提交CustomLogo组件**
|
||||
|
||||
```bash
|
||||
git add src/common/components/layout/CustomLogo.tsx
|
||||
git commit -m "feat: add CustomLogo component with collapse support"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 创建CustomHeader组件
|
||||
|
||||
**Files:**
|
||||
- Create: `src/common/components/layout/CustomHeader.tsx`
|
||||
|
||||
- [ ] **Step 1: 创建CustomHeader组件文件**
|
||||
|
||||
```typescript
|
||||
import React from 'react';
|
||||
import { Layout, Dropdown, Avatar, Space } from 'antd';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { AvatarProps } from './AvatarProps';
|
||||
import { useMyState } from '@/common';
|
||||
|
||||
const { Header } = Layout;
|
||||
|
||||
interface CustomHeaderProps {
|
||||
collapsed?: boolean;
|
||||
toggle?: () => void;
|
||||
}
|
||||
|
||||
export const CustomHeader: React.FC<CustomHeaderProps> = ({ collapsed, toggle }) => {
|
||||
const { snap } = useMyState();
|
||||
|
||||
const menuItems: MenuProps['items'] = [
|
||||
{
|
||||
key: 'profile',
|
||||
label: '个人资料',
|
||||
icon: <span>👤</span>,
|
||||
},
|
||||
{
|
||||
key: 'password',
|
||||
label: '修改密码',
|
||||
icon: <span>🔒</span>,
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
key: 'logout',
|
||||
label: '退出登录',
|
||||
icon: <span>🚪</span>,
|
||||
danger: true,
|
||||
onClick: () => {
|
||||
// 添加退出登录逻辑
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Header className="custom-header">
|
||||
{/* 左侧区域 */}
|
||||
<div className="header-left">
|
||||
{/* 可以添加面包屑导航或其他内容 */}
|
||||
</div>
|
||||
|
||||
{/* 右侧用户信息区域 */}
|
||||
<div className="header-right">
|
||||
<Dropdown menu={{ items: menuItems }} placement="bottomRight">
|
||||
<div style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }}>
|
||||
<AvatarProps user={snap.session.user} />
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 添加Header样式**
|
||||
|
||||
在文件末尾添加样式定义:
|
||||
|
||||
```typescript
|
||||
export const CustomHeader: React.FC<CustomHeaderProps> = ({ collapsed, toggle }) => {
|
||||
// ... 组件代码保持不变
|
||||
};
|
||||
|
||||
// 添加样式定义
|
||||
export const customHeaderStyles = `
|
||||
.custom-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 24px;
|
||||
background: #ffffff !important;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
line-height: 64px;
|
||||
}
|
||||
|
||||
.custom-header .header-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.custom-header .header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.custom-header .header-right > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.custom-header .header-right > div:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
`;
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 提交CustomHeader组件**
|
||||
|
||||
```bash
|
||||
git add src/common/components/layout/CustomHeader.tsx
|
||||
git commit -m "feat: add CustomHeader component with user dropdown"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: 创建组件导出索引
|
||||
|
||||
**Files:**
|
||||
- Create: `src/common/components/layout/index.ts`
|
||||
|
||||
- [ ] **Step 1: 创建index导出文件**
|
||||
|
||||
```typescript
|
||||
export { CustomLogo } from './CustomLogo';
|
||||
export { CustomHeader } from './CustomHeader';
|
||||
export { default as AvatarProps } from './AvatarProps';
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证导出正确**
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
Expected: 构建成功,无TypeScript错误
|
||||
|
||||
- [ ] **Step 3: 提交导出文件**
|
||||
|
||||
```bash
|
||||
git add src/common/components/layout/index.ts
|
||||
git commit -m "chore: add layout components export index"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: 在layoutConfig中集成自定义组件
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/common/libs/umi/layoutConfig.tsx:1-80`
|
||||
|
||||
- [ ] **Step 1: 导入自定义组件**
|
||||
|
||||
在文件顶部添加导入:
|
||||
|
||||
```typescript
|
||||
// import Logo from '@/assets/bitcoin.webp';
|
||||
import { MyIcons, MyIconsType, PermissionsType, useMyState } from '@/common';
|
||||
import { Link, RuntimeConfig, history } from '@umijs/max';
|
||||
import AvatarProps from '../../components/layout/AvatarProps';
|
||||
import { CustomHeader, CustomLogo } from '@/common/components/layout'; // 新增
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 添加自定义渲染函数**
|
||||
|
||||
在LayoutConfig函数返回对象中添加headerRender和logoRenderer:
|
||||
|
||||
```typescript
|
||||
export const LayoutConfig: RuntimeConfig['layout'] = () => {
|
||||
const { snap } = useMyState();
|
||||
|
||||
return {
|
||||
title: snap.session.campus?.name ?? '物业管理系统',
|
||||
logo: false,
|
||||
layout: 'side',
|
||||
colorPrimary: '#1890ff',
|
||||
siderWidth: 220,
|
||||
pure: history.location.pathname === '/login',
|
||||
|
||||
// 新增:自定义Header渲染
|
||||
headerRender: () => {
|
||||
return <CustomHeader />;
|
||||
},
|
||||
|
||||
// 新增:自定义Logo渲染
|
||||
logoRenderer: (collapsed: boolean) => {
|
||||
return <CustomLogo collapsed={collapsed} />;
|
||||
},
|
||||
|
||||
avatarProps: {
|
||||
render: () => <AvatarProps user={snap.session.user} />,
|
||||
},
|
||||
collapsedButtonRender: false,
|
||||
|
||||
token: {
|
||||
bgLayout: '#f5f5f5',
|
||||
header: {
|
||||
colorBgHeader: '#ffffff',
|
||||
colorHeaderTitle: '#262626',
|
||||
colorTextRightActionsItem: '#595959',
|
||||
heightLayoutHeader: 64,
|
||||
},
|
||||
sider: {
|
||||
colorMenuBackground: '#ffffff',
|
||||
colorMenuText: '#595959',
|
||||
colorMenuTextSelected: '#1890ff',
|
||||
colorMenuItemBgSelected: '#e6f7ff',
|
||||
colorMenuDivider: '#f0f0f0',
|
||||
},
|
||||
},
|
||||
|
||||
menuItemRender: (item, dom) => <Link to={item.path || '/'}>{dom}</Link>,
|
||||
|
||||
menu: {
|
||||
params: snap.session.permissions,
|
||||
request: async () => {
|
||||
let objjs: any = [];
|
||||
snap.session.permissions?.forEach((res: any) => {
|
||||
objjs.push(res);
|
||||
});
|
||||
let data = objjs.sort((a: any, b: any) => {
|
||||
return a._lft - b._lft;
|
||||
});
|
||||
const menus = loopMenu(data);
|
||||
return Promise.resolve(menus);
|
||||
},
|
||||
},
|
||||
|
||||
unAccessible: <div>unAccessible</div>,
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 测试自定义组件渲染**
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Expected: 应用启动,自定义Logo和Header正确显示
|
||||
|
||||
- [ ] **Step 4: 提交集成修改**
|
||||
|
||||
```bash
|
||||
git add src/common/libs/umi/layoutConfig.tsx
|
||||
git commit -m "feat: integrate CustomHeader and CustomLogo into layout"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 8: 添加全局样式
|
||||
|
||||
**Files:**
|
||||
- Create: `src/global.less` (如果不存在) 或 Modify: `src/global.less`
|
||||
|
||||
- [ ] **Step 1: 检查global.less是否存在**
|
||||
|
||||
```bash
|
||||
ls -la src/global.less
|
||||
```
|
||||
|
||||
如果文件存在,跳过此步。如果不存在,创建文件。
|
||||
|
||||
- [ ] **Step 2: 创建或修改global.less**
|
||||
|
||||
如果文件不存在,创建它:
|
||||
|
||||
```less
|
||||
@import '~antd/es/style/reset.css';
|
||||
|
||||
// 全局样式
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
||||
sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.5715;
|
||||
color: #262626;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
// 自定义Logo样式
|
||||
.custom-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 64px;
|
||||
padding: 0 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.custom-logo .logo-icon {
|
||||
font-size: 32px;
|
||||
min-width: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.custom-logo .logo-text {
|
||||
margin-left: 12px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
white-space: nowrap;
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
// 折叠状态
|
||||
.ant-layout-sider-collapsed .custom-logo {
|
||||
padding: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ant-layout-sider-collapsed .custom-logo .logo-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// 自定义Header样式
|
||||
.custom-header {
|
||||
display: flex !important;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 24px !important;
|
||||
background: #ffffff !important;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
line-height: 64px;
|
||||
}
|
||||
|
||||
.custom-header .header-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.custom-header .header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.custom-header .header-right > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.custom-header .header-right > div:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
// 内容区域
|
||||
.ant-pro-grid-content {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
// 页面容器卡片
|
||||
.ant-pro-page-container {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.ant-pro-page-container .ant-pro-page-container-warp {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03);
|
||||
padding: 24px;
|
||||
}
|
||||
```
|
||||
|
||||
如果文件已存在,在文件末尾添加上述样式。
|
||||
|
||||
- [ ] **Step 3: 验证样式加载**
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Expected: 样式正确应用,布局显示正常
|
||||
|
||||
- [ ] **Step 4: 提交全局样式**
|
||||
|
||||
```bash
|
||||
git add src/global.less
|
||||
git commit -m "style: add global styles for custom layout components"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 9: 测试侧边栏折叠功能
|
||||
|
||||
**Files:**
|
||||
- Test: Manual testing in browser
|
||||
|
||||
- [ ] **Step 1: 启动应用**
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 测试侧边栏折叠**
|
||||
|
||||
在浏览器中:
|
||||
1. 点击侧边栏折叠按钮(如果可见)
|
||||
2. 或者修改layoutConfig临时添加折叠按钮用于测试
|
||||
|
||||
Expected:
|
||||
- 侧边栏宽度从220px变为64px
|
||||
- Logo文字消失,仅显示图标
|
||||
- 菜单文字隐藏,图标居中
|
||||
|
||||
- [ ] **Step 3: 测试侧边栏展开**
|
||||
|
||||
再次点击折叠按钮
|
||||
|
||||
Expected:
|
||||
- 侧边栏宽度从64px恢复为220px
|
||||
- Logo文字显示
|
||||
- 菜单文字显示
|
||||
|
||||
- [ ] **Step 4: 测试Tooltip**
|
||||
|
||||
折叠状态下,鼠标悬停在菜单图标上
|
||||
|
||||
Expected: 显示完整菜单项文字的Tooltip
|
||||
|
||||
- [ ] **Step 5: 提交测试结果(如果有问题需修复)**
|
||||
|
||||
如果有问题需要修复,创建新的task。如果测试通过,继续下一步。
|
||||
|
||||
---
|
||||
|
||||
### Task 10: 测试菜单导航和权限
|
||||
|
||||
**Files:**
|
||||
- Test: Manual testing in browser
|
||||
|
||||
- [ ] **Step 1: 测试菜单点击导航**
|
||||
|
||||
点击各个菜单项
|
||||
|
||||
Expected:
|
||||
- 页面正确跳转
|
||||
- 菜单项激活状态正确显示(蓝色文字+浅蓝背景)
|
||||
- 左侧蓝色指示器显示
|
||||
|
||||
- [ ] **Step 2: 测试子菜单展开**
|
||||
|
||||
点击有子菜单的项
|
||||
|
||||
Expected:
|
||||
- 子菜单平滑展开
|
||||
- 层级缩进正确(每级20px)
|
||||
|
||||
- [ ] **Step 3: 测试权限控制**
|
||||
|
||||
使用不同权限的账户登录
|
||||
|
||||
Expected:
|
||||
- 菜单根据权限正确显示/隐藏
|
||||
- 无权限的菜单项不显示
|
||||
|
||||
- [ ] **Step 4: 提交测试结果(如果有问题需修复)**
|
||||
|
||||
如果有问题需要修复,创建新的task。如果测试通过,继续下一步。
|
||||
|
||||
---
|
||||
|
||||
### Task 11: 测试Header功能
|
||||
|
||||
**Files:**
|
||||
- Test: Manual testing in browser
|
||||
|
||||
- [ ] **Step 1: 测试用户信息显示**
|
||||
|
||||
Expected:
|
||||
- 头像正确显示
|
||||
- 用户名正确显示
|
||||
|
||||
- [ ] **Step 2: 测试下拉菜单**
|
||||
|
||||
点击用户信息区域
|
||||
|
||||
Expected:
|
||||
- 下拉菜单淡入显示
|
||||
- 菜单项:个人资料、修改密码、退出登录
|
||||
|
||||
- [ ] **Step 3: 测试退出登录**
|
||||
|
||||
点击"退出登录"
|
||||
|
||||
Expected:
|
||||
- 清除token
|
||||
- 跳转到登录页面
|
||||
|
||||
- [ ] **Step 4: 提交测试结果(如果有问题需修复)**
|
||||
|
||||
如果有问题需要修复,创建新的task。如果测试通过,继续下一步。
|
||||
|
||||
---
|
||||
|
||||
### Task 12: 响应式测试
|
||||
|
||||
**Files:**
|
||||
- Test: Manual testing in browser DevTools
|
||||
|
||||
- [ ] **Step 1: 测试桌面端(>1024px)**
|
||||
|
||||
Expected: 侧边栏宽度220px,完整显示
|
||||
|
||||
- [ ] **Step 2: 测试平板端(768px-1024px)**
|
||||
|
||||
使用浏览器DevTools切换到平板视图
|
||||
|
||||
Expected: 布局正常,侧边栏可能自动收起
|
||||
|
||||
- [ ] **Step 3: 测试移动端(<768px)**
|
||||
|
||||
使用浏览器DevTools切换到移动视图
|
||||
|
||||
Expected:
|
||||
- 侧边栏自动收起
|
||||
- 内容区域内边距适应
|
||||
|
||||
- [ ] **Step 4: 提交测试结果(如果有问题需修复)**
|
||||
|
||||
如果有问题需要修复,创建新的task。如果测试通过,继续下一步。
|
||||
|
||||
---
|
||||
|
||||
### Task 13: 浏览器兼容性测试
|
||||
|
||||
**Files:**
|
||||
- Test: Manual testing in different browsers
|
||||
|
||||
- [ ] **Step 1: Chrome浏览器测试**
|
||||
|
||||
打开Chrome浏览器测试所有功能
|
||||
|
||||
Expected: 所有功能正常
|
||||
|
||||
- [ ] **Step 2: Firefox浏览器测试**
|
||||
|
||||
打开Firefox浏览器测试所有功能
|
||||
|
||||
Expected: 所有功能正常
|
||||
|
||||
- [ ] **Step 3: Safari浏览器测试(如果有Mac)**
|
||||
|
||||
打开Safari浏览器测试所有功能
|
||||
|
||||
Expected: 所有功能正常
|
||||
|
||||
- [ ] **Step 4: Edge浏览器测试**
|
||||
|
||||
打开Edge浏览器测试所有功能
|
||||
|
||||
Expected: 所有功能正常
|
||||
|
||||
- [ ] **Step 5: 提交测试结果(如果有问题需修复)**
|
||||
|
||||
如果有问题需要修复,创建新的task。如果测试通过,继续下一步。
|
||||
|
||||
---
|
||||
|
||||
### Task 14: 性能检查
|
||||
|
||||
**Files:**
|
||||
- Test: Performance testing
|
||||
|
||||
- [ ] **Step 1: 检查折叠动画性能**
|
||||
|
||||
使用浏览器DevTools Performance面板录制折叠动画
|
||||
|
||||
Expected: 动画流畅,FPS > 30
|
||||
|
||||
- [ ] **Step 2: 检查首次渲染性能**
|
||||
|
||||
刷新页面,查看DevTools Network和Performance
|
||||
|
||||
Expected: 首次渲染时间 < 2秒
|
||||
|
||||
- [ ] **Step 3: 检查内存泄漏**
|
||||
|
||||
打开应用,使用一段时间,查看DevTools Memory
|
||||
|
||||
Expected: 无明显内存泄漏
|
||||
|
||||
- [ ] **Step 4: 提交性能检查结果(如果有问题需优化)**
|
||||
|
||||
如果有问题需要优化,创建新的task。如果性能良好,继续下一步。
|
||||
|
||||
---
|
||||
|
||||
### Task 15: 最终验收和文档
|
||||
|
||||
**Files:**
|
||||
- Update: `docs/superpowers/specs/2026-03-30-layout-redesign-design.md`
|
||||
|
||||
- [ ] **Step 1: 对照设计文档验收**
|
||||
|
||||
检查以下验收标准:
|
||||
- [ ] 布局结构符合设计要求(左右分栏)
|
||||
- [ ] 侧边栏可正常折叠和展开
|
||||
- [ ] 白色主题在所有页面正确显示
|
||||
- [ ] 菜单权限系统正常工作
|
||||
- [ ] 所有页面路由和功能无异常
|
||||
- [ ] 响应式布局在不同设备正常
|
||||
- [ ] 交互动画流畅自然
|
||||
- [ ] 通过所有测试要点
|
||||
|
||||
- [ ] **Step 2: 更新设计文档状态**
|
||||
|
||||
在设计文档末尾添加:
|
||||
|
||||
```markdown
|
||||
## 实施状态
|
||||
|
||||
- [x] 设计完成
|
||||
- [x] 实施计划完成
|
||||
- [x] 实施完成
|
||||
- [x] 测试通过
|
||||
- [x] 验收通过
|
||||
|
||||
**完成日期**: 2026-03-30
|
||||
**提交记录**: [commit hash]
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 创建实施总结**
|
||||
|
||||
创建 `docs/superpowers/summaries/2026-03-30-layout-redesign-summary.md`:
|
||||
|
||||
```markdown
|
||||
# Layout结构优化实施总结
|
||||
|
||||
**日期**: 2026-03-30
|
||||
**状态**: 已完成
|
||||
|
||||
## 实施内容
|
||||
|
||||
1. 将layout从mix模式优化为side模式
|
||||
2. 创建CustomLogo组件,支持折叠时智能显示
|
||||
3. 创建CustomHeader组件,保持用户信息功能
|
||||
4. 更新主题为白色系,提升视觉体验
|
||||
5. 添加全局样式和响应式支持
|
||||
|
||||
## 修改文件清单
|
||||
|
||||
- 新建: `src/common/components/layout/CustomHeader.tsx`
|
||||
- 新建: `src/common/components/layout/CustomLogo.tsx`
|
||||
- 新建: `src/common/components/layout/index.ts`
|
||||
- 修改: `src/common/libs/umi/layoutConfig.tsx`
|
||||
- 修改: `src/global.less`
|
||||
|
||||
## 测试结果
|
||||
|
||||
- [x] 侧边栏折叠/展开功能正常
|
||||
- [x] 菜单导航和权限系统正常
|
||||
- [x] Header用户信息功能正常
|
||||
- [x] 响应式布局正常
|
||||
- [x] 浏览器兼容性良好
|
||||
- [x] 性能满足要求
|
||||
|
||||
## 备注
|
||||
|
||||
所有功能按设计文档实现,测试通过,可以上线使用。
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 最终提交**
|
||||
|
||||
```bash
|
||||
git add docs/superpowers/specs/2026-03-30-layout-redesign-design.md
|
||||
git add docs/superpowers/summaries/2026-03-30-layout-redesign-summary.md
|
||||
git commit -m "docs: update design document and add implementation summary"
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 合并到主分支(如果需要)**
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git merge develop
|
||||
git push origin main
|
||||
```
|
||||
|
||||
或者根据项目流程创建PR。
|
||||
|
||||
---
|
||||
|
||||
## 计划自审查
|
||||
|
||||
**Spec覆盖检查:**
|
||||
- ✅ 布局从mix改为side - Task 2
|
||||
- ✅ 白色主题 - Task 3
|
||||
- ✅ Logo组件(折叠智能显示) - Task 4
|
||||
- ✅ Header组件(用户信息) - Task 5
|
||||
- ✅ 侧边栏折叠功能 - Task 9
|
||||
- ✅ 菜单权限系统 - Task 10
|
||||
- ✅ 响应式设计 - Task 12
|
||||
- ✅ 样式和动画 - Task 8
|
||||
- ✅ 测试验收 - Task 15
|
||||
|
||||
**占位符扫描:**
|
||||
- ✅ 无TBD、TODO
|
||||
- ✅ 所有步骤包含具体代码/命令
|
||||
- ✅ 文件路径完整明确
|
||||
- ✅ 无"类似Task X"引用
|
||||
|
||||
**类型一致性检查:**
|
||||
- ✅ CustomLogo组件在Task 4定义,Task 7导入使用
|
||||
- ✅ CustomHeader组件在Task 5定义,Task 7导入使用
|
||||
- ✅ layoutConfig配置在Task 2、3、7逐步完善,配置一致
|
||||
|
||||
**完整性检查:**
|
||||
- ✅ 每个任务都是独立的可执行单元
|
||||
- ✅ 每个步骤都有明确的验证标准
|
||||
- ✅ 包含备份、开发、测试、提交的完整流程
|
||||
- ✅ 涵盖设计文档的所有需求
|
||||
465
docs/superpowers/specs/2026-03-30-layout-redesign-design.md
Normal file
465
docs/superpowers/specs/2026-03-30-layout-redesign-design.md
Normal file
@ -0,0 +1,465 @@
|
||||
# Layout结构优化设计文档
|
||||
|
||||
**日期**: 2026-03-30
|
||||
**项目**: 物业缴费管理系统后台
|
||||
**目标**: 优化现有layout布局,改为左右结构,白色主题风格
|
||||
|
||||
## 1. 项目背景
|
||||
|
||||
当前系统使用UmiJS + Ant Design Pro Layout,采用`mix`混合布局模式。需要优化为更现代、清晰的左右分栏布局,以白色为主色调,提升用户体验。
|
||||
|
||||
## 2. 需求概述
|
||||
|
||||
### 2.1 布局结构要求
|
||||
- **左侧区域**(垂直分割):
|
||||
- 上方:Logo区域(Logo + 项目名称)
|
||||
- 下方:导航菜单
|
||||
|
||||
- **右侧区域**(垂直分割):
|
||||
- 上方:Header(用户信息、操作按钮)
|
||||
- 下方:页面内容区域
|
||||
|
||||
### 2.2 功能要求
|
||||
- 侧边栏固定宽度220px,支持折叠收起(折叠至64px)
|
||||
- Logo区域智能显示:展开时显示Logo+文字,折叠时仅显示Logo
|
||||
- 白色为主色调,整体风格干净整洁
|
||||
- 保持现有菜单权限系统不变
|
||||
- 响应式设计支持
|
||||
|
||||
## 3. 设计方案
|
||||
|
||||
### 3.1 整体架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Layout (side mode) │
|
||||
│ ┌──────────┬─────────────────────────────────────┐ │
|
||||
│ │ │ Header (64px, white background) │ │
|
||||
│ │ Left │ ┌───────────────────────────────┐ │ │
|
||||
│ │ Sidebar │ │ 用户信息区 + 操作按钮 │ │ │
|
||||
│ │ │ └───────────────────────────────┘ │ │
|
||||
│ │ ┌──────┐│ ┌───────────────────────────────┐ │ │
|
||||
│ │ │ Logo ││ │ │ │ │
|
||||
│ │ │+Text ││ │ 页面内容区域 │ │ │
|
||||
│ │ │64px ││ │ (children渲染) │ │ │
|
||||
│ │ ├──────┤│ │ padding: 24px │ │ │
|
||||
│ │ │ Menu ││ │ │ │ │
|
||||
│ │ │ ││ │ │ │ │
|
||||
│ │ └──────┘│ └───────────────────────────────┘ │ │
|
||||
│ └──────────┴─────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 侧边栏设计(左侧)
|
||||
|
||||
#### Logo区域
|
||||
- **高度**: 64px
|
||||
- **背景色**: `#ffffff`
|
||||
- **边框**: 底部 1px solid `#f0f0f0`
|
||||
- **展开状态**: Logo图标(32px) + 项目名称文字
|
||||
- **折叠状态**: 仅显示Logo图标,居中显示
|
||||
- **动画**: 平滑过渡 200ms
|
||||
|
||||
#### 菜单区域
|
||||
- **背景色**: `#ffffff`
|
||||
- **菜单项高度**: 40px
|
||||
- **菜单文字颜色**:
|
||||
- 默认: `#595959`
|
||||
- 悬停: `#1890ff`
|
||||
- 激活: `#1890ff` (背景 `#e6f7ff`)
|
||||
- **图标颜色**: 跟随文字颜色
|
||||
- **层级缩进**: 每级 20px
|
||||
- **激活指示器**: 左侧蓝色竖线 3px宽
|
||||
|
||||
#### 配置参数
|
||||
```typescript
|
||||
siderWidth: 220,
|
||||
collapsedWidth: 64,
|
||||
breakpoint: 'lg',
|
||||
collapsible: true
|
||||
```
|
||||
|
||||
### 3.3 Header设计(右侧上方)
|
||||
|
||||
#### 布局规格
|
||||
- **高度**: 64px
|
||||
- **背景色**: `#ffffff`
|
||||
- **边框**: 底部 1px solid `#f0f0f0`
|
||||
- **内边距**: 0 24px
|
||||
- **布局**: flex左右分布
|
||||
|
||||
#### 内容区域
|
||||
|
||||
**左侧区域**(可选):
|
||||
- 面包屑导航或页面标题
|
||||
|
||||
**右侧区域**:
|
||||
- 用户头像(32px圆形)
|
||||
- 用户名(悬停显示下拉菜单)
|
||||
- 下拉菜单项:
|
||||
- 个人资料
|
||||
- 修改密码
|
||||
- 退出登录
|
||||
|
||||
### 3.4 内容区域设计(右侧下方)
|
||||
|
||||
#### 容器规格
|
||||
- **背景色**: `#f5f5f5`
|
||||
- **内边距**: 24px
|
||||
- **最小高度**: `calc(100vh - 64px)`
|
||||
|
||||
#### 页面内容(可选包裹)
|
||||
如果需要卡片包裹:
|
||||
- **背景色**: `#ffffff`
|
||||
- **圆角**: 8px
|
||||
- **阴影**: `0 1px 2px 0 rgba(0, 0, 0, 0.03)`
|
||||
- **内边距**: 24px
|
||||
|
||||
### 3.5 颜色主题规范
|
||||
|
||||
#### 主色调
|
||||
- 主色: `#1890ff`
|
||||
- 成功: `#52c41a`
|
||||
- 警告: `#faad14`
|
||||
- 错误: `#ff4d4f`
|
||||
- 信息: `#1890ff`
|
||||
|
||||
#### 中性色
|
||||
- 标题文字: `#262626`
|
||||
- 正文文字: `#595959`
|
||||
- 次要文字: `#8c8c8c`
|
||||
- 禁用文字: `#bfbfbf`
|
||||
- 边框: `#d9d9d9`
|
||||
- 分割线: `#f0f0f0`
|
||||
|
||||
#### 背景色
|
||||
- 布局背景: `#f5f5f5`
|
||||
- 侧边栏: `#ffffff`
|
||||
- Header: `#ffffff`
|
||||
- 内容卡片: `#ffffff`
|
||||
|
||||
### 3.6 字体规范
|
||||
- **字号**: 14px(基准)
|
||||
- **行高**: 1.5715
|
||||
- **字体族**: `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial`
|
||||
|
||||
### 3.7 交互和动画
|
||||
|
||||
#### 侧边栏折叠
|
||||
- **动画时长**: 200ms
|
||||
- **动画曲线**: `cubic-bezier(0.645, 0.045, 0.355, 1.000)`
|
||||
- **折叠时**:
|
||||
- Logo文字淡出
|
||||
- 菜单文字隐藏,图标居中
|
||||
- Tooltip显示完整菜单项文字
|
||||
- **展开时**:
|
||||
- Logo文字淡入
|
||||
- 菜单文字从左侧滑入
|
||||
|
||||
#### 菜单交互
|
||||
- 悬停效果:背景色变化 0.2s
|
||||
- 子菜单展开:平滑动画
|
||||
- 激活状态:左侧蓝色竖线指示器
|
||||
|
||||
#### Header交互
|
||||
- 下拉菜单:淡入动画
|
||||
- 按钮悬停:背景色变化
|
||||
|
||||
## 4. 技术实现
|
||||
|
||||
### 4.1 核心配置修改
|
||||
|
||||
修改 `src/common/libs/umi/layoutConfig.tsx`:
|
||||
|
||||
```typescript
|
||||
export const LayoutConfig: RuntimeConfig['layout'] = () => {
|
||||
return {
|
||||
// 从 'mix' 改为 'side'
|
||||
layout: 'side',
|
||||
|
||||
title: '物业管理系统',
|
||||
logo: false, // 禁用默认logo,使用自定义
|
||||
|
||||
// 侧边栏配置
|
||||
siderWidth: 220,
|
||||
collapsedWidth: 64,
|
||||
breakpoint: 'lg',
|
||||
|
||||
// 主题token配置
|
||||
token: {
|
||||
bgLayout: '#f5f5f5',
|
||||
header: {
|
||||
colorBgHeader: '#ffffff',
|
||||
colorHeaderTitle: '#262626',
|
||||
colorTextRightActionsItem: '#595959',
|
||||
heightLayoutHeader: 64,
|
||||
},
|
||||
sider: {
|
||||
colorMenuBackground: '#ffffff',
|
||||
colorMenuText: '#595959',
|
||||
colorMenuTextSelected: '#1890ff',
|
||||
colorMenuItemBgSelected: '#e6f7ff',
|
||||
colorMenuDivider: '#f0f0f0',
|
||||
},
|
||||
},
|
||||
|
||||
// 自定义Header渲染
|
||||
headerRender: (props) => {
|
||||
return <CustomHeader {...props} />;
|
||||
},
|
||||
|
||||
// 自定义Logo渲染
|
||||
logoRenderer: (collapsed) => {
|
||||
return <CustomLogo collapsed={collapsed} />;
|
||||
},
|
||||
|
||||
// 菜单配置保持不变
|
||||
menu: {
|
||||
params: snap.session.permissions,
|
||||
request: async () => {
|
||||
// 现有菜单权限逻辑
|
||||
},
|
||||
},
|
||||
|
||||
// 其他配置保持不变
|
||||
avatarProps: {
|
||||
render: () => <AvatarProps user={snap.session.user} />,
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### 4.2 新增组件
|
||||
|
||||
#### CustomHeader.tsx
|
||||
```typescript
|
||||
import { Layout, Dropdown, Avatar } from 'antd';
|
||||
import type { MenuProps } from 'antd';
|
||||
|
||||
const { Header } = Layout;
|
||||
|
||||
export const CustomHeader = (props: any) => {
|
||||
const menuItems: MenuProps['items'] = [
|
||||
{ key: 'profile', label: '个人资料' },
|
||||
{ key: 'password', label: '修改密码' },
|
||||
{ type: 'divider' },
|
||||
{ key: 'logout', label: '退出登录' },
|
||||
];
|
||||
|
||||
return (
|
||||
<Header className="custom-header">
|
||||
{/* 左侧:面包屑或页面标题 */}
|
||||
<div className="header-left">
|
||||
{/* 可选:面包屑导航 */}
|
||||
</div>
|
||||
|
||||
{/* 右侧:用户信息 */}
|
||||
<div className="header-right">
|
||||
<Dropdown menu={{ items: menuItems }} placement="bottomRight">
|
||||
<AvatarProps user={user} />
|
||||
</Dropdown>
|
||||
</div>
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
#### CustomLogo.tsx
|
||||
```typescript
|
||||
import { MyIcons } from '@/common';
|
||||
|
||||
export const CustomLogo = ({ collapsed }: { collapsed: boolean }) => {
|
||||
return (
|
||||
<div className="custom-logo">
|
||||
<div className="logo-icon">
|
||||
{/* Logo图标或图片 */}
|
||||
<MyIcons.HomeOutlined />
|
||||
</div>
|
||||
{!collapsed && (
|
||||
<div className="logo-text">
|
||||
物业管理系统
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 4.3 样式文件
|
||||
|
||||
在 `src/global.less` 或组件内部添加:
|
||||
|
||||
```less
|
||||
.custom-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 24px;
|
||||
background: #ffffff;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.header-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 64px;
|
||||
padding: 0 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: all 0.2s;
|
||||
|
||||
.logo-icon {
|
||||
font-size: 32px;
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
margin-left: 12px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
white-space: nowrap;
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 实施计划
|
||||
|
||||
### 5.1 实施步骤
|
||||
1. 备份当前 `layoutConfig.tsx` 文件
|
||||
2. 修改 layout 模式:`'mix'` → `'side'`
|
||||
3. 更新 token 配置为白色主题
|
||||
4. 创建 `CustomHeader.tsx` 组件
|
||||
5. 创建 `CustomLogo.tsx` 组件
|
||||
6. 在 `layoutConfig.tsx` 中集成自定义组件
|
||||
7. 测试侧边栏折叠/展开功能
|
||||
8. 测试菜单导航和权限控制
|
||||
9. 测试响应式布局
|
||||
10. 调整细节样式和动画
|
||||
|
||||
### 5.2 文件修改清单
|
||||
|
||||
**需要修改**:
|
||||
- `src/common/libs/umi/layoutConfig.tsx` - 主要配置修改
|
||||
|
||||
**需要新建**:
|
||||
- `src/common/components/layout/CustomHeader.tsx`
|
||||
- `src/common/components/layout/CustomLogo.tsx`
|
||||
|
||||
**可能修改**:
|
||||
- `src/global.less` - 全局样式调整
|
||||
|
||||
**无需修改**:
|
||||
- 菜单权限逻辑(`loopMenu` 函数)
|
||||
- 用户信息组件(`AvatarProps`)
|
||||
- 页面路由和业务组件
|
||||
- `src/app.tsx` 配置
|
||||
|
||||
### 5.3 兼容性保证
|
||||
|
||||
**保持不变的功能**:
|
||||
- ✅ 菜单权限系统
|
||||
- ✅ 用户登录状态
|
||||
- ✅ 路由跳转逻辑
|
||||
- ✅ 页面内容渲染
|
||||
- ✅ 现有业务组件
|
||||
|
||||
**优化改进的功能**:
|
||||
- 🎨 布局结构和视觉风格
|
||||
- 🎨 主题色和配色方案
|
||||
- 🎨 交互体验和动画效果
|
||||
|
||||
## 6. 测试要点
|
||||
|
||||
### 6.1 功能测试
|
||||
- [ ] 侧边栏折叠/展开正常工作
|
||||
- [ ] 菜单项点击导航正确
|
||||
- [ ] 菜单激活状态正确显示
|
||||
- [ ] 用户信息下拉菜单功能正常
|
||||
- [ ] 所有页面路由可正常访问
|
||||
|
||||
### 6.2 样式测试
|
||||
- [ ] 白色主题在所有页面正常显示
|
||||
- [ ] Logo展开/折叠切换流畅
|
||||
- [ ] 菜单悬停和激活状态样式正确
|
||||
- [ ] Header样式和布局正确
|
||||
- [ ] 内容区域内边距和背景正确
|
||||
|
||||
### 6.3 响应式测试
|
||||
- [ ] 桌面端(>1024px)显示正常
|
||||
- [ ] 平板端(768px-1024px)显示正常
|
||||
- [ ] 移动端(<768px)侧边栏自动收起
|
||||
- [ ] 各断点下布局无错位
|
||||
|
||||
### 6.4 兼容性测试
|
||||
- [ ] 表单页面显示正常
|
||||
- [ ] 表格页面显示正常
|
||||
- [ ] 弹窗和抽屉组件正常
|
||||
- [ ] 现有业务功能无异常
|
||||
|
||||
## 7. 风险和注意事项
|
||||
|
||||
### 7.1 潜在风险
|
||||
- **样式冲突**: Ant Design版本升级可能导致样式变化
|
||||
- **浏览器兼容**: CSS动画在旧浏览器可能不支持
|
||||
- **性能影响**: 过多动画可能影响低端设备性能
|
||||
|
||||
### 7.2 缓解措施
|
||||
- 在主流浏览器中测试(Chrome, Firefox, Safari, Edge)
|
||||
- 使用CSS `@supports`检测动画支持
|
||||
- 优化动画性能,使用 `transform` 和 `opacity`
|
||||
- 提供关闭动画的选项(如果需要)
|
||||
|
||||
### 7.3 回滚方案
|
||||
- 保留原 `layoutConfig.tsx` 备份
|
||||
- 使用git版本控制,可随时回退
|
||||
- 分阶段发布,先在测试环境验证
|
||||
|
||||
## 8. 后续优化建议
|
||||
|
||||
### 8.1 短期优化
|
||||
- 添加暗色模式切换(可选)
|
||||
- 优化移动端触摸体验
|
||||
- 添加快捷键支持
|
||||
|
||||
### 8.2 长期优化
|
||||
- 考虑使用ProLayout替代UmiJS Layout(如需要更多功能)
|
||||
- 添加主题定制功能
|
||||
- 实现布局配置持久化(记住用户的折叠状态等)
|
||||
|
||||
## 9. 验收标准
|
||||
|
||||
项目完成需满足以下标准:
|
||||
|
||||
1. ✅ 布局结构符合设计要求(左右分栏)
|
||||
2. ✅ 侧边栏可正常折叠和展开
|
||||
3. ✅ 白色主题在所有页面正确显示
|
||||
4. ✅ 菜单权限系统正常工作
|
||||
5. ✅ 所有页面路由和功能无异常
|
||||
6. ✅ 响应式布局在不同设备正常
|
||||
7. ✅ 交互动画流畅自然
|
||||
8. ✅ 通过所有测试要点
|
||||
|
||||
## 10. 附录
|
||||
|
||||
### 10.1 参考文档
|
||||
- [UmiJS Layout配置](https://umijs.org/docs/max/layout-menu)
|
||||
- [Ant Design Layout组件](https://ant.design/components/layout-cn)
|
||||
- [Ant Design 主题定制](https://ant.design/docs/react/customize-theme-cn)
|
||||
|
||||
### 10.2 设计原则
|
||||
- **简洁**: 去除不必要的装饰和元素
|
||||
- **一致**: 保持整体风格统一
|
||||
- **高效**: 快速加载和响应
|
||||
- **易用**: 直观的交互和导航
|
||||
71
src/common/components/layout/CustomHeader.tsx
Normal file
71
src/common/components/layout/CustomHeader.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import { Layout, Dropdown } from 'antd';
|
||||
import type { MenuProps } from 'antd';
|
||||
import AvatarProps from './AvatarProps';
|
||||
import { useMyState } from '@/common';
|
||||
|
||||
const { Header } = Layout;
|
||||
|
||||
interface CustomHeaderProps {
|
||||
collapsed?: boolean;
|
||||
toggle?: () => void;
|
||||
}
|
||||
|
||||
export const CustomHeader: React.FC<CustomHeaderProps> = ({ collapsed, toggle }) => {
|
||||
const { snap } = useMyState();
|
||||
|
||||
// 在登录页面不显示header
|
||||
if (typeof window !== 'undefined' && window.location.pathname === '/login') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const menuItems: MenuProps['items'] = [
|
||||
{
|
||||
key: 'profile',
|
||||
label: '个人资料',
|
||||
icon: <span>👤</span>,
|
||||
},
|
||||
{
|
||||
key: 'password',
|
||||
label: '修改密码',
|
||||
icon: <span>🔒</span>,
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
key: 'logout',
|
||||
label: '退出登录',
|
||||
icon: <span>🚪</span>,
|
||||
danger: true,
|
||||
onClick: () => {
|
||||
// 添加退出登录逻辑
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Header className="custom-header">
|
||||
{/* 左侧区域 */}
|
||||
<div className="header-left">
|
||||
{/* 可以添加面包屑导航或其他内容 */}
|
||||
</div>
|
||||
|
||||
{/* 右侧用户信息区域 */}
|
||||
<div className="header-right">
|
||||
<Dropdown menu={{ items: menuItems }} placement="bottomRight">
|
||||
<div style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }}>
|
||||
<AvatarProps user={snap.session.user} />
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
|
||||
// Styles will be defined in global.less:
|
||||
// .custom-header { display: flex; justify-content: space-between; ... }
|
||||
// .custom-header .header-left { flex: 1; }
|
||||
// .custom-header .header-right { display: flex; gap: 16px; }
|
||||
27
src/common/components/layout/CustomLogo.tsx
Normal file
27
src/common/components/layout/CustomLogo.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { MyIcons } from '@/common';
|
||||
|
||||
interface CustomLogoProps {
|
||||
collapsed: boolean;
|
||||
}
|
||||
|
||||
export const CustomLogo: React.FC<CustomLogoProps> = ({ collapsed }) => {
|
||||
return (
|
||||
<div className="custom-logo">
|
||||
<div className="logo-icon">
|
||||
<MyIcons.HomeOutlined style={{ fontSize: '32px', color: '#1890ff' }} />
|
||||
</div>
|
||||
{!collapsed && (
|
||||
<div className="logo-text">
|
||||
物业管理系统
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Styles will be defined in global.less:
|
||||
// .custom-logo { display: flex; align-items: center; height: 64px; ... }
|
||||
// .custom-logo .logo-icon { font-size: 32px; min-width: 32px; ... }
|
||||
// .custom-logo .logo-text { margin-left: 12px; font-size: 16px; ... }
|
||||
// .ant-layout-sider-collapsed .custom-logo .logo-text { display: none; }
|
||||
@ -1,3 +1,4 @@
|
||||
import { useMyState } from '@/common';
|
||||
import { CloseOutlined, ReloadOutlined } from '@ant-design/icons';
|
||||
import { PageContainer, PageContainerProps } from '@ant-design/pro-components';
|
||||
import { history, useLocation } from '@umijs/max';
|
||||
@ -198,6 +199,7 @@ export function MyPageContainer({
|
||||
...rest
|
||||
}: MyPageContainerProps) {
|
||||
const location = useLocation();
|
||||
const { snap } = useMyState();
|
||||
const [tabs, setTabs] = useState<TabItem[]>([]);
|
||||
const [activeKey, setActiveKey] = useState<string>('');
|
||||
const contextMenuRef = useRef<{
|
||||
@ -206,6 +208,33 @@ export function MyPageContainer({
|
||||
targetKey: string;
|
||||
} | null>(null);
|
||||
|
||||
// 用户下拉菜单配置
|
||||
const userMenuItems: MenuProps['items'] = [
|
||||
{
|
||||
key: 'profile',
|
||||
label: '个人资料',
|
||||
icon: <span>👤</span>,
|
||||
},
|
||||
{
|
||||
key: 'password',
|
||||
label: '修改密码',
|
||||
icon: <span>🔒</span>,
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
key: 'logout',
|
||||
label: '退出登录',
|
||||
icon: <span>🚪</span>,
|
||||
danger: true,
|
||||
onClick: () => {
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// 订阅标签页状态变化
|
||||
useEffect(() => {
|
||||
const unsubscribe = tabsManager.subscribe(() => {
|
||||
@ -332,32 +361,42 @@ export function MyPageContainer({
|
||||
);
|
||||
};
|
||||
|
||||
if (!enableTabs || tabs.length === 0) {
|
||||
return (
|
||||
<PageContainer
|
||||
fixedHeader
|
||||
header={{
|
||||
title: title,
|
||||
style: { backgroundColor: '#FFF' },
|
||||
}}
|
||||
token={{
|
||||
paddingBlockPageContainerContent: 0,
|
||||
paddingInlinePageContainerContent: 0,
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
{children}
|
||||
</Space>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
// if (!enableTabs || tabs.length === 0) {
|
||||
// return (
|
||||
// <PageContainer
|
||||
// fixedHeader
|
||||
// // header={{
|
||||
// // title: title,
|
||||
// // style: { backgroundColor: '#FFF' },
|
||||
// // }}
|
||||
// token={{
|
||||
// paddingBlockPageContainerContent: 0,
|
||||
// paddingInlinePageContainerContent: 0,
|
||||
// }}
|
||||
// {...rest}
|
||||
// >
|
||||
// <Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
// {children}
|
||||
// </Space>
|
||||
// </PageContainer>
|
||||
// );
|
||||
// }
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
fixedHeader
|
||||
header={{
|
||||
title: (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{/* 左侧:Tabs标签页 */}
|
||||
<div style={{ flex: 1 }}>
|
||||
<Tabs
|
||||
type="editable-card"
|
||||
activeKey={activeKey}
|
||||
@ -365,7 +404,6 @@ export function MyPageContainer({
|
||||
onEdit={handleTabEdit}
|
||||
hideAdd
|
||||
size="middle"
|
||||
// 标签size
|
||||
renderTabBar={renderTabBar}
|
||||
items={tabs.map((tab) => ({
|
||||
key: tab.key,
|
||||
@ -376,8 +414,9 @@ export function MyPageContainer({
|
||||
marginTop: 6,
|
||||
marginBottom: 0,
|
||||
}}
|
||||
className="tabs-header-only"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
style: { backgroundColor: '#FFF' },
|
||||
}}
|
||||
|
||||
3
src/common/components/layout/index.ts
Normal file
3
src/common/components/layout/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { CustomLogo } from './CustomLogo';
|
||||
export { CustomHeader } from './CustomHeader';
|
||||
export { default as AvatarProps } from './AvatarProps';
|
||||
@ -1,7 +1,8 @@
|
||||
// import Logo from '@/assets/bitcoin.webp';
|
||||
import { MyIcons, MyIconsType, PermissionsType, useMyState } from '@/common';
|
||||
import { AvatarProps, CustomLogo } from '@/common/components/layout';
|
||||
import { Link, RuntimeConfig, history } from '@umijs/max';
|
||||
import AvatarProps from '../../components/layout/AvatarProps';
|
||||
import MyLoGo from './logo.jpeg';
|
||||
|
||||
const loopMenu = (permissions: PermissionsType[] | undefined) => {
|
||||
let tree: PermissionsType[] = [];
|
||||
@ -32,31 +33,93 @@ export const LayoutConfig: RuntimeConfig['layout'] = () => {
|
||||
const { snap } = useMyState();
|
||||
|
||||
return {
|
||||
title: snap.session.campus?.name ?? '总后台',
|
||||
// logo: <img src={Logo} style={{ height: '30px' }} />,
|
||||
logo: <></>,
|
||||
layout: 'mix',
|
||||
title: '物业管理平台',
|
||||
logo: MyLoGo, // 禁用默认logo,使用自定义
|
||||
layout: 'mix', // 从 'mix' 改为 'side'
|
||||
colorPrimary: '#1890ff',
|
||||
siderWidth: 220,
|
||||
prefixCls: 'my-prefix',
|
||||
pure: history.location.pathname === '/login',
|
||||
// 禁用顶部导航栏的菜单
|
||||
// 确保header显示 - 强制始终显示header,禁用响应式隐藏
|
||||
header: true,
|
||||
// 禁用响应式侧边栏,确保header始终显示
|
||||
responsive: false,
|
||||
// siderMenuType: 'inline',
|
||||
// 隐藏面包屑导航
|
||||
breadcrumb: { props: { style: { display: 'none' } } },
|
||||
avatarProps: {
|
||||
render: () => <AvatarProps user={snap.session.user} />,
|
||||
// src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
|
||||
// size: 'small',
|
||||
// title: snap?.session?.user?.username ?? '-',
|
||||
render: (props, dom) => {
|
||||
return <AvatarProps user={snap.session.user} />;
|
||||
// return (
|
||||
// <Dropdown
|
||||
// menu={{
|
||||
// items: [
|
||||
// {
|
||||
// key: 'logout',
|
||||
// icon: <LogoutOutlined />,
|
||||
// label: '退出登录',
|
||||
// },
|
||||
// ],
|
||||
// }}
|
||||
// >
|
||||
// {dom}
|
||||
// </Dropdown>
|
||||
// );
|
||||
},
|
||||
},
|
||||
// 自定义Header渲染 - 右侧显示用户信息
|
||||
// headerRender: (props, defaultDom) => {
|
||||
// return (
|
||||
// <div
|
||||
// style={{
|
||||
// display: 'flex',
|
||||
// justifyContent: 'space-between',
|
||||
// alignItems: 'center',
|
||||
// width: '100%',
|
||||
// paddingRight: 24,
|
||||
// paddingLeft: 24,
|
||||
// height: '100%',
|
||||
// }}
|
||||
// >
|
||||
// {/* 左侧可以为空 */}
|
||||
// <div style={{ flex: 1 }}></div>
|
||||
// {/* 右侧添加用户信息 */}
|
||||
// <div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
// <AvatarProps user={snap.session.user} />
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// },
|
||||
|
||||
// 新增:自定义Logo渲染
|
||||
logoRenderer: (collapsed: boolean) => {
|
||||
return <CustomLogo collapsed={collapsed} />;
|
||||
},
|
||||
//水印设置
|
||||
// waterMarkProps: {
|
||||
// content: snap.session.user?.username,
|
||||
// },
|
||||
collapsedButtonRender: false,
|
||||
// 禁用底部的版权信息等
|
||||
footerRender: false,
|
||||
token: {
|
||||
bgLayout: '#eef0f3',
|
||||
bgLayout: '#f5f5f5', // 浅灰布局背景
|
||||
header: {
|
||||
colorBgHeader: '#001529',
|
||||
colorHeaderTitle: '#FFF',
|
||||
colorTextRightActionsItem: '#FFF',
|
||||
heightLayoutHeader: 50,
|
||||
colorBgHeader: '#ffffff', // 白色header背景
|
||||
colorHeaderTitle: '#262626', // 深色标题文字
|
||||
colorTextRightActionsItem: '#595959', // 深色操作文字
|
||||
heightLayoutHeader: 64, // Header高度64px
|
||||
},
|
||||
sider: {
|
||||
colorMenuBackground: '#FFF',
|
||||
colorMenuBackground: '#ffffff', // 白色菜单背景
|
||||
colorMenuText: '#595959', // 深色菜单文字
|
||||
colorMenuTextSelected: '#1890ff', // 激活菜单文字蓝色
|
||||
colorMenuItemBgSelected: '#e6f7ff', // 激活菜单背景浅蓝
|
||||
colorMenuDivider: '#f0f0f0', // 分割线颜色
|
||||
},
|
||||
},
|
||||
menuItemRender: (item, dom) => <Link to={item.path || '/'}>{dom}</Link>,
|
||||
|
||||
79
src/common/libs/umi/layoutConfig.tsx.backup
Normal file
79
src/common/libs/umi/layoutConfig.tsx.backup
Normal file
@ -0,0 +1,79 @@
|
||||
// import Logo from '@/assets/bitcoin.webp';
|
||||
import { MyIcons, MyIconsType, PermissionsType, useMyState } from '@/common';
|
||||
import { Link, RuntimeConfig, history } from '@umijs/max';
|
||||
import AvatarProps from '../../components/layout/AvatarProps';
|
||||
|
||||
const loopMenu = (permissions: PermissionsType[] | undefined) => {
|
||||
let tree: PermissionsType[] = [];
|
||||
let map: Record<number, PermissionsType> = {};
|
||||
permissions?.forEach((permission) => {
|
||||
map[permission.id] = {
|
||||
path: permission.type === 'Button' ? 'null' : permission.path,
|
||||
name: permission.name,
|
||||
icon: permission.icon && MyIcons[permission.icon as MyIconsType],
|
||||
children: [],
|
||||
hideInMenu: permission.type === 'Button',
|
||||
};
|
||||
});
|
||||
|
||||
permissions?.forEach((permission) => {
|
||||
let node = map[permission.id];
|
||||
if (permission.parent_id !== null) {
|
||||
map[permission.parent_id].children.push(node);
|
||||
} else {
|
||||
tree.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
return tree?.[0]?.children;
|
||||
};
|
||||
|
||||
export const LayoutConfig: RuntimeConfig['layout'] = () => {
|
||||
const { snap } = useMyState();
|
||||
|
||||
return {
|
||||
title: snap.session.campus?.name ?? '总后台',
|
||||
// logo: <img src={Logo} style={{ height: '30px' }} />,
|
||||
logo: <></>,
|
||||
layout: 'mix',
|
||||
colorPrimary: '#1890ff',
|
||||
siderWidth: 220,
|
||||
pure: history.location.pathname === '/login',
|
||||
avatarProps: {
|
||||
render: () => <AvatarProps user={snap.session.user} />,
|
||||
},
|
||||
//水印设置
|
||||
// waterMarkProps: {
|
||||
// content: snap.session.user?.username,
|
||||
// },
|
||||
collapsedButtonRender: false,
|
||||
token: {
|
||||
bgLayout: '#eef0f3',
|
||||
header: {
|
||||
colorBgHeader: '#001529',
|
||||
colorHeaderTitle: '#FFF',
|
||||
colorTextRightActionsItem: '#FFF',
|
||||
heightLayoutHeader: 50,
|
||||
},
|
||||
sider: {
|
||||
colorMenuBackground: '#FFF',
|
||||
},
|
||||
},
|
||||
menuItemRender: (item, dom) => <Link to={item.path || '/'}>{dom}</Link>,
|
||||
menu: {
|
||||
params: snap.session.permissions,
|
||||
request: async () => {
|
||||
let objjs: any = [];
|
||||
snap.session.permissions?.forEach((res: any) => {
|
||||
objjs.push(res);
|
||||
});
|
||||
let data = objjs.sort((a: any, b: any) => {
|
||||
return a._lft - b._lft;
|
||||
});
|
||||
const menus = loopMenu(data);
|
||||
return Promise.resolve(menus);
|
||||
},
|
||||
},
|
||||
unAccessible: <div>unAccessible</div>,
|
||||
};
|
||||
};
|
||||
BIN
src/common/libs/umi/logo.jpeg
Normal file
BIN
src/common/libs/umi/logo.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
@ -12,6 +12,7 @@ import {
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { stateActions } from '..';
|
||||
import loginBgImg from './loginBgImg.jpg';
|
||||
|
||||
export function MyLoginPage1() {
|
||||
const navigate = useNavigate();
|
||||
@ -37,8 +38,9 @@ export function MyLoginPage1() {
|
||||
>
|
||||
<LoginFormPage<ApiTypes.Common.Auth.Login>
|
||||
title="欢迎使用后台管理系统"
|
||||
backgroundVideoUrl="https://gw.alipayobjects.com/v/huamei_gcee1x/afts/video/jXRBRK_VAwoAAAAAAAAAAAAAK4eUAQBr"
|
||||
subTitle="Admin management system"
|
||||
// backgroundVideoUrl="https://gw.alipayobjects.com/v/huamei_gcee1x/afts/video/jXRBRK_VAwoAAAAAAAAAAAAAK4eUAQBr"
|
||||
backgroundImageUrl={loginBgImg}
|
||||
subTitle="物业管理平台"
|
||||
onFinish={async (values: any) => {
|
||||
Apis.Common.Auth.Login({
|
||||
...values,
|
||||
|
||||
BIN
src/common/pages/loginBgImg.jpg
Normal file
BIN
src/common/pages/loginBgImg.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
274
src/global.less
Normal file
274
src/global.less
Normal file
@ -0,0 +1,274 @@
|
||||
@import '~antd/es/style/reset.css';
|
||||
|
||||
// 全局样式
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.5715;
|
||||
color: #262626;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
// // 自定义Logo样式
|
||||
// .custom-logo {
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// height: 64px;
|
||||
// padding: 0 16px;
|
||||
// border-bottom: 1px solid #f0f0f0;
|
||||
// transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
// background: #ffffff;
|
||||
// }
|
||||
|
||||
// .custom-logo .logo-icon {
|
||||
// font-size: 32px;
|
||||
// min-width: 32px;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// justify-content: center;
|
||||
// }
|
||||
|
||||
// .custom-logo .logo-text {
|
||||
// margin-left: 12px;
|
||||
// font-size: 16px;
|
||||
// font-weight: 600;
|
||||
// color: #262626;
|
||||
// white-space: nowrap;
|
||||
// opacity: 1;
|
||||
// transition: opacity 0.2s;
|
||||
// }
|
||||
|
||||
// // 折叠状态
|
||||
// .ant-layout-sider-collapsed .custom-logo {
|
||||
// padding: 0;
|
||||
// justify-content: center;
|
||||
// }
|
||||
|
||||
// .ant-layout-sider-collapsed .custom-logo .logo-text {
|
||||
// display: none;
|
||||
// }
|
||||
|
||||
// // PageContainer Header样式 - 固定悬浮在UmiJS Header下方
|
||||
// div[class*='page-container-header'],
|
||||
// .ant-pro-page-container > div[class*='page-container-header'],
|
||||
// .ant-pro-page-container-header {
|
||||
// position: fixed !important;
|
||||
// top: 64px !important; /* UmiJS Header的高度 */
|
||||
// right: 0 !important;
|
||||
// left: 220px !important; /* 侧边栏宽度 */
|
||||
// z-index: 999 !important;
|
||||
// margin: 0 !important;
|
||||
// padding: 8px 24px !important; /* 增加padding */
|
||||
// background: #ffffff !important;
|
||||
// border-bottom: 1px solid #f0f0f0 !important;
|
||||
// box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05) !important;
|
||||
// height: auto !important;
|
||||
// min-height: 40px !important;
|
||||
// max-height: 52px !important;
|
||||
// display: flex !important;
|
||||
// align-items: center !important;
|
||||
// }
|
||||
|
||||
// // 当侧边栏折叠时,调整tabs header的left值
|
||||
// .ant-layout-sider-collapsed ~ .ant-layout .ant-pro-page-container-header {
|
||||
// left: 64px !important;
|
||||
// }
|
||||
|
||||
// // 强制PageContainer header样式
|
||||
// .ant-pro-page-container .ant-pro-page-container-header {
|
||||
// position: fixed !important;
|
||||
// top: 64px !important;
|
||||
// right: 0 !important;
|
||||
// left: 220px !important;
|
||||
// z-index: 999 !important;
|
||||
// margin: 0 !important;
|
||||
// padding: 8px 24px !important;
|
||||
// min-height: 40px !important;
|
||||
// max-height: 52px !important;
|
||||
// display: flex !important;
|
||||
// align-items: center !important;
|
||||
// }
|
||||
|
||||
// // 确保UmiJS Header固定在顶部
|
||||
// .ant-pro-header,
|
||||
// .ant-layout-header {
|
||||
// position: fixed !important;
|
||||
// top: 0 !important;
|
||||
// right: 0 !important;
|
||||
// left: 220px !important;
|
||||
// z-index: 1000 !important;
|
||||
// width: auto !important;
|
||||
// background: #ffffff !important;
|
||||
// border-bottom: 1px solid #f0f0f0 !important;
|
||||
// }
|
||||
|
||||
// .ant-layout-sider-collapsed ~ .ant-layout .ant-pro-header,
|
||||
// .ant-layout-sider-collapsed + .ant-layout .ant-pro-header {
|
||||
// left: 64px !important;
|
||||
// }
|
||||
|
||||
// // Tabs样式优化 - 减小高度
|
||||
// .ant-pro-page-container-header .tabs-header-only,
|
||||
// div[class*='page-container-header'] .tabs-header-only {
|
||||
// margin: 0 !important;
|
||||
// line-height: 1 !important;
|
||||
// height: auto !important;
|
||||
// display: flex !important;
|
||||
// align-items: center !important;
|
||||
// }
|
||||
|
||||
// .ant-pro-page-container-header .ant-tabs,
|
||||
// div[class*='page-container-header'] .ant-tabs {
|
||||
// margin: 0 !important;
|
||||
// height: auto !important;
|
||||
// }
|
||||
|
||||
// .ant-pro-page-container-header .ant-tabs-nav,
|
||||
// div[class*='page-container-header'] .ant-tabs-nav {
|
||||
// margin-bottom: 0 !important;
|
||||
// height: auto !important;
|
||||
// }
|
||||
|
||||
// .ant-pro-page-container-header .ant-tabs-nav-list,
|
||||
// div[class*='page-container-header'] .ant-tabs-nav-list {
|
||||
// margin-bottom: 0 !important;
|
||||
// height: auto !important;
|
||||
// display: flex !important;
|
||||
// align-items: center !important;
|
||||
// }
|
||||
|
||||
// .ant-pro-page-container-header .ant-tabs-tab,
|
||||
// div[class*='page-container-header'] .ant-tabs-tab {
|
||||
// padding: 4px 10px !important;
|
||||
// background: #f5f5f5;
|
||||
// border-radius: 4px;
|
||||
// margin-right: 4px;
|
||||
// transition: all 0.2s;
|
||||
// height: 26px !important;
|
||||
// line-height: 26px !important;
|
||||
// font-size: 13px;
|
||||
// display: inline-flex !important;
|
||||
// align-items: center !important;
|
||||
// }
|
||||
|
||||
// .ant-pro-page-container-header .ant-tabs-tab-active,
|
||||
// div[class*='page-container-header'] .ant-tabs-tab-active {
|
||||
// background: #ffffff;
|
||||
// font-weight: 500;
|
||||
// }
|
||||
|
||||
// .ant-pro-page-container-header .ant-tabs-ink-bar,
|
||||
// div[class*='page-container-header'] .ant-tabs-ink-bar {
|
||||
// display: none !important;
|
||||
// }
|
||||
|
||||
// // 内容区域 - 调整padding以适应固定的header
|
||||
.ant-pro-grid-content {
|
||||
padding: 15px;
|
||||
// padding-top: 20px; /* 64px UmiJS Header + 48px tabs header + 8px */
|
||||
}
|
||||
.ant-page-header {
|
||||
padding: 0 15px !important;
|
||||
}
|
||||
// .ant-pro-page-container {
|
||||
// padding-top: 0 !important;
|
||||
// }
|
||||
|
||||
// .ant-pro-page-container-warp {
|
||||
// padding: 0 !important;
|
||||
// }
|
||||
|
||||
// .ant-pro-page-container-children-content {
|
||||
// padding: 24px !important;
|
||||
// padding-top: 0 !important;
|
||||
// }
|
||||
|
||||
// // 确保主内容区域正确的padding
|
||||
// .custom-header .header-left {
|
||||
// flex: 1;
|
||||
// }
|
||||
|
||||
// .custom-header .header-right {
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// gap: 16px;
|
||||
// }
|
||||
|
||||
// .custom-header .header-right > div {
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// transition: opacity 0.2s;
|
||||
// }
|
||||
|
||||
// .custom-header .header-right > div:hover {
|
||||
// opacity: 0.8;
|
||||
// }
|
||||
|
||||
// // 内容区域 - 添加顶部padding避免被固定header遮挡
|
||||
// .ant-pro-grid-content {
|
||||
// padding: 24px;
|
||||
// padding-top: 24px; /* 恢复正常padding */
|
||||
// }
|
||||
|
||||
// .ant-pro-page-container {
|
||||
// padding-top: 0 !important;
|
||||
// }
|
||||
|
||||
// .ant-pro-page-container-children-content {
|
||||
// padding-top: 0 !important;
|
||||
// padding: 24px !important;
|
||||
// }
|
||||
|
||||
// // 页面容器卡片
|
||||
// .ant-pro-page-container {
|
||||
// background: transparent;
|
||||
// }
|
||||
|
||||
// .ant-pro-page-container .ant-pro-page-container-warp {
|
||||
// background: #ffffff;
|
||||
// border-radius: 8px;
|
||||
// box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03);
|
||||
// padding: 24px;
|
||||
// }
|
||||
|
||||
// // 隐藏侧边栏底部的用户信息区域
|
||||
.ant-pro-sider .ant-pro-sider-actions {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.ant-pro-sider-collapsed .ant-pro-sider-actions {
|
||||
display: none !important;
|
||||
}
|
||||
.my-prefix-sider-actions {
|
||||
// display: none !important;
|
||||
}
|
||||
|
||||
// 确保顶部Header始终显示,不受响应式影响
|
||||
// .ant-pro-header {
|
||||
// display: flex !important;
|
||||
// width: 100% !important;
|
||||
// }
|
||||
|
||||
// 确保在所有屏幕尺寸下header都显示
|
||||
// @media (min-width: 0px) {
|
||||
// .ant-pro-header {
|
||||
// display: flex !important;
|
||||
// }
|
||||
// }
|
||||
|
||||
// .ant-layout-header {
|
||||
// display: flex !important;
|
||||
// }
|
||||
|
||||
// // 确保顶部Header显示
|
||||
// .ant-pro-header {
|
||||
// display: flex !important;
|
||||
// }
|
||||
|
||||
// // 确保PageContainer的affix header显示
|
||||
// .ant-pro-page-container-affix {
|
||||
// display: block !important;
|
||||
// }
|
||||
Loading…
x
Reference in New Issue
Block a user