Skip to content

表格

我们封装了 Table 组件、baTable 类、baTableApi 类、表格顶部菜单 组件等,以方便快速生成和制作表格。

TIP

我们提供了一个 表格开发示例模块,内含大量表格使用、编程式操作的实际示例,欢迎下载体验。

Table 组件

Table 组件位于 \src\components\table,我们基于 el-table 封装了该组件,所以 el-table所有事件属性都是可以直接在 Table 组件上使用的,部分属性可能失效,比如 el-tablesize,这是因为 Table 组件拥有一些默认的 css 样式。

组件属性

属性名注释
pagination是否显示底部分页组件boolean
...其他 el-table 的属性可以直接使用-

组件方法与事件

方法名/项目注释参数
getRef获取内部 el-tableref,然后就可以使用 el-table 的所有方法-
各类事件单双击、勾选、hover、右击等事件 el-table 均以自带,请转到 el-table 事件文档-

组件插槽

插槽名注释子标签
neck表格头与表格主体中间区域任意
columnPrepend通过此插槽在 baTable 类中定义的列之前插入列el-table-column
columnAppend通过此插槽在 baTable 类中定义的列之后插入列el-table-column
footer表格底部区域任意
任意列使用 render:slot, slotName:插槽名称 来使用 slot 渲染任意

TIP

Table 组件和下方的 baTable 类是搭配使用的,baTable 类为表格提供数据,并可以响应表格组件的各种事件,组件的使用示例,也请参考下方 baTable 类的示例。

baTable 表格管家类

  • 此类代码位于:\src\utils\baTable.ts
  • 我们在类里面封装好了一个表格应有的 属性方法,预设了 查看前、查看后、编辑前 等钩子

TIP

此类非常强大和重要,我们在 表格管家类的深度理解 一节对这一点进行了特别说明

使用示例

以下为基本使用示例,实际使用表格非常灵活,请发挥您的想象力;baTable 类支持的属性在 表格所有可用属性 有介绍,您还可以直接查阅 src\utils\baTable.ts 文件代码(有注释,不知道有哪些属性可用时,可直接查看 ts 的类型定义)

ts
<script setup lang="ts">
import Table from '/@/components/table/index.vue' // 导入 Table 组件
import baTableClass from '/@/utils/baTable' // 导入 baTable 类
import { baTableApi } from '/@/api/common' // 导入表格 api 方法生成器类
import { defaultOptButtons } from '/@/components/table' // 导入默认表格操作按钮数据:拖拽排序、编辑、删除按钮等

const tableRef = ref()

// 直接实例化 baTableClass 并传递各种参数
const baTable = new baTableClass(
    new baTableApi('/admin/user.user/'), // 一个 api 类的实例,快速生成一个控制器的 增、删、改、查、排序 接口的请求方法
    {
        // 表格列定义
        column: [
            { type: 'selection', align: 'center', operator: false },
            { label: 'ID', prop: 'id', align: 'center', operator: 'LIKE', operatorPlaceholder: '模糊查询', width: 70 },
            { label: '用户名', prop: 'username', align: 'center', operator: 'LIKE', operatorPlaceholder: '模糊查询' },
            // ...
            {
                label: '操作',
                align: 'center',
                width: '100',
                render: 'buttons',
                // 操作按钮传递的只是一个按钮配置数组,你也可以在渲染前对数组配置进行修改
                buttons: defaultOptButtons(['edit', 'delete']),
                operator: false,
            },
        ],
        // 不允许双击编辑的列的 prop
        dblClickNotEditColumn: [undefined],
        // ...属性很多,请参考本文下方的表格全部可用属性,或翻阅源代码(有注释)
    },
)

// 实例化表格后,将 baTable 的实例提供给上下文
provide('baTable', baTable)

// 相当于表格的 onMounted,也可以在页面的 onMounted 时执行
baTable.mount()

// 获取数据,可以在页面的 onMounted 时执行,也可以比如先请求一个API,再执行获取数据
baTable.getData()!.then(() => {
    // 查看请求完毕(已获取到表格数据)
    baTable.initSort() // 初始化默认排序(如果需要)
    baTable.dragSort() // 初始化拖拽排序(如果需要)
})
</script>
vue
<template>
    <!-- 可以直接在标签上使用 el-table 的属性和事件 -->
    <Table ref="tableRef">
        <template #columnPrepend>
            <el-table-column prop="prepend" label="第一个列" width="180" />
        </template>

        <!-- baTableClass 实例中定义的表格列会渲染在此处 -->
    </Table>

    <PopupForm />
</template>
vue
<!-- 独立的表单组件,以便二次开发 -->
<!-- 表格表单组件,表单项组件(FormItem)文档:https://doc.buildadmin.com/senior/web/formItem.html -->

<template>
    <!-- 对话框表单 -->
    <!-- 建议使用 Prettier 格式化代码 -->
    <!-- el-form 内可以混用 el-form-item、FormItem、ba-input 等输入组件 -->
    <el-dialog
        class="ba-operate-dialog"
        :close-on-click-modal="false"
        :model-value="['Add', 'Edit'].includes(baTable.form.operate!)"
        @close="baTable.toggleForm"
        width="50%"
    >
        <template #header>
            <div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
                {{ baTable.form.operate ? t(baTable.form.operate) : '' }}
            </div>
        </template>
        <el-scrollbar v-loading="baTable.form.loading" class="ba-table-form-scrollbar">
            <div
                class="ba-operate-form"
                :class="'ba-' + baTable.form.operate + '-form'"
                :style="config.layout.shrink ? '' : 'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"
            >
                <el-form
                    v-if="!baTable.form.loading"
                    ref="formRef"
                    @submit.prevent=""
                    @keyup.enter="baTable.onSubmit(formRef)"
                    :model="baTable.form.items"
                    :label-position="config.layout.shrink ? 'top' : 'right'"
                    :label-width="baTable.form.labelWidth + 'px'"
                    :rules="rules"
                >
                    <FormItem
                        :label="t('test.string')"
                        type="string"
                        v-model="baTable.form.items!.string"
                        prop="string"
                        :placeholder="t('Please input field', { field: t('test.string') })"
                    />
                </el-form>
            </div>
        </el-scrollbar>
        <template #footer>
            <div :style="'width: calc(100% - ' + baTable.form.labelWidth! / 1.8 + 'px)'">
                <el-button @click="baTable.toggleForm()">{{ t('Cancel') }}</el-button>
                <el-button v-blur :loading="baTable.form.submitLoading" @click="baTable.onSubmit(formRef)" type="primary">
                    {{ baTable.form.operateIds && baTable.form.operateIds.length > 1 ? t('Save and edit next item') : t('Save') }}
                </el-button>
            </div>
        </template>
    </el-dialog>
