diff --git a/.clinerules/admin-rule.md b/.clinerules/admin-rule.md
index 7769030..0e6dcd3 100644
--- a/.clinerules/admin-rule.md
+++ b/.clinerules/admin-rule.md
@@ -59,6 +59,18 @@ resources/admin/
│ │ └── index.vue # 主布局
│ ├── pages/ # 页面组件
│ │ ├── auth/ # 认证页面
+│ │ │ ├── users/ # 用户管理
+│ │ │ │ ├── index.vue # 主页面
+│ │ │ │ └── components/ # 页面私有组件
+│ │ │ │ ├── SaveDialog.vue # 新增/编辑弹窗
+│ │ │ │ ├── RoleDialog.vue # 角色设置弹窗
+│ │ │ │ └── DetailDialog.vue # 详情弹窗
+│ │ │ ├── roles/ # 角色管理
+│ │ │ │ ├── index.vue
+│ │ │ │ └── components/
+│ │ │ │ ├── SaveDialog.vue
+│ │ │ │ └── PermissionDialog.vue
+│ │ │ └── ...
│ │ ├── home/ # 首页
│ │ ├── login/ # 登录页
│ │ ├── system/ # 系统管理
@@ -84,13 +96,446 @@ resources/admin/
└── README.md # 项目说明
```
-### 2. 组件开发规范
+### 2. 页面组件开发规范
+
+#### 页面组件目录结构
+
+每个页面模块应遵循以下目录结构:
+
+```
+pages/
+└── {module}/ # 模块目录(如 auth, system 等)
+ └── {resource}/ # 资源目录(如 users, roles 等)
+ ├── index.vue # 主页面(列表页)
+ └── components/ # 页面私有组件目录
+ ├── SaveDialog.vue # 新增/编辑弹窗(字段较少时使用)
+ ├── SaveDrawer.vue # 新增/编辑抽屉(字段较多时使用)
+ ├── DetailDialog.vue # 详情弹窗
+ ├── RoleDialog.vue # 角色设置弹窗
+ ├── SearchDrawer.vue # 高级搜索抽屉
+ └── ... # 其他页面专用组件
+```
+
+**目录结构说明**:
+- `index.vue`: 页面的主入口组件,通常是列表展示页面
+- `components/`: 存放该页面专用的私有组件
+ - 弹窗/抽屉组件:根据表单字段数量选择使用弹窗或抽屉
+ - 字段较少(3-5个):使用 `*Dialog.vue` 弹窗组件
+ - 字段较多(5个以上):使用 `*Drawer.vue` 抽屉组件
+ - `SaveDialog.vue` / `SaveDrawer.vue`: 用于新增和编辑数据的表单组件
+ - `DetailDialog.vue`: 用于查看数据详情的弹窗
+ - `SearchDrawer.vue`: 用于高级搜索条件的抽屉组件
+ - 其他与该页面紧密关联的组件
+
+**组件命名规范**:
+- 页面私有组件使用 PascalCase 命名
+- 建议以功能命名:
+ - 表单类:`SaveDialog.vue`, `SaveDrawer.vue`, `EditDialog.vue` 等
+ - 功能类:`RoleDialog.vue`, `PermissionDialog.vue` 等
+ - 搜索类:`SearchDrawer.vue`
+- 避免 `Dialog.vue` 或 `Drawer.vue` 这样过于通用的命名
+
+#### 页面布局规范
+
+**基本布局要求**:
+
+1. **使用 Flex 布局**: 页面容器必须使用 Flex 布局,确保占满全部内容区域
+2. **表格高度自适应**: 表格使用封装的 `scTable` 组件,高度占满剩余内容空间
+
+**标准页面布局模板**:
+
+```vue
+
+
+
+
+
+```
+
+**布局样式说明**:
+- 页面根元素使用 `display: flex` 和 `flex-direction: column` 实现垂直布局
+- `height: 100%` 确保页面占满父容器高度
+- `padding: 0` 去除内边距(由布局组件控制外边距)
+- `table-content` 使用 `flex: 1` 占据剩余空间
+- `overflow: hidden` 防止出现滚动条(表格内部自己处理滚动)
+
+**侧边栏布局示例** (如用户管理页面):
+
+```vue
+
+
+
+
+
+```
+
+#### 列表搜索区域规范
+
+**搜索区域设计原则**:
+
+1. **精简显示**: 搜索条件不全部显示,只显示最常用的 1-2 项
+2. **优先使用表格筛选**: 对于状态等枚举类型的筛选,优先使用 Ant Design Vue Table 的自定义过滤器功能
+3. **高级搜索抽屉**: 其他复杂搜索条件使用抽屉弹出(需要在页面 components 目录下创建 SearchDrawer.vue 组件)
+4. **保持界面整洁**: 避免搜索区域占用过多空间
+
+**实现方式**:
+
+**方式一: 使用表格自定义筛选(推荐)**
+
+```vue
+
+
+
+
+
+```
+
+**方式二: 使用高级搜索抽屉(复杂搜索条件)**
+
+```vue
+
+
+
+
+
+
+
+
+```
+
+**SearchDrawer.vue 组件示例**:
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 正常
+ 禁用
+
+
+
+
+
+ 取消
+ 确定
+
+
+
+
+
+
+```
+
+**搜索条件选择建议**:
+
+常用搜索条件(显示在工具栏):
+- 主要关键字搜索(如用户名、角色名称)
+- 无需筛选的简单字段
+
+表格筛选(推荐用于):
+- 状态、类型等枚举值字段
+- 不需要组合条件的单一筛选
+
+高级搜索抽屉(用于):
+- 次要字段(如邮箱、手机号)
+- 日期范围筛选
+- 多条件组合筛选
+- 复杂的搜索逻辑
+
+**scTable 组件筛选器优化说明**:
+
+scTable 组件已支持 Ant Design Vue Table 的筛选功能,使用方式:
+
+1. 在 `columns` 配置中添加 `filters` 属性
+2. 通过 `@filterChange` 事件处理筛选变化
+3. 将筛选条件合并到搜索表单中提交
+
+```javascript
+// 在 useTable Hook 中处理筛选变化
+const handleFilterChange = (pagination, filters) => {
+ // 将筛选条件添加到搜索表单
+ if (filters.status) {
+ searchForm.status = filters.status[0] // filterMultiple: false 时取第一个值
+ }
+ handleSearch()
+}
+```
+
+### 3. 组件开发规范
#### 组件命名
- **单文件组件**: 使用 PascalCase 命名,如 `UserList.vue`
- **公共组件**: 以 `sc` 开头,如 `scTable.vue`
-- **页面组件**: 使用语义化命名,如 `UserManagement.vue`
+- **页面私有组件**: 使用语义化命名,如 `SaveDialog.vue`, `RoleDialog.vue`
#### 组件结构
@@ -121,7 +566,7 @@ onMounted(() => {})
```
-### 3. API 接口开发规范
+### 4. API 接口开发规范
#### API 文件组织
@@ -189,7 +634,7 @@ const getMenu = async () => {
}
```
-### 4. 路由开发规范
+### 5. 路由开发规范
#### 路由类型
@@ -243,7 +688,23 @@ export default [
**后端菜单数据格式**:
-菜单权限节点需要遵循以下规范:
+菜单权限节点数据结构如下:
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | number | 权限ID |
+| title | string | 权限标题(用于菜单显示) |
+| name | string | 权限编码(唯一标识) |
+| type | string | 权限类型:menu(菜单)、api(接口)、button(按钮)、url(链接) |
+| parent_id | number | 父级ID,顶级菜单为 0 |
+| path | string | 路由路径(如 `/system/users`) |
+| component | string | 前端组件路径(如 `system/users/index`) |
+| meta | object | 元数据(包含 icon、hidden、keepAlive 等) |
+| sort | number | 排序 |
+| status | number | 状态:1启用 0禁用 |
+| children | array | 子权限列表(树形结构) |
+
+**菜单节点规范**:
1. **顶级菜单**(parent_id=0)不需要 component 值
- 顶级菜单作为分组容器,不需要指定组件路径
@@ -260,47 +721,77 @@ export default [
- 前端会将菜单数据转换为路由,所有页面路由都会挂载到 Layout 布局下
- 不需要在前端维护嵌套的路由结构
+**数据示例**:
+
```javascript
// 示例:顶级菜单(无 component)
{
- name: '系统管理',
- code: 'system',
+ id: 1,
+ title: '系统管理',
+ name: 'system',
type: 'menu',
parent_id: 0,
- route: '/system',
+ path: '/system',
component: null, // 顶级菜单不需要 component
meta: {
icon: 'Setting',
- hidden: false
+ hidden: false,
+ hiddenBreadcrumb: false,
+ keepAlive: false
},
- sort: 1
+ sort: 1,
+ status: 1,
+ children: []
}
// 示例:最后一级菜单(有 component)
{
- name: '用户管理',
- code: 'system.users',
+ id: 2,
+ title: '用户管理',
+ name: 'system.users',
type: 'menu',
parent_id: 0,
- route: '/system/users',
+ path: '/system/users',
component: 'system/users/index', // 最后一级菜单需要 component
meta: {
icon: 'User',
- hidden: false
+ hidden: false,
+ hiddenBreadcrumb: false,
+ keepAlive: true
},
- sort: 1
+ sort: 1,
+ status: 1,
+ children: []
}
// 示例:按钮权限(无 component)
{
- name: '查看用户',
- code: 'system.users.view',
+ id: 3,
+ title: '查看用户',
+ name: 'system.users.view',
type: 'button',
- parent_id: 0,
- route: 'admin.users.index',
+ parent_id: 2,
+ path: null,
component: null, // 非菜单类型不需要 component
meta: null,
- sort: 1
+ sort: 1,
+ status: 1,
+ children: []
+}
+
+// 示例:API 权限
+{
+ id: 4,
+ title: '获取用户列表',
+ name: 'system.users.list',
+ type: 'api',
+ parent_id: 0,
+ path: 'admin.users.index',
+ component: null,
+ meta: null,
+ sort: 1,
+ status: 1,
+ children: []
}
```
@@ -313,12 +804,12 @@ export default [
function transformMenusToRoutes(menus) {
return menus.map(menu => {
const route = {
- path: menu.route,
- name: menu.name || menu.code,
+ path: menu.path,
+ name: menu.name,
meta: {
- title: menu.name,
+ title: menu.title,
icon: menu.meta?.icon,
- hidden: menu.meta?.hidden,
+ hidden: menu.meta?.hidden || false,
keepAlive: menu.meta?.keepAlive || false
}
}
@@ -346,9 +837,12 @@ function loadComponent(componentPath) {
**菜单拉平处理说明**:
由于前端将所有页面菜单拉平挂载在 Layout 下,因此:
-- 后端菜单的 parent_id 主要用于构建菜单树的层级关系(侧边栏显示)
+- 后端菜单的 `parent_id` 主要用于构建菜单树的层级关系(侧边栏显示)
- 路由层面不需要维护嵌套结构,所有页面路由都在同一层级
-- component 值只需要在最后一级菜单(叶子节点)中设置
+- `component` 值只需要在最后一级菜单(叶子节点)中设置
+- 路由 `path` 使用后端返回的 `path` 字段
+- 路由 `name` 使用后端返回的 `name` 字段(权限编码)
+- 路由 `meta.title` 使用后端返回的 `title` 字段(权限标题)
#### 路由守卫
@@ -387,7 +881,7 @@ router.beforeEach(async (to, from, next) => {
})
```
-### 5. 状态管理规范
+### 6. 状态管理规范
#### Pinia Store 定义
@@ -547,7 +1041,7 @@ const userStore = useUserStore()
```
-### 6. 表格开发规范
+### 7. 表格开发规范
#### 使用 useTable Hook
@@ -594,7 +1088,7 @@ const searchParams = ref({
```
-### 7. 表单开发规范
+### 8. 表单开发规范
#### scForm 组件使用
@@ -650,7 +1144,7 @@ const handleSubmit = async () => {
```
-### 8. 图标使用规范
+### 9. 图标使用规范
#### Ant Design Vue Icons
@@ -680,7 +1174,7 @@ const handleSubmit = async () => {
- `info-circle`: 信息
- `warning`: 警告
-### 9. 国际化 (i18n) 规范
+### 10. 国际化 (i18n) 规范
#### 使用 i18n
@@ -711,7 +1205,7 @@ export default {
}
```
-### 10. 文件上传规范
+### 11. 文件上传规范
#### scUpload 组件使用
@@ -732,7 +1226,7 @@ const imageUrl = ref('')
```
-### 11. 富文本编辑器规范
+### 12. 富文本编辑器规范
#### scEditor 组件使用
@@ -757,12 +1251,129 @@ const toolbarConfig = [
```
-### 12. 样式规范
+### 13. 样式规范
#### 全局样式
在 `src/style.css` 中定义全局样式。
+#### 页面公共样式
+
+页面布局的公共样式已提取到全局样式文件中,各页面可直接使用:
+
+**在 `src/assets/style/pages.scss` 中定义的公共类:**
+
+```scss
+// 标准页面布局(垂直布局)
+.pages-base-layout {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ padding: 0;
+
+ .tool-bar {
+ padding: 12px 16px;
+ background: #fff;
+ border-bottom: 1px solid #f0f0f0;
+
+ .left-panel {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ }
+
+ .right-panel {
+ display: flex;
+ gap: 8px;
+ }
+ }
+
+ .table-content {
+ flex: 1;
+ overflow: hidden;
+ padding: 16px;
+ background: #f5f5f5;
+ }
+}
+
+// 侧边栏布局(左右分栏)
+.pages-sidebar-layout {
+ display: flex;
+ flex-direction: row;
+ height: 100%;
+ padding: 0;
+
+ .left-box {
+ width: 260px;
+ border-right: 1px solid #f0f0f0;
+ display: flex;
+ flex-direction: column;
+ background: #fff;
+
+ .header {
+ padding: 12px 16px;
+ border-bottom: 1px solid #f0f0f0;
+ background: #fafafa;
+ }
+
+ .body {
+ flex: 1;
+ overflow-y: auto;
+ padding: 16px;
+ }
+ }
+
+ .right-box {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+
+ .tool-bar {
+ padding: 12px 16px;
+ background: #fff;
+ border-bottom: 1px solid #f0f0f0;
+
+ .left-panel {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ }
+
+ .right-panel {
+ display: flex;
+ gap: 8px;
+ }
+ }
+
+ .table-content {
+ flex: 1;
+ overflow: hidden;
+ padding: 16px;
+ background: #f5f5f5;
+ }
+ }
+}
+```
+
+**使用示例:**
+
+```vue
+
+
+
+
+
+```
+
#### 组件样式
使用 `scoped` 避免样式污染:
@@ -784,7 +1395,7 @@ const toolbarConfig = [
- 使用 BEM 命名法
- 类名使用 kebab-case
-### 13. 工具函数使用
+### 14. 工具函数使用
#### request.js (HTTP 请求)
@@ -816,7 +1427,7 @@ formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss')
deepClone(originalObject)
```
-### 14. 开发流程
+### 15. 开发流程
#### 登录流程
@@ -875,7 +1486,7 @@ const handleLogin = async () => {
3. 添加必要的 Props 和 Emits
4. 编写组件文档
-### 15. 常用命令
+### 16. 常用命令
```bash
# 安装依赖
@@ -897,7 +1508,7 @@ npm run format
npm run lint
```
-### 16. 注意事项
+### 17. 注意事项
1. **禁止重复引入图标**: Ant Design Vue 图标已全局引入,直接使用即可
2. **使用组合式 API**: 新代码统一使用 `