Skip to content

组件体系

框架提供了一系列高级业务组件,封装了后台管理系统的通用交互模式。

表格组件

AppTable

通用 CRUD 表格组件,自动管理分页和数据加载:

vue
<AppTable
  ref="tableRef"
  :columns="columns"
  :fetch-data="fetchApi"
  :search-form="searchForm"
  :row-selection="rowSelection"
  :perm="'sys:module:page'"
  :fixed-height="true"
>
  <template #toolbar>
    <a-button type="primary" @click="openCreate">新增</a-button>
  </template>
  <template #bodyCell="{ column, record }">
    <!-- 自定义列渲染 -->
  </template>
</AppTable>

Props:

Prop类型说明
columnsarray表格列定义
data / fetchDatafunction数据获取函数(接收 { current, size, ...search }
searchFormobject搜索条件对象,自动过滤空值后传入 data 函数
rowSelectionobject行选择配置 { selectedRowKeys, onChange, type }
permstring/array权限码,无权限时隐藏
fixedHeightboolean固定高度模式,自动计算滚动区域
rowKeystring行 key 字段名,默认 id

fetch-datasearch-form 绑定后,表格自动在初始化、搜索条件变化、分页变化时加载数据。

AppTreeTable

树形表格组件,适用于组织架构、资源管理等树形数据展示:

vue
<AppTreeTable
  :columns="columns"
  :data-source="treeData"
  :loading="loading"
  :default-expand-all-rows="true"
  children-column-name="children"
>
  <template #toolbar>
    <a-button type="primary" @click="handleAdd">新增</a-button>
  </template>
</AppTreeTable>

与 AppTable 不同,AppTreeTable 的数据由父组件控制(data-source prop),不自动加载。

表单组件

AppSearchPanel

搜索面板组件,自动折叠多余搜索项,支持权限控制:

vue
<AppSearchPanel
  :model="searchForm"
  :collapse-after="4"
  perm="sys:module:page"
  @search="handleSearch"
  @reset="resetSearch"
>
  <a-col :xs="24" :sm="12" :md="8" :lg="6">
    <a-form-item label="关键词" name="keyword">
      <a-input v-model:value="searchForm.keyword" allow-clear />
    </a-form-item>
  </a-col>
</AppSearchPanel>

AppDrawerForm

抽屉表单组件,从右侧滑入的表单,通过 on-submit 回调函数处理提交:

vue
<AppDrawerForm
  :open="open"
  title="添加用户"
  :form="form"
  :rules="rules"
  :on-submit="handleSubmit"
  @close="handleClose"
  @success="handleFormSuccess"
>
  <template #default="{ form }">
    <a-form-item label="用户名" name="username">
      <a-input v-model:value="form.username" />
    </a-form-item>
  </template>
</AppDrawerForm>

组件在提交成功后自动调用 onSubmit 并显示成功提示,然后关闭抽屉。

DictSelect

字典选择器,自动从字典缓存中加载选项列表,支持多种展示形式:

vue
<DictSelect
  v-model="form.status"
  type-code="USER_STATUS"
  option-type="dropdown"
  placeholder="全部"
/>

Props:

Prop类型说明
type-codestring字典类型编码(需与后端定义一致)
option-typestring展示形式:dropdown / radio / button / checkbox
v-modelany绑定值
placeholderstring占位文本
allow-clearboolean是否允许清除
disabledboolean是否禁用

布局组件

AppSplitPanel

分割面板组件,实现可拖拽调整宽度的左右分栏布局:

vue
<AppSplitPanel
  :initial-size="260"
  :min-size="200"
  :max-size="400"
  @resize="handleResize"
>
  <template #left>
    <!-- 左侧树形结构 -->
  </template>
  <template #right>
    <!-- 右侧内容 -->
  </template>
</AppSplitPanel>

AppTreePanel

树形面板组件,左侧展示树形结构,右侧展示选中节点的详情或列表。

CRUD Hook

useCrud Hook 封装了列表页的通用操作逻辑:

typescript
import { useCrud } from '@/hooks/useCrud'
import { fetchUserRemove } from '@/api/user'

const crud = useCrud({
  name: '用户',
  deleteApi: fetchUserRemove,
  onSuccess: () => { /* 刷新后的回调 */ },
})

const { tableRef, selectedKeys, rowSelection, handleSearch, handleDelete, handleBatchDelete, handleFormSuccess } = crud

Options:

参数类型说明
namestring实体名称,用于删除确认提示
deleteApifunction删除 API:({ ids }) => Promise<{ success }>
onSuccessfunction操作成功后的回调(可选)

返回值:

返回值说明
tableRef传递给 AppTable 的 ref,用于调用内部方法
selectedKeys当前选中的行 ID 列表(响应式)
rowSelection绑定到 AppTable 的 row-selection 的计算属性
handleSearch触发表格刷新(保留当前分页)
resetSearch重置搜索表单并刷新(接收 form 对象和可选的默认值)
handleDelete单条删除(确认后调用 deleteApi)
handleBatchDelete批量删除(弹窗确认后调用 deleteApi)
handleFormSuccess表单操作成功后刷新表格

使用模式

实际页面中的完整使用示例:

vue
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useAuthStore } from '@/store'
import { fetchModulePage, fetchModuleRemove } from '@/api/module'
import { useCrud } from '@/hooks/useCrud'

const auth = useAuthStore()
const hasPermission = auth.hasPermission

const crud = useCrud({ name: '模块', deleteApi: fetchModuleRemove })
const { tableRef, selectedKeys, rowSelection, handleSearch, handleDelete, handleBatchDelete, handleFormSuccess } = crud

const searchForm = reactive({ keyword: '', status: undefined })
const columns = [
  { title: '名称', dataIndex: 'name', key: 'name' },
  { title: '状态', dataIndex: 'status', key: 'status', width: 90 },
  { title: '操作', key: 'action', width: 150, fixed: 'right' },
]

function resetSearch() {
  searchForm.keyword = ''
  searchForm.status = undefined
  tableRef.value?.refresh(true)
}
</script>

<template>
  <AppSearchPanel
    :model="searchForm"
    perm="sys:module:page"
    @search="handleSearch"
    @reset="resetSearch"
  >
    <a-col :xs="24" :sm="12" :md="8" :lg="6">
      <a-form-item label="关键词" name="keyword">
        <a-input v-model:value="searchForm.keyword" allow-clear />
      </a-form-item>
    </a-col>
  </AppSearchPanel>

  <AppTable
    ref="tableRef"
    perm="sys:module:page"
    :columns="columns"
    :fetch-data="fetchModulePage"
    :search-form="searchForm"
    :row-selection="rowSelection"
  >
    <template #toolbar>
      <a-button v-if="hasPermission('sys:module:create')" type="primary">新增</a-button>
      <a-button v-if="hasPermission('sys:module:remove')" danger :disabled="selectedKeys.length === 0" @click="handleBatchDelete">批量删除</a-button>
    </template>
  </AppTable>
</template>

Released under the MIT License.