</template>

<script setup lang="ts">
import type { FormInstance, FormItemRule } from 'element-plus'
import { inject, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import FormItem from '/@/components/formItem/index.vue'
import { useConfig } from '/@/stores/config'
import type baTableClass from '/@/utils/baTable'
import { buildValidatorData } from '/@/utils/validate'

const config = useConfig()
const formRef = ref<FormInstance>()
const baTable = inject('baTable') as baTableClass

const { t } = useI18n()

const rules: Partial<Record<string, FormItemRule[]>> = reactive({})
</script>

<style scoped lang="scss"></style>

表格列

此处特指实例化表格管家类(new baTable)时定义的列(column)数据,您还可以使用表格组件的插槽渲染列。

ts
import baTableClass from '/@/utils/baTable'
import { baTableApi } from '/@/api/common'

const baTable = new baTableClass(new baTableApi('table controller url'), {
    // 表格列数据,其中 type、align、operator 等称为列属性,所有可用属性可在下方查阅
    column: [
        { type: 'selection', align: 'center', operator: false },
    ]
})

表格列的可用属性

ts
interface TableColumn extends Partial<TableColumnCtx<TableRow>> {
    // 是否于表格显示此列
    show?: boolean
    // 渲染器组件名,即 \src\components\table\fieldRender\ 中的组件之一,也可以查看 TableRenderer 类型定义
    render?: TableRenderer
    // 值替换数据(字典数据),同时用于单元格渲染时和作为公共搜索下拉框数据,格式如:{ open: '开', close: '关', disable: '已禁用' }
    replaceValue?: Record<string, any>

    // render=slot 时,slot 的名称
    slotName?: string
    // render=customRender 时,要渲染的组件或已注册组件名称的字符串
    customRender?: string | Component
    // render=customTemplate 时,自定义渲染 html,应谨慎使用:请返回 html 内容,务必确保返回内容是 xss 安全的
    customTemplate?: (row: TableRow, field: TableColumn, value: any, column: TableColumnCtx<TableRow>, index: number) => string
    // 渲染前对字段值的预处理函数(对 el-table 的 formatter 扩展)
    formatter?: (row: TableRow, column: TableColumnCtx<TableRow>, cellValue: any, index: number) => any

    /**
     * 自定义单元格渲染属性(比如单元格渲染器内部的 tag、button 组件的属性,设计上不仅是组件属性,也可以自定义其他渲染相关属性)
     * 直接定义对应组件的属性 object,或使用一个函数返回组件属性 object
     */
    customRenderAttr?: {
        tag?: TableContextDataFun<TagProps>
        icon?: TableContextDataFun<InstanceType<typeof Icon>['$props']>
        image?: TableContextDataFun<ImageProps>
        switch?: TableContextDataFun<SwitchProps>
        tooltip?: TableContextDataFun<ElTooltipProps>
        [key: string]: any
    }

    // render=tag 时,el-tag 组件的 effect
    effect?: TagProps['effect']
    // render=tag 时,el-tag 组件的 size
    size?: TagProps['size']
    // render=url 时,链接的打开方式
    target?: '_blank' | '_self'
    // render=datetime 时,时间日期的格式化方式,字母可以自由组合:y=年,m=月,d=日,h=时,M=分,s=秒,默认:yyyy-mm-dd hh:MM:ss
    timeFormat?: string
    // render=buttons 时,操作按钮数组
    buttons?: OptButton[]

    /**
     * 单元格渲染器需要的其他任意自定义数据
     * 1. render=tag 时,可单独指定每个不同的值 tag 的 type 属性 { open: 'success', close: 'info', disable: 'danger' }
     */
    custom?: any

    // 默认值(单元格值为 undefined,null,'' 时取默认值,仅使用了 render 时有效)
    default?: any
    // 是否允许动态控制字段是否显示,默认为 true
    enableColumnDisplayControl?: boolean
    // 单元格渲染组件的 key,默认将根据列配置等属性自动生成(此 key 值改变时单元格将自动重新渲染)
    getRenderKey?: (row: TableRow, field: TableColumn, column: TableColumnCtx<TableRow>, index: number) => string

    // 公共搜索操作符,默认值为 = ,值为 false 禁用此字段公共搜索,支持的操作符见下类型定义
    operator?: boolean | OperatorStr
    // 公共搜索框的 placeholder
    operatorPlaceholder?: string | string[]
    // 公共搜索渲染方式,render=tag|switch 时公共搜索也会渲染为下拉,数字会渲染为范围筛选,时间渲染为时间选择器等
    comSearchRender?: 'remoteSelect' | 'select' | 'date' | 'customRender' | 'slot'
    // 公共搜索自定义组件/函数渲染
    comSearchCustomRender?: string | Component
    // 公共搜索自定义渲染为 slot 时,slot 的名称
    comSearchSlotName?: string
    // 公共搜索自定义渲染时,外层 el-col 的属性(仅 customRender、slot 支持)
    comSearchColAttr?: Partial<ColProps>
    // 公共搜索是否显示字段的 label
    comSearchShowLabel?: boolean
    // 公共搜索渲染为远程下拉时,远程下拉组件的必要属性
    remote?: {
        pk?: string
        field?: string
        params?: anyObj
        multiple?: boolean
        remoteUrl: string
    }

    // 使用了 render 属性时,渲染前对字段值的预处理方法(即将废弃,请使用兼容 el-table 的 formatter 函数代替)
    renderFormatter?: (row: TableRow, field: TableColumn, value: any, column: TableColumnCtx<TableRow>, index: number) => any
    // 渲染为 url 时的点击事件(即将废弃,请使用 el-table 的 @cell-click 或单元格自定义渲染代替)
    click?: (row: TableRow, field: TableColumn, value: any, column: TableColumnCtx<TableRow>, index: number) => any
}
ts
/**
 * 可用的表格单元格渲染器,以 ./src/components/table/fieldRender/ 目录中的文件名自动生成
 */
type TableRenderer =
    | 'buttons'
    | 'color'
    | 'customRender'
    | 'customTemplate'
    | 'datetime'
    | 'icon'
    | 'image'
    | 'images'
    | 'switch'
    | 'tag'
    | 'tags'
    | 'url'
    | 'slot'
ts
type OperatorStr =
    | 'eq' // 等于,默认值
    | 'ne' // 不等于
    | 'gt' // 大于
    | 'egt' // 大于等于
    | 'lt' // 小于
    | 'elt' // 小于等于
    | 'LIKE'
    | 'NOT LIKE'
    | 'IN'
    | 'NOT IN'
    | 'RANGE' // 范围,将生成两个输入框 以输入最小值和最大值
    | 'NOT RANGE'
    | 'NULL' // 是否为NULL,将生成单个复选框
    | 'NOT NULL'
    | 'FIND_IN_SET'
    // 不推荐使用的,因为部分符号不利于网络传输
    | '='
    | '<>'
    | '>'
    | '>='
    | '<'
    | '<='

自定义单元格渲染

我们预设了一系列的单元格渲染方式 image、switch、tag...,若它们不能满足您的需求,我们提供了另外四种方案,最终方案实现了完全的自定义渲染能力。

ts
import { TableColumnCtx } from 'element-plus'

// 在您使用预设的单元格渲染方案时(`switch|image|tag...`),可以在渲染前,通过函数对单元格值进行一次处理:
const baTable = new baTableClass(
    new baTableApi('admin/auth.Admin/'),
    {
        column: [
            {
                label: 'id',
                prop: 'id',
                render: 'tags',
                // >= v2.1.2 推荐直接使用 formatter,它是由 el-table-column 自带的,新版系统自定义的单元格渲染器也使用了它格式化
                formatter(row, column, cellValue, index) {
                    return cellValue + ' - 为该列所有值,加了个后缀'
                },
                // < v2.1.2 使用 renderFormatter
                renderFormatter: (row: TableRow, field: TableColumn, value: any, column: TableColumnCtx<TableRow>, index: number) => {
                    return value + ' - 为该列所有值,加了个后缀';
                }
            },
        ]
    }
)
vue
<!-- 此方案在 `BuildAdmin >= v2.0.0` 添加,实现了开发者最舒适的模式 -->
<template>
    <div class="default-main">
        <Table>
            <!-- 请注意 #test 它是自定义的插槽名称 -->
            <template #test>
                <!-- 在插槽内,您可以随意发挥,通常使用 el-table-column 组件 -->
                <el-table-column prop="username" label="用户名" width="180" />

                <!-- 还可以继续使用 el-table-column 组件本身的插槽 -->
                <el-table-column prop="id" label="ID" width="180">
                    <template #default="scope">
                        <b>{{ scope.row['id'] }}</b>
                        列宽度是:{{ scope.column.width }}
                    </template>
                </el-table-column>
            </template>
        </Table>
    </div>
</template>

<script setup lang="ts">
import baTableClass from '/@/utils/baTable'
const baTable = new baTableClass(
    new baTableApi('/admin/auth.Admin/'),
    {
        column: [
            // render: 'slot' 表示本列将使用 slot渲染
            // slotName: 'test' slot的名称
            { label: '管理员', render: 'slot', slotName: 'test', operator: 'LIKE' },
        ]
    }
)
// ...
</script>
ts
import { h, resolveComponent } from 'vue'

// renderId 即自定义的组件,你也可以直接单独建立一个 vue 文件导入使用
// 自定义组件可以接受五个 props,分别是:renderValue=单元格值,renderRow=当前行数据,renderField=当前列数据,renderColumn=当前列上下文数据,renderIndex=当前行号
const renderId = {
    render(context: TableRenderPublicInstance) {
        console.log(context.$attrs.renderRow, context.$attrs.renderField, context.$attrs.renderValue, context.$attrs.renderColumn, context.$attrs.renderIndex)
        
        // 使用原生元素渲染,如`div、h1、a`等,以下演示了将 单元格值 直接通过h1标签进行渲染
        return h('h1', { class: 'id-h1' }, context.$attrs.renderValue)

        // 使用vue组件定义进行渲染(导入的组件)
        return h(Foo, {onClick: () => {}}, context.$attrs.renderValue)

        // 使用vue组件定义进行渲染(全局注册的组件)
        return h(resolveComponent('el-xxx'), context.$attrs.renderValue)
    },
}

const baTable = new baTableClass(
    new baTableApi('/admin/auth.admin/'),
    {
        // id 字段将使用渲染函数进行渲染
        column: [
            { label: 'id', prop: 'id', render: 'customRender', customRender: h(renderId) },
        ]
    }
)
ts
// 自 v2.1.2 起,您可以直接建立 image、switch 等系统同级的单元格渲染器,全局使用
// 请查阅 /components/table/fieldRender/ 文件夹,其中的每个组件为一种单元格渲染器
// 组件名称即为渲染器名称,可直接将组件名于 baTable.column.render 配置使用
// 假设您参考已有渲染器建立了 /components/table/fieldRender/customRenderId.vue 组件,可如下,直接于 baTable.column 配置使用
// 建立了新的单元格渲染器后,重新执行 pnpm dev,系统还会自动更新可用渲染器的类型定义,以获得语法提示支持
// 此处不再提供渲染器内部的代码示例,因为系统已经自带了很多个定义好的,请直接复制文件->改名->修改->使用

const baTable = new baTableClass(
    new baTableApi('/admin/auth.admin/'),
    {
        column: [
            { label: 'id', prop: 'id', render: 'customRenderId' },
        ]
    }
)

自定义表格顶部按钮

有时您需要自定义表格顶部的按钮,请直接 点击此处查看示例代码,或者您可以参考 表格示例模块数据安全管理->数据回收站,我们在该管理功能内,通过插槽自定义了一个表格顶部的 还原 按钮(代码示例更完整)。

自定义表格行侧边按钮

表格行侧边的按钮组通过普通的 js 数组进行配置,所以自定义它非常容易,因为您只需对一个 js 数组进行增删改查操作;常用的按钮配置数组可通过 defaultOptButtons() 快速生成

ts
import baTableClass from '/@/utils/baTable'
import { defaultOptButtons } from '/@/components/table'

// defaultOptButtons 函数返回常用按钮数据
let optBtn: OptButton[] = defaultOptButtons(['edit', 'delete'])

// 给第一个按钮加上点击事件
optBtn[0].click = (row: TableRow, field: TableColumn) => {
    console.log('点击了按钮')
}

// 自定义一个新的按钮
let newButton: OptButton[] = [
    {
        // 渲染方式:tipButton=带tip的按钮,confirmButton=带确认框的按钮,moveButton=移动按钮
        render: 'tipButton',
        // 按钮名称,将作为触发表格内事件(onTableAction)时的事件名
        name: 'info',
        // 鼠标 hover 时的提示,可使用多语言翻译 key,比如 user.group
        title: '详情',
        // 直接在按钮内显示的文字,title 有值时可为空,可使用多语言翻译 key,比如 user.group
        text: '详情',
        // 按钮类型,请参考 element plus 的按钮类型
        type: 'primary',
        // 按钮 icon
        icon: 'fa fa-search-plus',
        class: 'table-row-info',
        // 自定义点击事件
        click: (row: TableRow, field: TableColumn) => {},
        // 按钮是否显示,请返回布尔值
        display: (row: TableRow, field: TableColumn) => {
            return true
        },
        // 按钮是否禁用,请返回布尔值
        disabled: (row: TableRow, field: TableColumn) => {
            return false
        },
        // 自定义其他 el-button 属性
        attr: {}
        // 是否禁用 title 提示,此值通常由系统动态调整以确保提示的显示效果
        disabledTip: false,
    },
]

// 新按钮合入到默认的按钮数组
optBtn = newButton.concat(optBtn)

// 实例化 baTable
const baTable = new baTableClass(new baTableApi('table controller url'), {
    // 表格列数据
    column: [
        { type: 'selection', align: 'center', operator: false },
        {
            label: '操作',
            align: 'center',
            width: 120,
            // 表格列渲染为 buttons
            render: 'buttons',
            // 按钮数据为上面组装好的 optBtn
            buttons: optBtn,
            operator: false,
        },
    ],
})

provide('baTable', baTable)

若以上方案不能满足您的需求或任有疑惑,还可以参考 表格示例模块数据安全管理->数据回收站,我们在该管理功能内,实例化 baTable 时定义了一个额外的表格侧边确认还原按钮。

表格操作前后置钩子

TIP

钩子不是事件,事件请参考 Element Plus 官方文档,如单击单元格、双击、勾选、鼠标 hover、右击、列宽度改变等事件 el-table 本身均以自带实现。

ts
// 前置钩子返回 false 可取消原操作

// 获取表格数据前的钩子
baTable.before.getData = () => {}
// 删除前的钩子
baTable.before.postDel = ({ ids }: { ids: string[] }) => {}
// 获取被编辑行数据前的钩子
baTable.before.getEditData = ({ id }: { id: string }) => {}
// 双击表格具体操作执行前钩子
baTable.before.onTableDblclick = ({ row, column }: { row: TableRow; column: TableColumn }) => {}
// 表单切换前钩子
baTable.before.toggleForm = ({ operate, operateIds }: { operate: string; operateIds: string[] }) => {}
// 表单提交前钩子
baTable.before.onSubmit = ({ formEl, operate, items }: { formEl: FormInstance | undefined; operate: string; items: anyObj }) => {}
// 表格内事件响应前钩子,event 有效值:selection-change=选中项改变,page-size-change=每页数量改变,current-page-change=翻页,sort-change=排序,edit=编辑,delete=删除,field-change=单元格值改变,com-search=公共搜索
baTable.before.onTableAction = ({ event, data }: { event: string; data: anyObj }) => {}
// 表格顶部菜单事件响应前钩子,event 有效值:refresh=刷新,edit=编辑,delete=删除,quick-search=快速查询,unfold=折叠/展开,change-show-column=调整列显示状态
baTable.before.onTableHeaderAction = ({ event, data }: { event: string; data: anyObj }) => {}
// 表格初始化前钩子
baTable.before.mount = () => {}

// getData 的别名
baTable.before.getIndex = () => {}
// getEditData 的别名
baTable.before.requestEdit = ({ id }: { id: string }) => {}
ts
// 后置钩子均无需返回值

// 请求到表格数据后钩子,此时 baTable.table.data 已赋值
baTable.after.getData = ({ res }: { res: ApiResponse }) => {}
// 删除请求后钩子
baTable.after.postDel = ({ res }: { res: ApiResponse }) => {}
// 获取到编辑行数据后钩子,此时 baTable.form.items 已赋值
baTable.after.getEditData = ({ res }: { res: ApiResponse }) => {}
// 双击单元格操作执行后钩子
baTable.after.onTableDblclick = ({ row, column }: { row: TableRow; column: TableColumn }) => {}
// 表单切换后钩子
baTable.after.toggleForm = ({ operate, operateIds }: { operate: string; operateIds: string[] }) => {}
// 表单提交后钩子
baTable.after.onSubmit = ({ res }: { res: ApiResponse }) => {}
// 表格内事件响应后钩子,event 有效值:selection-change=选中项改变,page-size-change=每页数量改变,current-page-change=翻页,sort-change=排序,edit=编辑,delete=删除,
baTable.after.onTableAction = ({ event, data }: { event: string; data: anyObj }) => {}
// 表格顶部菜单事件响应后钩子,event 有效值:refresh=刷新,edit=编辑,delete=删除,quick-search=快速查询,unfold=折叠/展开,change-show-column=调整列显示状态
baTable.after.onTableHeaderAction = ({ event, data }: { event: string; data: anyObj }) => {}

// getData 的别名
baTable.after.getIndex = ({ res }: { res: ApiResponse }) => {}
// getEditData 的别名
baTable.after.requestEdit = ({ res }: { res: ApiResponse }) => {}

表格管家类的深度理解

以往很多小伙伴不能掌握 baTable 类的精髓,我们认为它没有一个正式的中文名称是决定性原因之一,现在问题已经解决。

类如其名,作为表格的管家:

  1. 你可以找它拿数据,比如 表格行数据、当前被编辑行的数据、公共搜索表单数据、快速搜索关键词、当前表单操作标识、加载状态、页码、每页显示数 等,几乎与表格相关的所有数据都能从这里拿到
  2. 你可以找它更新数据,你能拿到的数据自然也能直接修改,这些数据通常都具备响应性
  3. 你可以找它做操作,比如 刷新表格、发起公共/快速搜索、调整排序、分页、打开编辑表单
  4. 监控与拦截操作,几乎所有事件均可拦截或监控,比如 获取表格数据前后、获取编辑行数据前后、双击单元格前后、打开/提交表单、刷新、删除
  5. 随时待命,通过表格管理类你可以在代码任何位置对任意属性进行修改,类似状态商店,且该类通过 provide 提供给子级组件,你可以随时 inject,而无需自行传递(就像 popupForm.vue 中那样)
  6. 高度可定制化,您可以随时重写它的方法,添加自定义属性(方便数据随整个类在上下文中流通),甚至可以直接继承它写个新类
  7. 它是一个 js 类实例,但更是表格状态商店,是表格事件巴士,是万能入口与工具库,它是真正的好管家。

以下是一些使用示例:

ts
// 您可以先设置好一些表格属性,再获取表格数据
// 表格的所有可用属性,在本文下方可以寻找到,您也可以直接阅读源代码
const example1 = () => {
    baTable.table.filter.limit = 20
    baTable.table.filter.page = 2
    baTable.table.filter!.search = [{field:'status',val:'1',operator:'=',render:'tags'}]
    baTable.getData() // 或 baTable.getIndex()
}
ts
// 数据加载完成后打开公共搜索
baTable.getData()?.then(() => {
    baTable.table.showComSearch = true
})
ts
/**
 * 利用钩子在修改数据前对数据进行预处理
 * getEditData 为获取到编辑行数据后钩子(老版本此钩子名为 requestEdit)
 * @param object.res 为请求被编辑数据时,接口的完整响应数据
 */
baTable.after.getEditData = ({ res }) => {
    if (res.code == 1 && baTable.form.items) {
        // 将被编辑行的标题置空
        baTable.form.items.title = ''

        // 如果被编辑行没有图标,给定一个默认图标
        if (!baTable.form.items.icon) {
            baTable.form.items.icon = 'fa fa-circle-o'
        }
    }
}
ts
import { auth } from '/@/utils/common'

// ... 略

// 重写表格内部验权方法
baTable.auth = (node) => {
    return auth({ name: '/admin/auth/group' })
}

// auth 是全局公共函数,它的文档请参考:https://doc.buildadmin.com/senior/web/utils.html
// baTable.auth 是表格内部鉴权方法,参数 node 是表格内部需要鉴权的节点,比如 add、edit、delete 等

表格所有可用属性

ts
// 一般通过 baTable.table 访问

interface BaTable {
    /**
     * 表格数据,通过 baTable.getData 获取
     * 刷新数据可使用 baTable.onTableHeaderAction('refresh', { event: 'custom' })
     */
    data?: TableRow[]

    /**
     * 表格列定义
     */
    column: TableColumn[]

    /**
     * 获取表格数据时的过滤条件(含公共搜索、快速搜索、分页、排序等数据)
     * 公共搜索数据可使用 baTable.setComSearchData 和 baTable.getComSearchData 进行管理
     */
    filter?: {
        page?: number
        limit?: number
        order?: string
        quickSearch?: string
        // comSearchData 类型定义请参考旁边的 杂项 选项卡,数据示例直接于后台发起搜索从网络面板查看
        search?: comSearchData[]
        [key: string]: any
    }

    /**
     * 不需要双击编辑的字段,type=selection 的列为 undefined
     * 禁用全部列的双击编辑,可使用 ['all']
     */
    dblClickNotEditColumn?: (string | undefined)[]

    /**
     * 表格扩展数据,随意定义,以便一些自定义数据可以随 baTable 实例传递
     */
    extend?: anyObj

    // 表格 ref,通常在 页面 onMounted 时赋值,可选的
    ref?: typeof Table
    // 表格对应数据表的主键字段
    pk?: string
    // 路由 remark,后台菜单规则备注信息
    remark?: string | null
    // 表格加载状态
    loading?: boolean
    // 当前选中行
    selection?: TableRow[]
    // 数据总量
    total?: number
    // 默认排序字段和排序方式
    defaultOrder?: { prop: string; order: string }
    // 拖动排序限位字段,例如拖动行 pid=1,那么拖动目的行 pid 也需要为 1
    dragSortLimitField?: string
    // 接受 url 的 query 参数并自动触发公共搜索
    acceptQuery?: boolean
    // 显示公共搜索
    showComSearch?: boolean
    // 是否展开所有子项,树状表格专用属性
    expandAll?: boolean
    // 当前表格所在页面的路由 path
    routePath?: string
}
ts
// 一般通过 baTable.form 访问

interface BaTableForm {
    /**
     * 当前表单项数据
     */
    items?: anyObj

    /**
     * 当前操作标识:Add=添加,Edit=编辑
     */
    operate?: string

    /**
     * 添加表单字段默认值,打开表单时会使用 cloneDeep 赋值给 this.form.items 对象
     */
    defaultItems?: anyObj

    /**
     * 表单扩展数据,可随意定义,以便一些自定义数据可以随 baTable 实例传递
     */
    extend?: anyObj

    // 表单 ref,实例化表格时通常无需传递
    ref?: FormInstance | undefined
    // 表单项 label 的宽度
    labelWidth?: number
    // 被操作数据ID,支持批量编辑:add=[0],edit=[1,2,n]
    operateIds?: string[]
    // 提交按钮状态
    submitLoading?: boolean
    // 表单加载状态
    loading?: boolean
}
ts
// 一般通过 baTable.comSearch 访问

interface ComSearch {
    /** 表单项数据 */
    form: anyObj
    /** 字段搜索配置,搜索操作符(operator)、字段渲染方式(render)等 */
    fieldData: Map<string, any>
}
ts
// 一般通过 baTable.table.column 访问

interface TableColumn extends Partial<TableColumnCtx<TableRow>> {
    // 是否于表格显示此列
    show?: boolean
    // 渲染器组件名,即 \src\components\table\fieldRender\ 中的组件之一,也可以查看 TableRenderer 类型定义
    render?: TableRenderer
    // 值替换数据(字典数据),同时用于单元格渲染时和作为公共搜索下拉框数据,格式如:{ open: '开', close: '关', disable: '已禁用' }
    replaceValue?: Record<string, any>

    // render=slot 时,slot 的名称
    slotName?: string
    // render=customRender 时,要渲染的组件或已注册组件名称的字符串
    customRender?: string | Component
    // render=customTemplate 时,自定义渲染 html,应谨慎使用:请返回 html 内容,务必确保返回内容是 xss 安全的
    customTemplate?: (row: TableRow, field: TableColumn, value: any, column: TableColumnCtx<TableRow>, index: number) => string
    // 渲染前对字段值的预处理函数(对 el-table 的 formatter 扩展)
    formatter?: (row: TableRow, column: TableColumnCtx<TableRow>, cellValue: any, index: number) => any

    /**
     * 自定义单元格渲染属性(比如单元格渲染器内部的 tag、button 组件的属性,设计上不仅是组件属性,也可以自定义其他渲染相关属性)
     * 直接定义对应组件的属性 object,或使用一个函数返回组件属性 object
     */
    customRenderAttr?: {
        tag?: TableContextDataFun<TagProps>
        icon?: TableContextDataFun<InstanceType<typeof Icon>['$props']>
        image?: TableContextDataFun<ImageProps>
        switch?: TableContextDataFun<SwitchProps>
        tooltip?: TableContextDataFun<ElTooltipProps>
        [key: string]: any
    }

    // render=tag 时,el-tag 组件的 effect
    effect?: TagProps['effect']
    // render=tag 时,el-tag 组件的 size
    size?: TagProps['size']
    // render=url 时,链接的打开方式
    target?: '_blank' | '_self'
    // render=datetime 时,时间日期的格式化方式,字母可以自由组合:y=年,m=月,d=日,h=时,M=分,s=秒,默认:yyyy-mm-dd hh:MM:ss
    timeFormat?: string
    // render=buttons 时,操作按钮数组
    buttons?: OptButton[]

    /**
     * 单元格渲染器需要的其他任意自定义数据
     * 1. render=tag 时,可单独指定每个不同的值 tag 的 type 属性 { open: 'success', close: 'info', disable: 'danger' }
     */
    custom?: any

    // 默认值(单元格值为 undefined,null,'' 时取默认值,仅使用了 render 时有效)
    default?: any
    // 是否允许动态控制字段是否显示,默认为 true
    enableColumnDisplayControl?: boolean
    // 单元格渲染组件的 key,默认将根据列配置等属性自动生成(此 key 值改变时单元格将自动重新渲染)
    getRenderKey?: (row: TableRow, field: TableColumn, column: TableColumnCtx<TableRow>, index: number) => string

    // 公共搜索操作符,默认值为 = ,值为 false 禁用此字段公共搜索,支持的操作符见下类型定义
    operator?: boolean | OperatorStr
    // 公共搜索框的 placeholder
    operatorPlaceholder?: string | string[]
    // 公共搜索渲染方式,render=tag|switch 时公共搜索也会渲染为下拉,数字会渲染为范围筛选,时间渲染为时间选择器等
    comSearchRender?: 'remoteSelect' | 'select' | 'date' | 'customRender' | 'slot'
    // 公共搜索自定义组件/函数渲染
    comSearchCustomRender?: string | Component
    // 公共搜索自定义渲染为 slot 时,slot 的名称
    comSearchSlotName?: string
    // 公共搜索自定义渲染时,外层 el-col 的属性(仅 customRender、slot 支持)
    comSearchColAttr?: Partial<ColProps>
    // 公共搜索是否显示字段的 label
    comSearchShowLabel?: boolean
    // 公共搜索渲染为远程下拉时,远程下拉组件的必要属性
    remote?: {
        pk?: string
        field?: string
        params?: anyObj
        multiple?: boolean
        remoteUrl: string
    }

    // 使用了 render 属性时,渲染前对字段值的预处理方法(即将废弃,请使用兼容 el-table 的 formatter 函数代替)
    renderFormatter?: (row: TableRow, field: TableColumn, value: any, column: TableColumnCtx<TableRow>, index: number) => any
    // 渲染为 url 时的点击事件(即将废弃,请使用 el-table 的 @cell-click 或单元格自定义渲染代替)
    click?: (row: TableRow, field: TableColumn, value: any, column: TableColumnCtx<TableRow>, index: number) => any
}
ts
// 一般通过 baTable.table.filter!.search 访问

/**
 * 公共搜索事件返回的 Data
 */
interface comSearchData {
    field: string
    val: string | string[] | number | number[]
    operator: string
    render?: string
}

表格公共搜索

表格公共搜索是在实例化 baTable 时定义的,表格列的 render、operator、comSearchRender 属性共同参与公共搜索框的渲染。comSearchRender 支持 remoteSelect、select、date、customRender、slot,详细使用方法请直接查看下方示例代码。

公共搜索输入组件渲染示意表

renderoperatorcomSearchRender公共搜索渲染
任意false不渲染
任意RANGE、NOT RANGE生成两个输入框(A和B),可输入从A到B的范围值
任意NULL、NOT NULL生成一个复选框,勾选则搜索值为NULL、NOT NULL的情况
datetimeRANGE、NOT RANGE时间范围选择器
datetime=、>、<等比较符号,请参考 表格列operator 属性时间选择器
tag=、>、<等比较符号生成下拉框,下拉列表定义请参考 表格列replaceValue
switch无需设定生成下拉框: 1=开,0=关,也可以通过设置 表格列replaceValue 自定义值
任意表格列 的其他 operator 属性生成普通字符串输入框
任意=、>、< 等比较符号remoteSelect远程下拉
任意=、>、< 等比较符号select下拉框
任意无需设定date日期选择器(纯日期无时间)
datetimeRANGE、NOT RANGEdate日期范围选择器(纯日期无时间)
任意任意customRender自定义渲染组件,请参考下方示例

公共搜索配置示例代码

ts
import baTableClass from '/@/utils/baTable'
import { baTableApi } from '/@/api/common'

const baTable = new baTableClass(new baTableApi('table controller url'), {
    // 定义表格列数据、同时定义公共搜索数据
    column: [
        // 关闭这个字段的公共搜索
        { type: 'selection', align: 'center', operator: false },
        // 此字段是模糊查找,并为公共搜索输入框设置了 placeholder
        { label: 'ID', prop: 'id', align: 'center', operator: 'LIKE', operatorPlaceholder: t('Fuzzy query'), width: 70 },
        // 此字段是图片,建议关闭公共搜索
        { label: '头像', prop: 'avatar', align: 'center', render: 'image', operator: false },
        // 此字段将生成一个下拉框选择进行搜索,拥有三个值
        {
            label: t('user.user.Gender'),
            prop: 'gender',
            align: 'center',
            render: 'tag',
            replaceValue: { '0': t('unknown'), '1': t('user.user.male'), '2': t('user.user.female') },
        },
        // 此字段将生成一个时间范围选择框,选择时间日期进行搜索
        { label: t('createtime'), prop: 'createtime', align: 'center', render: 'datetime', sortable: 'custom', operator: 'RANGE', width: 160 },
        // 此字段将生成一个日期范围选择框,选择日期进行搜索(请注意渲染还是 datetime 若需自定义单元格渲染请参考`表格列`一节)
        { label: t('updatetime'), prop: 'updatetime', align: 'center', render: 'datetime', sortable: 'custom', operator: 'RANGE', width: 160, comSearchRender: 'date' },
        // 另外一种在公共搜索渲染下拉框的办法
        {
            label: t('user.user.Gender'),
            prop: 'gender',
            align: 'center',
            comSearchRender: 'select',
            replaceValue: { '0': t('unknown'), '1': t('user.user.male'), '2': t('user.user.female') },
        },
        // 远程下拉选择框
        { label: '会员', prop: 'user_id', comSearchRender: 'remoteSelect', remote: {
            // 主键,下拉 value
            pk: 'id',
            // 字段,下拉 label
            field: 'username',
            // 远程接口URL
            // 比如想要获取 user(会员) 表的数据,后台`会员管理`控制器URL为`/index.php/admin/user.user/index`
            // 该URL地址通常等于对应后台管理功能的`查看`操作请求的URL
            remoteUrl: '/admin/user.user/index',
            // 额外的请求参数
            params: {},
        }},
    ]
})

自动获取表格筛选条件

有时您只需要带着一些筛选条件跳转到表格,并获取筛选后的数据,您可以直接参考会员管理->编辑会员资料->调整余额;我们在跳转到余额日志管理时,携带了 user_id 参数,该参数由于和表格字段对应,所以会自动获取到并作为公共搜索的筛选条件。

ts
import router from '/@/router/index'

// 跳转到 user/moneyLog 页面,并携带 user_id 参数
router.push({
    name: 'user/moneyLog',
    query: {
        // user_id 字段存在于 user/moneyLog 页面表格的公共搜索中
        // user/moneyLog 页面内的表格公共搜索,会自动获取下列参数,如果存在,则自动填充并搜索
        user_id: baTable.form.items!.id,
    },
})

完全自定义公共搜索的渲染

vue
// 此方案在 `BuildAdmin >= v2.0.0` 添加,实现了开发者最舒适的模式
<template>
    <div class="default-main">
        <TableHeader
            :buttons="['refresh', 'add', 'edit', 'delete', 'comSearch', 'quickSearch', 'columnDisplay']"
            :quick-search-placeholder="t('Quick search placeholder', { fields: t('auth.admin.username') + '/' + t('auth.admin.nickname') })"
        >
            <!-- 请注意 #test 它是自定义的插槽名称 -->
            <template #test>
                <!-- 在插槽内,您可以随意发挥,通常渲染一个输入框供用户输入内容 -->
                <!-- 输入组件的 v-model="baTable.comSearch.form[item.prop!]" 即可在baTable上下文获取用户输入的关键词 -->
                我是公共搜索的slot渲染内容
            </template>
        </TableHeader>

        <Table />
    </div>
</template>

<script setup lang="ts">
import baTableClass from '/@/utils/baTable'
const baTable = new baTableClass(
    new baTableApi('/admin/auth.Admin/'),
    {
        column: [
            // comSearchRender: 'slot' 表示本字段的公共搜索将使用 slot渲染
            // comSearchSlotName: 'test' slot的名称
            { label: '管理员', prop: 'username', operator: 'LIKE', comSearchRender:'slot', comSearchSlotName: 'test' },
        ]
    }
)
// ...
</script>
ts
import { h, resolveComponent } from 'vue'

// searchId 即自定义的组件,你也可以直接单独建立一个 vue 文件导入使用
// 自定义组件可以接受三个 props,分别是:renderRow=当前行数据,renderField=当前列数据,renderValue=搜索框绑定值
const searchId = {
    render(context: TableRenderPublicInstance) {
        console.log(context.$attrs.renderRow, context.$attrs.renderField, context.$attrs.renderValue)
        
        // 使用原生元素渲染
        return h('input', { class: 'id-h1' }, context.$attrs.renderValue)

        // 使用vue组件定义进行渲染(全局注册的组件)
        return h(resolveComponent('el-input'), context.$attrs.renderValue)
    },
}

const baTable = new baTableClass(
    new baTableApi('/admin/auth.admin/'),
    {
        // id 字段的公共搜索将使用 searchId 组件进行渲染
        // 与单元格的完全自定义渲染只是属性名不同
        column: [
            { label: 'id', prop: 'id', comSearchRender: 'customRender', comSearchCustomRender: h(searchId) },
        ]
    }
)
vue

<!-- 1. 表格顶部菜单的 buttons 里边不要有 comSearch,表示不启用默认的公共搜索 -->
<TableHeader
    :buttons="['refresh', 'add', 'edit', 'delete', 'quickSearch', 'columnDisplay']"
    :quick-search-placeholder="t('Quick search placeholder', { fields: t('auth.admin.username') + '/' + t('auth.admin.nickname') })"
></TableHeader>

<!-- 2. 完整实现一个自定义的公共搜索组件并导入使用 -->
<!-- 3. 通过 `baTable.table.showComSearch` 来判断您自定义公共搜索组件的显示隐藏状态 -->
<ComSearch v-if="baTable.table.showComSearch"></ComSearch>

baTableApi 类

此类代码位于:\src\api\common.ts,它实现快速生成一个控制器的:增、删、改、查、排序 的操作 url 和请求方法,提供控制器的 url 即可。此类通常与 baTable 类搭配使用,若需单独定义 api 请求函数,可以直接在 \src\api 目录下定义,无需经过 baTableApi

使用示例

vue
<script>
import { baTableApi } from '/@/api/common' // 导入表格api方法生成器

// 请注意,baTableApi 类的方法通常都是由 baTable 类来使用的,并不是在外部示例化一个 baTableApi 类使用,以下仅为方便读者理解它是什么

let baApi = new baTableApi('/admin/user.user/')
baApi.index({}) // 请求查看,参数为筛选条件,具体使用请参考baTable类
baApi.edit({}) // 请求编辑数据,参数为被编辑项id等
baApi.del({}) // 请求删除,参数为被删除数据的ids
// ...
</script>

表格顶部组件

TableHeader 组件内含有公共搜索和表头操作按钮,菜单按钮可以自动根据当前路由进行 鉴权,当前管理员无权限的按钮,则不会显示。

属性列表

属性名注释
buttons要显示的按钮数组,比如 ['refresh', 'add', 'comSearch'],即表示于表格顶部显示 刷新、添加、公共搜索 按钮
quick-search-placeholder快速搜索输入框的 placeholder

插槽列表

插槽名注释
refreshPrepend刷新按钮前插槽
refreshAppend刷新按钮后插槽
此插槽内容将放置在组件内置的菜单按钮之后,可自定义表格顶部按钮等
quickSearchPrepend快速搜索前插槽
任意列配置中使用 comSearchRender:'slot', comSearchSlotName: 插槽名称来通过slot渲染公共搜索

支持的菜单按钮

菜单按钮注释
refresh刷新按钮
add添加
edit编辑
delete删除
comSearch公共搜索
quickSearch快速搜索
columnDisplay字段显示状态
unfold展开/折叠,与 baTable.table.expandAll 属性关联

表格顶部菜单示例代码

src/views/backend/security/dataRecycleLog/index.vue 文件

vue
<template>
    <!-- buttons 属性定义了 TableHeader 本身支持的顶部按钮,仅需传递按钮名即可 -->
    <TableHeader
        :buttons="['refresh', 'add', 'edit', 'delete', 'comSearch', 'quickSearch', 'columnDisplay', 'unfold']"
        :quick-search-placeholder="t('Quick search placeholder', { fields: t('security.dataRecycleLog.Rule name') })"
    >
        <!-- 可以在此处以插槽的方式设置一些自定义按钮 -->
        
        <template #refreshPrepend>
            <!-- 刷新按钮前插槽内容 -->
        </template>

        <!-- 默认插槽 -->
        <template #default>
            <el-button v-blur :disabled="baTable.table.selection!.length > 0 ? false:true" class="table-header-operate" type="success">
                <Icon color="#ffffff" name="el-icon-RefreshRight" />
                <span class="table-header-operate-text">还原</span>
            </el-button>
        </template>

    </TableHeader>
</template>

<script>
import TableHeader from '/@/components/table/header/index.vue'
</script>

常见问题

如何在一个页面渲染两个表格?

由于表格数据来自 baTable 实例,并且通过 provideinject 将整个实例传递给组件上下文,所以,两个表格不能共处于一个 vue 文件中,您可以另外建立 vue 文件,在其中写好新的表格,然后导入使用即可。

如何禁用列的双击编辑功能?

我们可以通过添加列的 propbaTabledblClickNotEditColumn数组 来禁用对应列的双击编辑功能,当我们想禁用全部列的双击编辑功能时只需要定义 dblClickNotEditColumn: ['all'] 即可。