# UI公共组件使用手册

## 1. Pagination 分页组件

Pagination 是一个基于 Element Plus 的分页组件封装，提供了丰富的功能和灵活的配置选项。

### 1.1 核心功能

- 自定义页面大小选择
- 响应式设计（移动端适配）
- 自动滚动到顶部
- 支持多种布局配置
- 事件回调机制

### 1.2 基本使用

```vue
<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 自定义页面大小

```vue
<Pagination
  :page-sizes="[10, 20, 50, 100, 200]"
  :limit="10"
  @pagination="getList"
/>
```

#### 1.3.2 自定义布局

```vue
<Pagination
  :layout="'total, sizes, prev, pager, next, jumper'
  @pagination="getList"
/>
```

#### 1.3.3 禁用自动滚动

```vue
<Pagination
  :auto-scroll="false"
  @pagination="getList"
/>
```

### 1.4 响应式设计

组件会根据屏幕宽度自动调整页码按钮的数量：
- 大屏幕（≥992px）：显示7个页码按钮
- 小屏幕（<992px）：显示5个页码按钮

## 2. GlobalTable 全局表格组件

GlobalTable 是一个功能强大的表格组件，基于 Element Plus Table 封装，提供了丰富的功能和灵活的配置选项。

### 2.1 核心功能

- 数据加载与分页
- 排序与筛选
- 行选择与点击事件
- 行展开与树形结构
- 自定义列与表头
- 表格浮动表头
- 字段动态配置
- 性能优化（虚拟滚动、缓存等）

### 2.2 基本使用

```vue
<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 树形表格

```vue
<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 自定义排序

```vue
<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 表格汇总

```vue
<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 动态字段配置

```vue
<GlobalTable
  :table-data="tableData"
  :field-type="'user_list'"
  @load-table="getList"
>
  <!-- 列定义 -->
</GlobalTable>
```

通过配置 `field-type`，GlobalTable 会自动从后端获取字段配置，并根据用户设置动态显示/隐藏列。

### 2.4 性能优化

#### 2.4.1 浮动表头

当表格内容较长时，滚动时表头会固定在顶部：

```vue
<GlobalTable
  :table-data="tableData"
  @load-table="getList"
>
  <!-- 列定义 -->
</GlobalTable>
```

#### 2.4.2 虚拟滚动

对于大数据量表格，可以启用虚拟滚动：

```vue
<GlobalTable
  :table-data="tableData"
  :height="500"
  :scrollbar-always-on="true"
  @load-table="getList"
>
  <!-- 列定义 -->
</GlobalTable>
```

### 2.5 常用方法

```vue
<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 获取字典数据：

```vue
<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` 组件展示字典标签：

```vue
<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 字典数据转换

使用工具函数转换字典值：

```vue
<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` 中，避免重复请求：

```javascript
// 手动获取字典数据
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 单个权限控制

```vue
<el-button type="primary" v-hasPerm="'user:add'">新增用户</el-button>
```

当用户拥有 `user:add` 权限时，按钮会显示；否则会隐藏。

#### 4.2.2 v-hasPermi 多个权限控制

```vue
<el-button type="danger" v-hasPermi="['user:edit', 'user:delete']">批量操作</el-button>
```

当用户拥有 `user:edit` 或 `user:delete` 权限时，按钮会显示；否则会隐藏。

### 4.3 权限控制原理

权限控制的核心逻辑在 `permission.js` 中实现：

```javascript
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` 中：

```javascript
// 从后端获取权限数据
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 权限控制在表格操作列

```vue
<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 权限控制在表单字段

```vue
<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 权限控制在菜单

```vue
<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 图标组件

```vue
<svg-icon icon-class="user" />
<svg-icon icon-class="edit" class="icon-large" />
```

### 5.2 FileUpload 文件上传组件

```vue
<FileUpload
  v-model="form.fileUrl"
  :limit="1"
  :file-list="fileList"
  @success="handleUploadSuccess"
/>
```

### 5.3 Editor 富文本组件

```vue
<Editor
  v-model="form.content"
  :height="300"
  @change="handleContentChange"
/>
```

### 5.4 RightToolbar 表格工具栏组件

```vue
<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 组件组合使用

**表格 + 分页 + 字典 + 权限**：

```vue
<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` 属性
   - 使用 `stripe` 和 `border` 属性时注意性能影响

2. **字典数据优化**：
   - 尽量在组件初始化时获取所有需要的字典类型
   - 避免在循环中频繁调用 `selectDictLabel` 函数

3. **权限控制优化**：
   - 避免在大量重复元素上使用权限指令
   - 可以在父组件层面进行权限判断，减少指令使用

### 6.3 代码规范建议

1. **组件命名**：
   - 使用 PascalCase 命名组件
   - 组件文件使用 kebab-case 命名

2. **Props 定义**：
   - 明确 Props 类型和默认值
   - 使用 v-model 实现双向绑定

3. **事件处理**：
   - 使用明确的事件名称
   - 避免在模板中编写复杂的事件处理逻辑

4. **代码结构**：
   - 合理组织组件代码结构
   - 使用 setup script 语法
   - 避免组件过大，合理拆分组件

---

**文档版本**：1.0
**更新日期**：2023-07-01
**编写人员**：Wimoor UI 开发团队