跳转到主要内容

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-hasPermv-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:edituser: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 性能优化建议

  1. 表格性能优化

    • 对于大数据量表格,使用 height 属性启用虚拟滚动
    • 合理设置 scrollbar-always-on 属性
    • 使用 stripeborder 属性时注意性能影响
  2. 字典数据优化

    • 尽量在组件初始化时获取所有需要的字典类型
    • 避免在循环中频繁调用 selectDictLabel 函数
  3. 权限控制优化

    • 避免在大量重复元素上使用权限指令
    • 可以在父组件层面进行权限判断,减少指令使用

6.3 代码规范建议

  1. 组件命名

    • 使用 PascalCase 命名组件
    • 组件文件使用 kebab-case 命名
  2. Props 定义

    • 明确 Props 类型和默认值
    • 使用 v-model 实现双向绑定
  3. 事件处理

    • 使用明确的事件名称
    • 避免在模板中编写复杂的事件处理逻辑
  4. 代码结构

    • 合理组织组件代码结构
    • 使用 setup script 语法
    • 避免组件过大,合理拆分组件

文档版本:1.0 更新日期:2023-07-01 编写人员:Wimoor UI3UI 开发团队