UI公共组件使用手册
1. Pagination 分页组件
Pagination 是一个基于 Element Plus 的分页组件封装,提供了丰富的功能和灵活的配置选项。
1.1 核心功能
- 自定义页面大小选择
- 响应式设计(移动端适配)
- 自动滚动到顶部
- 支持多种布局配置
- 事件回调机制
1.2 基本使用
<template>
<Pagination
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</template>
<script setup>
import { ref } from 'vue'
import Pagination from '@/components/Pagination/index.vue'
const queryParams = ref({
pageNum: 1,
pageSize: 20
})
const total = ref(0)
const getList = (pagination) => {
queryParams.value = { ...queryParams.value, ...pagination }
// 调用API获取数据
}
</script>
1.3 高级配置
1.3.1 自定义页面大小
<Pagination
:page-sizes="[10, 20, 50, 100, 200]"
:limit="10"
@pagination="getList"
/>
1.3.2 自定义布局
<Pagination
:layout="'total, sizes, prev, pager, next, jumper'
@pagination="getList"
/>
1.3.3 禁用自动滚动
<Pagination
:auto-scroll="false"
@pagination="getList"
/>
1.4 响应式设计
组件会根据屏幕宽度自动调整页码按钮的数量:
- 大屏幕(≥992px):显示7个页码按钮
- 小屏幕(<992px):显示5个页码按钮
2. GlobalTable 全局表格组件
GlobalTable 是一个功能强大的表格组件,基于 Element Plus Table 封装,提供了丰富的功能和灵活的配置选项。
2.1 核心功能
- 数据加载与分页
- 排序与筛选
- 行选择与点击事件
- 行展开与树形结构
- 自定义列与表头
- 表格浮动表头
- 字段动态配置
- 性能优化(虚拟滚动、缓存等)
2.2 基本使用
<template>
<GlobalTable
ref="globalTableRef"
:table-data="tableData"
:query-params="queryParams"
@load-table="getList"
@row-click="handleRowClick"
>
<template #field>
<el-table-column prop="name" label="名称" />
<el-table-column prop="status" label="状态" />
<el-table-column prop="createTime" label="创建时间" width="180" />
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</template>
</GlobalTable>
</template>
<script setup>
import { ref } from 'vue'
import GlobalTable from '@/components/Table/GlobalTable/index.vue'
const globalTableRef = ref()
const tableData = ref({ records: [], total: 0 })
const queryParams = ref({})
const getList = (params, callback) => {
// 调用API获取数据
// 成功后调用callback
callback({
records: data.list,
total: data.total
})
}
const handleRowClick = (row) => {
console.log('点击行:', row)
}
</script>
2.3 高级配置
2.3.1 树形表格
<GlobalTable
:table-data="tableData"
:lazy="true"
:load="loadChild"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
@load-table="getList"
>
<!-- 列定义 -->
</GlobalTable>
<script setup>
const loadChild = (tree, treeNode, resolve) => {
// 加载子节点数据
// 成功后调用resolve
resolve(childData)
}
</script>
2.3.2 自定义排序
<GlobalTable
:table-data="tableData"
:default-sort="{ prop: 'createTime', order: 'descending' }"
@load-table="getList"
@table-sort="handleSort"
>
<!-- 列定义 -->
</GlobalTable>
<script setup>
const handleSort = (option) => {
queryParams.value.sort = option.prop
queryParams.value.order = option.order.replace('ascending', 'asc').replace('descending', 'desc')
}
</script>
2.3.3 表格汇总
<GlobalTable
:table-data="tableData"
:show-summary="true"
:summary-method="getSummaries"
@load-table="getList"
>
<!-- 列定义 -->
</GlobalTable>
<script setup>
const getSummaries = ({ columns, data }) => {
const sums = []
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = '合计'
return
}
const values = data.map(item => Number(item[column.property]))
sums[index] = values.reduce((prev, curr) => prev + curr, 0)
})
return sums
}
</script>
2.3.4 动态字段配置
<GlobalTable
:table-data="tableData"
:field-type="'user_list'"
@load-table="getList"
>
<!-- 列定义 -->
</GlobalTable>
通过配置 field-type,GlobalTable 会自动从后端获取字段配置,并根据用户设置动态显示/隐藏列。
2.4 性能优化
2.4.1 浮动表头
当表格内容较长时,滚动时表头会固定在顶部:
<GlobalTable
:table-data="tableData"
@load-table="getList"
>
<!-- 列定义 -->
</GlobalTable>
2.4.2 虚拟滚动
对于大数据量表格,可以启用虚拟滚动:
<GlobalTable
:table-data="tableData"
:height="500"
:scrollbar-always-on="true"
@load-table="getList"
>
<!-- 列定义 -->
</GlobalTable>
2.5 常用方法
<script setup>
const globalTableRef = ref()
// 刷新表格
globalTableRef.value.refreshTable()
// 加载表格数据
globalTableRef.value.loadTable(queryParams)
// 切换行选中状态
globalTableRef.value.toggleRowSelection(row, true)
// 获取选中行
globalTableRef.value.getSelectionRows()
// 设置当前行
globalTableRef.value.setCurrentRow(row)
</script>
3. 字典树运用
字典树是系统中用于管理和展示字典数据的机制,包括字典数据的获取、缓存和展示。
3.1 核心功能
- 字典数据缓存
- 多字典类型同时获取
- 字典标签展示
- 字典值转换
3.2 字典数据获取
使用 useDict hook 获取字典数据:
<script setup>
import { useDict } from '@/utils/dict'
// 获取单个字典类型
const { status } = useDict('sys_status')
// 获取多个字典类型
const { status, gender, userType } = useDict('sys_status', 'sys_user_gender', 'sys_user_type')
</script>
3.3 字典数据展示
使用 DictTag 组件展示字典标签:
<template>
<DictTag :options="status" :value="record.status" />
</template>
<script setup>
import { useDict } from '@/utils/dict'
import DictTag from '@/components/DictTag/index.vue'
const { status } = useDict('sys_status')
</script>
3.4 字典数据转换
使用工具函数转换字典值:
<script setup>
import { selectDictLabel } from '@/utils/wimoor'
import { useDict } from '@/utils/dict'
const { status } = useDict('sys_status')
// 将字典值转换为标签
const statusLabel = selectDictLabel(status.value, record.status)
</script>
3.5 字典数据缓存
字典数据会自动缓存到 Vuex 的 dictStore 中,避免重复请求:
// 手动获取字典数据
import useDictStore from '@/hooks/store/useDictStore'
// 获取字典数据
const statusDict = useDictStore.getDict('sys_status')
// 手动设置字典数据
useDictStore.setDict('sys_status', dictData)
// 清除字典数据
useDictStore.removeDict('sys_status')
// 清除所有字典数据
useDictStore.cleanDict()
4. 权限控制
系统提供了基于指令的权限控制机制,使用 v-hasPerm 和 v-hasPermi 指令动态显示/隐藏元素。
4.1 核心功能
- 单个权限控制(v-hasPerm)
- 多个权限控制(v-hasPermi)
- 基于用户权限动态判断
- 无权限时自动隐藏元素
4.2 权限指令使用
4.2.1 v-hasPerm 单个权限控制
<el-button type="primary" v-hasPerm="'user:add'">新增用户</el-button>
当用户拥有 user:add 权限时,按钮会显示;否则会隐藏。
4.2.2 v-hasPermi 多个权限控制
<el-button type="danger" v-hasPermi="['user:edit', 'user:delete']">批量操作</el-button>
当用户拥有 user:edit 或 user:delete 权限时,按钮会显示;否则会隐藏。
4.3 权限控制原理
权限控制的核心逻辑在 permission.js 中实现:
export const hasPerm = {
install: (app) => {
app.directive('hasPerm', {
mounted(el, binding) {
const permissions = store.state.permissionStore.permission;
const value = binding.value;
const flag = permissions.has(value);
if (!flag) {
el.parentNode.removeChild(el);
}
}
});
}
};
4.4 权限数据来源
用户权限数据从后端 API 获取,并存储在 Vuex 的 permissionStore 中:
// 从后端获取权限数据
avoid axios.get('/api/admin/api/v1/menus/route').then(res => {
// 存储权限数据
store.dispatch("permissionStore/setPermission", res.data.data[0].meta.permissions);
});
4.5 高级使用场景
4.5.1 权限控制在表格操作列
<GlobalTable
:table-data="tableData"
@load-table="getList"
>
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" v-hasPerm="'user:edit'" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="danger" size="small" v-hasPerm="'user:delete'" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</GlobalTable>
4.5.2 权限控制在表单字段
<el-form-item label="角色" prop="roleId" v-hasPerm="'user:assignRole'">
<el-select v-model="form.roleId" placeholder="请选择角色">
<el-option v-for="role in roles" :key="role.roleId" :label="role.roleName" :value="role.roleId" />
</el-select>
</el-form-item>
4.5.3 权限控制在菜单
<el-menu-item v-for="menu in menus" :key="menu.path" :index="menu.path" v-hasPerm="menu.permission">
<template #title>
<span>{{ menu.name }}</span>
</template>
</el-menu-item>
5. 其他公共组件
5.1 SvgIcon 图标组件
<svg-icon icon-class="user" />
<svg-icon icon-class="edit" class="icon-large" />
5.2 FileUpload 文件上传组件
<FileUpload
v-model="form.fileUrl"
:limit="1"
:file-list="fileList"
@success="handleUploadSuccess"
/>
5.3 Editor 富文本组件
<Editor
v-model="form.content"
:height="300"
@change="handleContentChange"
/>
5.4 RightToolbar 表格工具栏组件
<RightToolbar @handleSizeChange="handleSizeChange">
<el-button type="primary" @click="handleAdd">
<i class="el-icon-plus"></i> 新增
</el-button>
<el-button type="danger" @click="handleDelete" :disabled="selectionList.length === 0">
<i class="el-icon-delete"></i> 删除
</el-button>
</RightToolbar>
6. 最佳实践
6.1 组件组合使用
表格 + 分页 + 字典 + 权限:
<template>
<div class="app-container">
<RightToolbar @handleSizeChange="handleSizeChange">
<el-button type="primary" v-hasPerm="'user:add'" @click="handleAdd">
<i class="el-icon-plus"></i> 新增用户
</el-button>
<el-button type="danger" v-hasPerm="'user:delete'" @click="handleDelete" :disabled="selectionList.length === 0">
<i class="el-icon-delete"></i> 批量删除
</el-button>
</RightToolbar>
<GlobalTable
ref="globalTableRef"
:table-data="tableData"
:query-params="queryParams"
@load-table="getList"
@selection-change="handleSelectionChange"
>
<template #field>
<el-table-column type="selection" width="55" />
<el-table-column prop="userName" label="用户名" />
<el-table-column prop="gender" label="性别">
<template #default="scope">
<DictTag :options="gender" :value="scope.row.gender" />
</template>
</el-table-column>
<el-table-column prop="status" label="状态">
<template #default="scope">
<DictTag :options="status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" v-hasPerm="'user:edit'" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="danger" size="small" v-hasPerm="'user:delete'" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</template>
</GlobalTable>
</div>
</template>
<script setup>
import { ref } from 'vue'
import GlobalTable from '@/components/Table/GlobalTable/index.vue'
import RightToolbar from '@/components/RightToolbar/index.vue'
import DictTag from '@/components/DictTag/index.vue'
import { useDict } from '@/utils/dict'
const globalTableRef = ref()
const tableData = ref({ records: [], total: 0 })
const queryParams = ref({})
const selectionList = ref([])
const { gender, status } = useDict('sys_user_gender', 'sys_status')
const getList = (params, callback) => {
// 调用API获取数据
callback(data)
}
const handleSelectionChange = (selection) => {
selectionList.value = selection
}
const handleAdd = () => {
// 新增用户
}
const handleEdit = (row) => {
// 编辑用户
}
const handleDelete = (row) => {
// 删除用户
}
const handleSizeChange = (size) => {
globalTableRef.value.changeSize(size)
}
</script>
6.2 性能优化建议
-
表格性能优化:
- 对于大数据量表格,使用
height属性启用虚拟滚动 - 合理设置
scrollbar-always-on属性 - 使用
stripe和border属性时注意性能影响
- 对于大数据量表格,使用
-
字典数据优化:
- 尽量在组件初始化时获取所有需要的字典类型
- 避免在循环中频繁调用
selectDictLabel函数
-
权限控制优化:
- 避免在大量重复元素上使用权限指令
- 可以在父组件层面进行权限判断,减少指令使用
6.3 代码规范建议
-
组件命名:
- 使用 PascalCase 命名组件
- 组件文件使用 kebab-case 命名
-
Props 定义:
- 明确 Props 类型和默认值
- 使用 v-model 实现双向绑定
-
事件处理:
- 使用明确的事件名称
- 避免在模板中编写复杂的事件处理逻辑
-
代码结构:
- 合理组织组件代码结构
- 使用 setup script 语法
- 避免组件过大,合理拆分组件
文档版本:1.0
更新日期:2023-07-01
编写人员:Wimoor UI3UI 开发团队