更新后天规则文档
This commit is contained in:
@@ -59,6 +59,18 @@ resources/admin/
|
|||||||
│ │ └── index.vue # 主布局
|
│ │ └── index.vue # 主布局
|
||||||
│ ├── pages/ # 页面组件
|
│ ├── pages/ # 页面组件
|
||||||
│ │ ├── auth/ # 认证页面
|
│ │ ├── auth/ # 认证页面
|
||||||
|
│ │ │ ├── users/ # 用户管理
|
||||||
|
│ │ │ │ ├── index.vue # 主页面
|
||||||
|
│ │ │ │ └── components/ # 页面私有组件
|
||||||
|
│ │ │ │ ├── SaveDialog.vue # 新增/编辑弹窗
|
||||||
|
│ │ │ │ ├── RoleDialog.vue # 角色设置弹窗
|
||||||
|
│ │ │ │ └── DetailDialog.vue # 详情弹窗
|
||||||
|
│ │ │ ├── roles/ # 角色管理
|
||||||
|
│ │ │ │ ├── index.vue
|
||||||
|
│ │ │ │ └── components/
|
||||||
|
│ │ │ │ ├── SaveDialog.vue
|
||||||
|
│ │ │ │ └── PermissionDialog.vue
|
||||||
|
│ │ │ └── ...
|
||||||
│ │ ├── home/ # 首页
|
│ │ ├── home/ # 首页
|
||||||
│ │ ├── login/ # 登录页
|
│ │ ├── login/ # 登录页
|
||||||
│ │ ├── system/ # 系统管理
|
│ │ ├── system/ # 系统管理
|
||||||
@@ -84,13 +96,446 @@ resources/admin/
|
|||||||
└── README.md # 项目说明
|
└── 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
|
||||||
|
<template>
|
||||||
|
<div class="pages {module}-{resource}-page">
|
||||||
|
<!-- 工具栏区域 -->
|
||||||
|
<div class="tool-bar">
|
||||||
|
<div class="left-panel">
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
</div>
|
||||||
|
<div class="right-panel">
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表格内容区域 -->
|
||||||
|
<div class="table-content">
|
||||||
|
<scTable
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="tableData"
|
||||||
|
:loading="loading"
|
||||||
|
:pagination="pagination"
|
||||||
|
:row-key="rowKey"
|
||||||
|
@refresh="refreshTable"
|
||||||
|
@paginationChange="handlePaginationChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.{module}-{resource}-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.tool-bar {
|
||||||
|
// 工具栏样式
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
**布局样式说明**:
|
||||||
|
- 页面根元素使用 `display: flex` 和 `flex-direction: column` 实现垂直布局
|
||||||
|
- `height: 100%` 确保页面占满父容器高度
|
||||||
|
- `padding: 0` 去除内边距(由布局组件控制外边距)
|
||||||
|
- `table-content` 使用 `flex: 1` 占据剩余空间
|
||||||
|
- `overflow: hidden` 防止出现滚动条(表格内部自己处理滚动)
|
||||||
|
|
||||||
|
**侧边栏布局示例** (如用户管理页面):
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div class="pages user-page">
|
||||||
|
<!-- 左侧部门树 -->
|
||||||
|
<div class="left-box">
|
||||||
|
<div class="header">
|
||||||
|
<!-- 搜索框 -->
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
<!-- 树形组件 -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧内容区 -->
|
||||||
|
<div class="right-box">
|
||||||
|
<!-- 工具栏 -->
|
||||||
|
<div class="tool-bar">...</div>
|
||||||
|
|
||||||
|
<!-- 表格 -->
|
||||||
|
<div class="table-content">...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.user-page {
|
||||||
|
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;
|
||||||
|
|
||||||
|
.table-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 列表搜索区域规范
|
||||||
|
|
||||||
|
**搜索区域设计原则**:
|
||||||
|
|
||||||
|
1. **精简显示**: 搜索条件不全部显示,只显示最常用的 1-2 项
|
||||||
|
2. **优先使用表格筛选**: 对于状态等枚举类型的筛选,优先使用 Ant Design Vue Table 的自定义过滤器功能
|
||||||
|
3. **高级搜索抽屉**: 其他复杂搜索条件使用抽屉弹出(需要在页面 components 目录下创建 SearchDrawer.vue 组件)
|
||||||
|
4. **保持界面整洁**: 避免搜索区域占用过多空间
|
||||||
|
|
||||||
|
**实现方式**:
|
||||||
|
|
||||||
|
**方式一: 使用表格自定义筛选(推荐)**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div class="pages-base-layout role-page">
|
||||||
|
<div class="tool-bar">
|
||||||
|
<div class="left-panel">
|
||||||
|
<a-form layout="inline" :model="searchForm">
|
||||||
|
<!-- 常用搜索条件 -->
|
||||||
|
<a-form-item label="角色名称">
|
||||||
|
<a-input v-model:value="searchForm.keyword" placeholder="请输入角色名称" allow-clear style="width: 180px" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="handleSearch">
|
||||||
|
<template #icon><search-outlined /></template>
|
||||||
|
搜索
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="handleReset">
|
||||||
|
<template #icon><redo-outlined /></template>
|
||||||
|
重置
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
<div class="right-panel">
|
||||||
|
<a-button type="primary" @click="handleAdd">
|
||||||
|
<template #icon><plus-outlined /></template>
|
||||||
|
新增
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table-content">
|
||||||
|
<scTable
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="tableData"
|
||||||
|
:loading="loading"
|
||||||
|
:pagination="pagination"
|
||||||
|
@refresh="refreshTable"
|
||||||
|
@paginationChange="handlePaginationChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useTable } from '@/hooks/useTable'
|
||||||
|
|
||||||
|
// 使用 useTable Hook
|
||||||
|
const {
|
||||||
|
tableRef,
|
||||||
|
searchForm,
|
||||||
|
tableData,
|
||||||
|
loading,
|
||||||
|
pagination,
|
||||||
|
handleSearch,
|
||||||
|
handleReset,
|
||||||
|
handlePaginationChange,
|
||||||
|
refreshTable
|
||||||
|
} = useTable({
|
||||||
|
api: api.roles.list.get,
|
||||||
|
searchForm: {
|
||||||
|
keyword: '',
|
||||||
|
status: null
|
||||||
|
},
|
||||||
|
columns: [],
|
||||||
|
needPagination: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表格列配置(包含筛选器)
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '角色名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
width: 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '描述',
|
||||||
|
dataIndex: 'description',
|
||||||
|
key: 'description',
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
key: 'status',
|
||||||
|
width: 100,
|
||||||
|
align: 'center',
|
||||||
|
filters: [
|
||||||
|
{ text: '正常', value: 1 },
|
||||||
|
{ text: '禁用', value: 0 }
|
||||||
|
],
|
||||||
|
filterMultiple: false,
|
||||||
|
slot: 'status'
|
||||||
|
},
|
||||||
|
// ...其他列
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
**方式二: 使用高级搜索抽屉(复杂搜索条件)**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div class="pages-base-layout user-page">
|
||||||
|
<div class="tool-bar">
|
||||||
|
<div class="left-panel">
|
||||||
|
<a-form layout="inline" :model="searchForm">
|
||||||
|
<!-- 常用搜索条件 -->
|
||||||
|
<a-form-item label="用户名">
|
||||||
|
<a-input v-model:value="searchForm.username" placeholder="请输入用户名" allow-clear style="width: 140px" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="handleSearch">搜索</a-button>
|
||||||
|
<a-button @click="handleReset">重置</a-button>
|
||||||
|
<a-button @click="showAdvancedSearch = true">高级搜索</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-content">
|
||||||
|
<scTable
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="tableData"
|
||||||
|
:loading="loading"
|
||||||
|
:pagination="pagination"
|
||||||
|
@refresh="refreshTable"
|
||||||
|
@paginationChange="handlePaginationChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 高级搜索抽屉 -->
|
||||||
|
<SearchDrawer
|
||||||
|
v-model:visible="showAdvancedSearch"
|
||||||
|
:form-data="searchForm"
|
||||||
|
@confirm="handleAdvancedSearch"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useTable } from '@/hooks/useTable'
|
||||||
|
import SearchDrawer from './components/SearchDrawer.vue'
|
||||||
|
|
||||||
|
const showAdvancedSearch = ref(false)
|
||||||
|
|
||||||
|
const handleAdvancedSearch = (formData) => {
|
||||||
|
// 更新搜索表单数据
|
||||||
|
Object.assign(searchForm, formData)
|
||||||
|
showAdvancedSearch.value = false
|
||||||
|
handleSearch()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
**SearchDrawer.vue 组件示例**:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<a-drawer v-model:open="visible" title="高级搜索" placement="right" width="400">
|
||||||
|
<a-form :model="formData" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||||
|
<a-form-item label="姓名">
|
||||||
|
<a-input v-model:value="formData.real_name" placeholder="请输入姓名" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="邮箱">
|
||||||
|
<a-input v-model:value="formData.email" placeholder="请输入邮箱" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="手机号">
|
||||||
|
<a-input v-model:value="formData.phone" placeholder="请输入手机号" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="状态">
|
||||||
|
<a-select v-model:value="formData.status" placeholder="请选择状态" allow-clear>
|
||||||
|
<a-select-option :value="1">正常</a-select-option>
|
||||||
|
<a-select-option :value="0">禁用</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<template #footer>
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="handleCancel">取消</a-button>
|
||||||
|
<a-button type="primary" @click="handleConfirm">确定</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
visible: Boolean,
|
||||||
|
formData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:visible', 'confirm'])
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('update:visible', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleConfirm = () => {
|
||||||
|
emit('confirm', { ...formData })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
**搜索条件选择建议**:
|
||||||
|
|
||||||
|
常用搜索条件(显示在工具栏):
|
||||||
|
- 主要关键字搜索(如用户名、角色名称)
|
||||||
|
- 无需筛选的简单字段
|
||||||
|
|
||||||
|
表格筛选(推荐用于):
|
||||||
|
- 状态、类型等枚举值字段
|
||||||
|
- 不需要组合条件的单一筛选
|
||||||
|
|
||||||
|
高级搜索抽屉(用于):
|
||||||
|
- 次要字段(如邮箱、手机号)
|
||||||
|
- 日期范围筛选
|
||||||
|
- 多条件组合筛选
|
||||||
|
- 复杂的搜索逻辑
|
||||||
|
|
||||||
|
**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`
|
- **单文件组件**: 使用 PascalCase 命名,如 `UserList.vue`
|
||||||
- **公共组件**: 以 `sc` 开头,如 `scTable.vue`
|
- **公共组件**: 以 `sc` 开头,如 `scTable.vue`
|
||||||
- **页面组件**: 使用语义化命名,如 `UserManagement.vue`
|
- **页面私有组件**: 使用语义化命名,如 `SaveDialog.vue`, `RoleDialog.vue`
|
||||||
|
|
||||||
#### 组件结构
|
#### 组件结构
|
||||||
|
|
||||||
@@ -121,7 +566,7 @@ onMounted(() => {})
|
|||||||
</style>
|
</style>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. API 接口开发规范
|
### 4. API 接口开发规范
|
||||||
|
|
||||||
#### 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 值
|
1. **顶级菜单**(parent_id=0)不需要 component 值
|
||||||
- 顶级菜单作为分组容器,不需要指定组件路径
|
- 顶级菜单作为分组容器,不需要指定组件路径
|
||||||
@@ -260,47 +721,77 @@ export default [
|
|||||||
- 前端会将菜单数据转换为路由,所有页面路由都会挂载到 Layout 布局下
|
- 前端会将菜单数据转换为路由,所有页面路由都会挂载到 Layout 布局下
|
||||||
- 不需要在前端维护嵌套的路由结构
|
- 不需要在前端维护嵌套的路由结构
|
||||||
|
|
||||||
|
**数据示例**:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// 示例:顶级菜单(无 component)
|
// 示例:顶级菜单(无 component)
|
||||||
{
|
{
|
||||||
name: '系统管理',
|
id: 1,
|
||||||
code: 'system',
|
title: '系统管理',
|
||||||
|
name: 'system',
|
||||||
type: 'menu',
|
type: 'menu',
|
||||||
parent_id: 0,
|
parent_id: 0,
|
||||||
route: '/system',
|
path: '/system',
|
||||||
component: null, // 顶级菜单不需要 component
|
component: null, // 顶级菜单不需要 component
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'Setting',
|
icon: 'Setting',
|
||||||
hidden: false
|
hidden: false,
|
||||||
|
hiddenBreadcrumb: false,
|
||||||
|
keepAlive: false
|
||||||
},
|
},
|
||||||
sort: 1
|
sort: 1,
|
||||||
|
status: 1,
|
||||||
|
children: []
|
||||||
}
|
}
|
||||||
|
|
||||||
// 示例:最后一级菜单(有 component)
|
// 示例:最后一级菜单(有 component)
|
||||||
{
|
{
|
||||||
name: '用户管理',
|
id: 2,
|
||||||
code: 'system.users',
|
title: '用户管理',
|
||||||
|
name: 'system.users',
|
||||||
type: 'menu',
|
type: 'menu',
|
||||||
parent_id: 0,
|
parent_id: 0,
|
||||||
route: '/system/users',
|
path: '/system/users',
|
||||||
component: 'system/users/index', // 最后一级菜单需要 component
|
component: 'system/users/index', // 最后一级菜单需要 component
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'User',
|
icon: 'User',
|
||||||
hidden: false
|
hidden: false,
|
||||||
|
hiddenBreadcrumb: false,
|
||||||
|
keepAlive: true
|
||||||
},
|
},
|
||||||
sort: 1
|
sort: 1,
|
||||||
|
status: 1,
|
||||||
|
children: []
|
||||||
}
|
}
|
||||||
|
|
||||||
// 示例:按钮权限(无 component)
|
// 示例:按钮权限(无 component)
|
||||||
{
|
{
|
||||||
name: '查看用户',
|
id: 3,
|
||||||
code: 'system.users.view',
|
title: '查看用户',
|
||||||
|
name: 'system.users.view',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
parent_id: 0,
|
parent_id: 2,
|
||||||
route: 'admin.users.index',
|
path: null,
|
||||||
component: null, // 非菜单类型不需要 component
|
component: null, // 非菜单类型不需要 component
|
||||||
meta: null,
|
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) {
|
function transformMenusToRoutes(menus) {
|
||||||
return menus.map(menu => {
|
return menus.map(menu => {
|
||||||
const route = {
|
const route = {
|
||||||
path: menu.route,
|
path: menu.path,
|
||||||
name: menu.name || menu.code,
|
name: menu.name,
|
||||||
meta: {
|
meta: {
|
||||||
title: menu.name,
|
title: menu.title,
|
||||||
icon: menu.meta?.icon,
|
icon: menu.meta?.icon,
|
||||||
hidden: menu.meta?.hidden,
|
hidden: menu.meta?.hidden || false,
|
||||||
keepAlive: menu.meta?.keepAlive || false
|
keepAlive: menu.meta?.keepAlive || false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -346,9 +837,12 @@ function loadComponent(componentPath) {
|
|||||||
**菜单拉平处理说明**:
|
**菜单拉平处理说明**:
|
||||||
|
|
||||||
由于前端将所有页面菜单拉平挂载在 Layout 下,因此:
|
由于前端将所有页面菜单拉平挂载在 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 定义
|
#### Pinia Store 定义
|
||||||
|
|
||||||
@@ -547,7 +1041,7 @@ const userStore = useUserStore()
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. 表格开发规范
|
### 7. 表格开发规范
|
||||||
|
|
||||||
#### 使用 useTable Hook
|
#### 使用 useTable Hook
|
||||||
|
|
||||||
@@ -594,7 +1088,7 @@ const searchParams = ref({
|
|||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7. 表单开发规范
|
### 8. 表单开发规范
|
||||||
|
|
||||||
#### scForm 组件使用
|
#### scForm 组件使用
|
||||||
|
|
||||||
@@ -650,7 +1144,7 @@ const handleSubmit = async () => {
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 8. 图标使用规范
|
### 9. 图标使用规范
|
||||||
|
|
||||||
#### Ant Design Vue Icons
|
#### Ant Design Vue Icons
|
||||||
|
|
||||||
@@ -680,7 +1174,7 @@ const handleSubmit = async () => {
|
|||||||
- `info-circle`: 信息
|
- `info-circle`: 信息
|
||||||
- `warning`: 警告
|
- `warning`: 警告
|
||||||
|
|
||||||
### 9. 国际化 (i18n) 规范
|
### 10. 国际化 (i18n) 规范
|
||||||
|
|
||||||
#### 使用 i18n
|
#### 使用 i18n
|
||||||
|
|
||||||
@@ -711,7 +1205,7 @@ export default {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 10. 文件上传规范
|
### 11. 文件上传规范
|
||||||
|
|
||||||
#### scUpload 组件使用
|
#### scUpload 组件使用
|
||||||
|
|
||||||
@@ -732,7 +1226,7 @@ const imageUrl = ref('')
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 11. 富文本编辑器规范
|
### 12. 富文本编辑器规范
|
||||||
|
|
||||||
#### scEditor 组件使用
|
#### scEditor 组件使用
|
||||||
|
|
||||||
@@ -757,12 +1251,129 @@ const toolbarConfig = [
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 12. 样式规范
|
### 13. 样式规范
|
||||||
|
|
||||||
#### 全局样式
|
#### 全局样式
|
||||||
|
|
||||||
在 `src/style.css` 中定义全局样式。
|
在 `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
|
||||||
|
<!-- 标准页面布局 -->
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.user-page {
|
||||||
|
@extend .pages-base-layout;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- 侧边栏布局 -->
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.user-page {
|
||||||
|
@extend .pages-sidebar-layout;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
#### 组件样式
|
#### 组件样式
|
||||||
|
|
||||||
使用 `scoped` 避免样式污染:
|
使用 `scoped` 避免样式污染:
|
||||||
@@ -784,7 +1395,7 @@ const toolbarConfig = [
|
|||||||
- 使用 BEM 命名法
|
- 使用 BEM 命名法
|
||||||
- 类名使用 kebab-case
|
- 类名使用 kebab-case
|
||||||
|
|
||||||
### 13. 工具函数使用
|
### 14. 工具函数使用
|
||||||
|
|
||||||
#### request.js (HTTP 请求)
|
#### request.js (HTTP 请求)
|
||||||
|
|
||||||
@@ -816,7 +1427,7 @@ formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss')
|
|||||||
deepClone(originalObject)
|
deepClone(originalObject)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 14. 开发流程
|
### 15. 开发流程
|
||||||
|
|
||||||
#### 登录流程
|
#### 登录流程
|
||||||
|
|
||||||
@@ -875,7 +1486,7 @@ const handleLogin = async () => {
|
|||||||
3. 添加必要的 Props 和 Emits
|
3. 添加必要的 Props 和 Emits
|
||||||
4. 编写组件文档
|
4. 编写组件文档
|
||||||
|
|
||||||
### 15. 常用命令
|
### 16. 常用命令
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 安装依赖
|
# 安装依赖
|
||||||
@@ -897,7 +1508,7 @@ npm run format
|
|||||||
npm run lint
|
npm run lint
|
||||||
```
|
```
|
||||||
|
|
||||||
### 16. 注意事项
|
### 17. 注意事项
|
||||||
|
|
||||||
1. **禁止重复引入图标**: Ant Design Vue 图标已全局引入,直接使用即可
|
1. **禁止重复引入图标**: Ant Design Vue 图标已全局引入,直接使用即可
|
||||||
2. **使用组合式 API**: 新代码统一使用 `<script setup>` 语法
|
2. **使用组合式 API**: 新代码统一使用 `<script setup>` 语法
|
||||||
@@ -908,7 +1519,7 @@ npm run lint
|
|||||||
7. **不要编写 demo**: 开发过程中不编写示例代码
|
7. **不要编写 demo**: 开发过程中不编写示例代码
|
||||||
8. **测试提示**: 如需测试,提示用户是否运行测试,不主动运行
|
8. **测试提示**: 如需测试,提示用户是否运行测试,不主动运行
|
||||||
|
|
||||||
### 17. 代码质量
|
### 18. 代码质量
|
||||||
|
|
||||||
- 遵循 Vue 3 官方风格指南
|
- 遵循 Vue 3 官方风格指南
|
||||||
- 保持代码简洁、可读
|
- 保持代码简洁、可读
|
||||||
@@ -916,7 +1527,7 @@ npm run lint
|
|||||||
- 使用语义化的变量和函数命名
|
- 使用语义化的变量和函数命名
|
||||||
- 避免过多的嵌套层级
|
- 避免过多的嵌套层级
|
||||||
|
|
||||||
### 18. 性能优化
|
### 19. 性能优化
|
||||||
|
|
||||||
- 合理使用 `v-if` 和 `v-show`
|
- 合理使用 `v-if` 和 `v-show`
|
||||||
- 列表渲染必须设置 `key`
|
- 列表渲染必须设置 `key`
|
||||||
|
|||||||
Reference in New Issue
Block a user