Skip to content

表格

我们封装了 Table 组件、表格管家、TableManagerAPI 类、表头组件等,以方便快速生成和制作表格。

Table 组件

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

组件属性

属性名注释
manager表格管家实例TableManagerInstance
pagination是否显示底部分页组件boolean
...其他 el-table 的属性可以直接使用-

组件方法与事件

方法名/项目注释
getElTableRef获取内部 el-tableref,通过它调用 el-table 的原有方法
toggleExpansionAll折叠/展开表格(树状表格特有)
其他各类事件单双击、勾选、hover、右击等事件 el-table 均以自带,请转到 el-table 事件文档

组件插槽

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

TIP

Table 组件和下方的表格管家是搭配使用的,管家为表格提供数据,并可以响应表格组件的各种事件,Table 组件的使用示例放在了下方表格管家示例一起。

表格管家

  • 代码位于:@/hooks/useTableManager,之所以没有放在 @/components/table/ 目录内,因为 table 组件还依赖 API 请求函数(放在了 @/api/table),拆开放是不可避免和合理的。
  • 我们在表格管家里面封装好了一个表格应有的 属性方法,预设了 查看前、查看后、编辑前 等钩子,几乎和表格相关的所有数据和操作都可以找它。

TIP

表格管家本质是一个工厂函数,非常强大和重要,我们在 表格管家的深度理解 一节对这一点进行了强调说明。

使用示例

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

切换以下代码块的 Tab 来查看不同文件的示例代码。

ts
<script setup lang="ts">
import { TableManagerAPI } from '@/api/table' // 导入表格 API 方法生成器类
import TableHeader from '@/components/table/header/index.vue' // 导入单独的表格表头组件
import { getDefaultOptButtons } from '@/components/table/index'
import Table from '@/components/table/index.vue' // 导入 Table 组件
import { useTableManager } from '@/hooks/useTableManager' // 导入表格管家
import { useI18n } from 'vue-i18n'
import DialogForm from './dialogForm.vue' // 独立表单组件,方便二开,它接受并使用了表格管家的数据、提交方法等,但并无强耦合

const { t } = useI18n()

// 获取默认的 [编辑, 删除] 操作按钮,按钮数据其实就是一个 ts 数组,可以随便改
const optButtons = getDefaultOptButtons(['edit', 'delete'])

const tableManager = useTableManager({
    // 一个 API 类的实例,该类可快速生成一个控制器的 增、删、改、查、排序 接口的请求方法
    api: new TableManagerAPI('/admin/test/'),
    table: {
        // 支持使用 [render: 'slot', slotName: '插槽名称'] 来指定插槽渲染,<Table> 组件本身也有一些其他可以放表格列的插槽
        column: [
            { type: 'selection', align: 'center', operator: false },
            { label: 'ID', prop: 'id', align: 'center', operator: 'BETWEEN', width: 70, sortable: 'custom' },
            {
                label: '用户名',
                prop: 'username',
                align: 'center',
                operator: 'ILIKE',
                sortable: false,
                quickSearch: true,
                showOverflowTooltip: true,
            },
            { label: '操作', align: 'center', width: 100, render: 'buttons', buttons: optButtons, operator: false },
        ],
        // 默认排序
        filter: {
            sort: 'id',
            order: 'desc',
        },
        // 不需要 【双击编辑】的列
        dblClickNotEditColumn: ['switch'],

        // ...... 属性很多,请参考本文下方的表格全部可用属性,或翻阅源代码(有注释)
    },
    form: {
        // 添加表单的字段默认值
        defaultItems: {
            string: 'test',
        },
    },
})

onMounted(() => {
    tableManager.table.ref = tableRef.value
    tableManager.initCtx()
    tableManager.getData()?.then(() => {
        // 拖拽排序需要在获取到表格数据之后,才能初始化
        // 如果需要拖拽排序则调用,否则可直接精简为 onMounted 之外执行 tableManager.initCtx() / tableManager.getData() 两行即可
        tableManager.initDragSort()
    })
})
</script>
vue
<template>
    <div class="default-main">
        <TableHeader
            :manager="tableManager"
            v-model:com-search="tableManager.comSearch"
            :buttons="['refresh', 'add', 'edit', 'delete', 'comSearch', 'quickSearch', 'columnDisplay']"
        />

        <!-- 可以直接在标签上使用 el-table 的属性和事件 -->
        <Table ref="tableRef" :manager="tableManager">
            <!-- 插槽语法自定义列 -->
            <template #columnPrepend>
                <el-table-column prop="prepend" label="第一个列" width="180" />
            </template>

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

        <DialogForm :manager="tableManager" v-model:form-items="tableManager.form.items!" />
    </div>
</template>
vue
<!-- 独立的表单组件 + 尽量原生的语法,以便二次开发 -->
<!-- 本组件接受表格管家实例,并使用了实例上的 `数据` 和 `表单提交方法`,并无强耦合 -->
<template>
    <el-dialog
        class="ag-operate-dialog"
        :close-on-click-modal="false"
        :model-value="['create', 'update'].includes(manager.form.operate!)"
        @close="manager.toggleForm"
        :destroy-on-close="true"
        :draggable="true"
    >
        <template #header>
            <div class="title">
                {{ manager.form.operate == 'create' ? t('common.add') : t('common.edit') }}
            </div>
        </template>
        <el-scrollbar v-loading="manager.form.loading" class="ag-table-form-scrollbar">
            <div
                class="ag-operate-form"
                :class="'ag-' + manager.form.operate + '-form'"
                :style="config.layout.shrink ? '' : 'width: calc(100% - ' + manager.form.labelWidth! / 2 + 'px)'"
            >
                <el-form
                    ref="formRef"
                    @keyup.enter="manager.submitForm(formRef)"
                    :model="formItems"
                    :label-position="config.layout.shrink ? 'top' : 'right'"
                    :label-width="manager.form.labelWidth + 'px'"
                    :rules="rules"
                    v-if="!manager.form.loading"
                >
                    <el-form-item :label="t('test.username')" prop="username">
                        <el-input v-model="formItems.username" :placeholder="t('common.pleaseEnter', { field: t('test.username') })" />
                    </el-form-item>

                    <el-form-item :label="t('common.weigh')" prop="weigh">
                        <el-input-number
                            class="w100"
                            controls-position="right"
                            v-model="formItems.weigh"
                            :placeholder="t('common.pleaseEnter', { field: t('common.weigh') })"
                        />
                    </el-form-item>
                </el-form>
            </div>
        </el-scrollbar>
        <template #footer>
            <div :style="'width: calc(100% - ' + manager.form.labelWidth! / 1.8 + 'px)'">
                <el-button @click="manager.toggleForm()">{{ t('common.cancel') }}</el-button>
                <el-button :loading="manager.form.submitLoading" @click="manager.submitForm(formRef)" type="primary">
                    {{ manager.form.operatePKs && manager.form.operatePKs.length > 1 ? t('common.saveAndContinue') : t('common.save') }}
                </el-button>
            </div>
        </template>
    </el-dialog>
</template>

<script setup lang="ts">
import { useConfig } from '@/stores/config'
import { buildValidatorRule } from '@/utils/validate'
import type { FormItemRule } from 'element-plus'
import { useTemplateRef } from 'vue'
import { useI18n } from 'vue-i18n'

interface Props {
    manager: TableManagerInstance
}

defineProps<Props>()
const formItems = defineModel<AnyObj>('formItems', { required: true })

const config = useConfig()
const formRef = useTemplateRef('formRef')

const { t } = useI18n()

const rules: Partial<Record<string, FormItemRule[]>> = {
    username: [buildValidatorRule({ name: 'required', title: t('test.username') })],
}
</script>

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

表格列

此处特指实例化表格管家(useTableManager())时定义的列(table.column)数据,如下:

ts
import { useTableManager } from '@/hooks/useTableManager'
import { TableManagerAPI } from '@/api/table'

const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: {
        // 表格列数据,其中 type、align、operator 等称为列属性,所有可用属性可在下方查阅
        // 支持使用 [render: 'slot', slotName: '插槽名称'] 来指定插槽渲染,<Table> 组件本身也有一些其他可以放表格列的插槽
        column: [
            { type: 'selection', align: 'center', operator: false },
            { label: 'ID', prop: 'id', align: 'center', operator: 'BETWEEN', width: 70, sortable: 'custom' },
            {
                label: '用户名',
                prop: 'username',
                align: 'center',
                operator: 'ILIKE',
                sortable: false,
                quickSearch: true,
                showOverflowTooltip: true,
            },
            { label: '操作', align: 'center', width: 140, render: 'buttons', buttons: optButtons, operator: false },
        ],
    },
})

表格列的可用属性

切换以下代码块的 Tab 来查看多份示例代码。

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

    // render=slot 时,slot 的名称
    slotName?: string
    // render=customRender 时,要渲染的组件或已注册组件名称的字符串
    customRender?: string | Component
    // render=customTemplate 时,自定义渲染 html,应谨慎使用: 请返回 html 内容,务必确保返回内容是 xss 安全的
    customTemplate?: (row: TableRow, columnConfig: TableColumn, column: TableColumnCtx<TableRow>, cellValue: any, 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>
        link?: TableContextDataFun<LinkProps>
        [key: string]: any
    }

    // render=buttons 时,按钮数据数组
    buttons?: OptButton[]

    /**
     * 单元格渲染器需要的其他任意自定义数据
     * 1. render=tag 时,可单独指定每个不同的值 tag 的 type 属性 { open: 'success', close: 'info', disable: 'danger' }
     * 2. render=datetime 时,可指定时间日期的格式化模板(dayjs().format 模板,如 { format: 'YYYY-MM-DD HH:mm:ss' })
     */
    custom?: {
        format?: string
        [key: string]: any
    }

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

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

        remoteURL: string
        remoteParams?: AnyObj
        remoteSearchFields?: string[]
    }
}
ts
/**
 * 可用的表格单元格渲染器,以 ./src/components/table/cellRenderer/ 目录中的文件名自动生成
 */
type TableCellRenderer =
    | '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'
    | 'ILIKE'
    | 'NOT ILIKE'
    | 'IN'
    | 'NOT IN'
    | 'BETWEEN' // 范围,将生成两个输入框,可以输入最小值和最大值
    | 'NOT BETWEEN'
    | 'NULL' // 是否为NULL,将生成单个复选框
    | 'NOT NULL'

自定义单元格渲染

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

切换以下代码块的 Tab 来查看多份示例代码。

ts
import { TableManagerAPI } from '@/api/table'
import { useTableManager } from '@/hooks/useTableManager'

// 在您使用预设的单元格渲染方案时(`switch|image|tag...`),可以在渲染前,通过函数对单元格值进行一次处理:
const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: {
        column: [
            {
                label: 'ID',
                prop: 'id',
                align: 'center',
                width: 70,
                formatter(row, column, cellValue, index) {
                    return cellValue + ' - 为该列所有值,加了个后缀'
                },
            },
        ],
    },
})
vue
<template>
    <div class="default-main">
        <Table ref="tableRef" :manager="tableManager">
            <!-- 请注意 #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">
const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: {
        column: [
            { label: '管理员', render: 'slot', slotName: 'test', operator: 'LIKE' },
        ],
    },
})
</script>
ts
import { h, resolveComponent } from 'vue'

// renderId 即自定义的组件,你也可以直接单独建立一个 vue 文件导入使用
// 自定义组件可以接受五个 props,分别是:renderRow=当前行数据,renderColumnConfig=当前列配置数据,renderValue=单元格值,renderColumn=当前列上下文数据,renderIndex=当前行号,manager=表格管家实例
const renderId = {
    render(context: any) {
        console.log(
            context.$attrs.renderRow,
            context.$attrs.renderColumnConfig,
            context.$attrs.renderValue,
            context.$attrs.renderColumn,
            context.$attrs.renderIndex,
            context.$attrs.manager
        )

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

const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: {
        column: [
            { label: 'id', prop: 'id', render: 'customRenderId' }
        ],
    },
})

自定义表格顶部按钮

有时您需要自定义表格顶部的按钮,请直接 点击此处查看示例代码

自定义表格行侧边按钮

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

切换以下代码块的 Tab 来查看多份示例代码。

ts
import { TableManagerAPI } from '@/api/table'
import { useTableManager } from '@/hooks/useTableManager'
import { getDefaultOptButtons } from '@/components/table/index'

// getDefaultOptButtons 函数返回默认按钮数据
const optButtons = getDefaultOptButtons(['edit', 'delete'])

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

// 自定义一个新的按钮
let newButton: OptButton = {
    render: 'tip',
    name: 'copy',
    icon: 'lucide-database-arrow-down',
    type: 'success',
    title: '复制',
    class: 'table-row-copy',
    click: (row) => {
        console.log(row)
    },
}

// 新按钮合入到默认的按钮数组
optButtons.push(newButton)

const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: {
        column: [{ label: t('common.operate'), align: 'center', width: 100, render: 'buttons', buttons: optButtons, operator: false }],
    },
})

tableManager.initCtx()
tableManager.getData()
ts
interface OptButton {
    /**
     * 渲染方式:basic=普通按钮,tip=带提示的按钮,confirm=带确认框的按钮,sort=拖动排序按钮
     */
    render: 'basic' | 'tip' | 'confirm' | 'sort'
    /**
     * 按钮名称,同时将作为触发表格内事件(onTableAction)时的事件名
     */
    name: string
    /**
     * 鼠标 hover 时的提示
     */
    title?: string
    /**
     * 直接在按钮内显示的文字,可为空
     */
    text?: string
    /**
     * 自定义按钮的点击事件
     * @param row 当前行数据
     * @param column 当前列数据
     */
    click?: (row: TableRow, column: TableColumn) => void
    /**
     * 按钮是否显示(请返回布尔值,比如: display: auth('create'))
     * @param row 当前行数据
     * @param column 当前列数据
     */
    display?: (row: TableRow, column: TableColumn) => boolean
    /**
     * 按钮是否禁用(请返回布尔值)
     * @param row 当前行数据
     * @param column 当前列数据
     */
    disabled?: (row: TableRow, column: TableColumn) => boolean
    /**
     * 按钮是否正在加载中(请返回布尔值)
     * @param row 当前行数据
     * @param column 当前列数据
     */
    loading?: (row: TableRow, column: TableColumn) => boolean
    /**
     * 自定义 el-button 的其他属性(格式为属性 object 或一个返回属性 object 的函数)
     */
    attr?: TableContextDataFun<ButtonProps>
    // 按钮 class
    class?: string
    // 按钮 type
    type: ButtonType
    // 按钮 icon 的名称
    icon: string
    // 确认按钮的气泡确认框的属性(el-popconfirm 的属性,格式为属性 object 或一个返回属性 object 的函数)
    popconfirm?: TableContextDataFun<PopconfirmProps>
    // 是否禁用 title 提示,此值通常由系统动态调整以确保提示的显示效果
    disabledTip?: boolean
}

手动拼装表格筛选数据

TIP

  1. 公共搜索表单数据 != 查询筛选数据,它们可以分别设定。
  2. 预设的条件数据可能被覆盖,比如用户 点击公共搜索、使用快速搜索 时,为保障用户实时需求,会直接覆盖所有筛选条件,如需固定筛选数据,首选服务端,其次是利用钩子函数。

切换以下代码块的 Tab 来查看多份示例代码。

ts
import { TableManagerAPI } from '@/api/table'
import { useTableManager } from '@/hooks/useTableManager'

const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: {column: []},
})

// 先调用管家的基础上下文初始化函数
tableManager.initCtx()

// 准备一个 wheres 容器
const groups: WhereGroup[] = []

// 设定【公共搜索表单数据】
// id 字段是范围筛选,以逗号分割多个值,此处支持同时传递多个字段的值
tableManager.setComSearchData({ id: '1,2' })

// 获取【公共搜索表单数据】,结果为拼接好的 where 数据(自动根据列的 operator 和 comSearchRender 组装好对应的 where)
const com = tableManager.getComSearchData()
if (com !== false) {
    // 将 where 数据合入容器
    groups.push(com)
}

// 如果有快速搜索关键词
if (tableManager.table.filter!.quickSearchKeywords) {
    const quick = tableManager.getQuickSearchData(tableManager.table.filter!.quickSearchKeywords)
    if (quick !== false) {
        // 将快速搜索的 where 数据合入容器
        groups.push(quick)
    }
}

// 设定列表请求时的 where 数据
tableManager.setFilterWheres(groups)

// 执行查看请求
tableManager.getData()
ts
import { TableManagerAPI } from '@/api/table'
import { useTableManager } from '@/hooks/useTableManager'

const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: {
        column: [],
        filter: {
            // 大组,大组与大组之间固定使用 AND 连接
            wheres: [
                {
                    // 小组,小组与小组之间可以指定连接符号,通过如下 or 属性
                    wheres: [
                        {
                            field: 'id',
                            value: [1, 2],
                            operator: 'IN',
                        },
                    ],
                    or: false,
                },
            ],
        },
    },
    form: {},
})
ts
import { TableManagerAPI } from '@/api/table'
import { useTableManager } from '@/hooks/useTableManager'

const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: {
        column: [],
        filter: {
            // 排序字段
            sort: 'id',
            // 排序方式
            order: 'desc',
            wheres: [
                // 第一大组 where
                {
                    wheres: [
                        {
                            field: 'id',
                            value: [1, 2],
                            operator: 'IN',
                        },
                        {
                            field: 'string',
                            value: '字符串1',
                            operator: 'ILIKE',
                        },
                    ],
                    // 第一大组 where 内的多个 where 之间是否使用 OR 连接: false=不使用,true=使用
                    or: false,
                },

                // 第二大组 where,与第一组的查询连接符号固定为 AND(即大组之间不能指定连接符号,但小组之间可以自定义)
                {
                    wheres: [
                        {
                            field: 'id',
                            value: [1, 2],
                            operator: 'IN',
                        },
                        {
                            field: 'string',
                            value: '字符串1',
                            operator: 'ILIKE',
                        },
                    ],
                    // 第二大组 where 内的多个 where 之间是否使用 OR 连接: false=不使用,true=使用
                    or: true,
                },
            ],
        },
    },
    form: {},
})

表格操作前后置钩子

TIP

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

切换以下代码块的 Tab 来查看多份示例代码。

ts
const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: { column: [] },
})

// 获取表格数据前的钩子
tableManager.opts.before!.getData = () => {
    tableManager.table.expandAll = tableManager.table.filter!.quickSearchKeywords ? true : false
}

// 获取到编辑行数据后钩子
tableManager.opts.after!.getEditData = () => {
    if (tableManager.form.items && !tableManager.form.items.icon) {
        tableManager.form.items.icon = 'lucide-circle-small'
    }
}
ts
interface TableManagerBefore {
    /**
     * 获取表格数据前的钩子(返回 false 可取消原操作)
     */
    getData?: () => boolean | void

    /**
     * 获取被编辑行数据前的钩子(返回 false 可取消原操作)
     * @param object.pk 被编辑行主键
     */
    getEditData?: ({ pk }: { pk: string }) => boolean | void

    /**
     * 删除前的钩子(返回 false 可取消原操作)
     * @param object.pks 被删除数据的主键集合
     */
    delete?: ({ pks }: { pks: string[] }) => boolean | void

    /**
     * 双击表格具体操作执行前钩子(返回 false 可取消原操作)
     * @param object.row 被双击行数据
     * @param object.column 被双击列数据
     */
    columnDblclick?: ({ row, column }: { row: TableRow; column: TableColumn }) => boolean | void

    /**
     * 表单切换前钩子(返回 false 可取消默认行为)
     * @param object.operate 当前操作标识:create=添加,update=更新
     * @param object.operatePKs 被操作的行的主键集合
     */
    toggleForm?: ({ operate, operatePKs }: { operate: string; operatePKs: string[] }) => boolean | void

    /**
     * 表单提交前钩子(返回 false 可取消原操作)
     * @param object.formEl 表单组件ref
     * @param object.operate 当前操作标识:create=添加,update=更新
     * @param object.items 表单数据
     */
    submitForm?: ({ formEl, operate, items }: { formEl?: FormInstance | null; operate: string; items: AnyObj }) => boolean | void

    /**
     * 表格内事件响应前钩子(返回 false 可取消原操作)
     * @param object.event 事件名称
     * @param object.data 事件携带的数据
     */
    tableEvent?: ({ event, data }: { event: TableEventName; data: AnyObj }) => boolean | void

    /**
     * 表格上下文数据初始化前钩子
     */
    initCtx?: () => boolean | void

    // 可自定义其他钩子
    [key: string]: Function | undefined
}
ts
interface TableManagerAfter {
    /**
     * 请求到表格数据后钩子
     * 此时 useTableManager.table.data 已赋值
     * @param object.res 请求完整响应
     */
    getData?: ({ res }: { res: ApiResponse }) => void

    /**
     * 获取到编辑行数据后钩子
     * 此时 useTableManager.form.items 已赋值
     * @param object.res 请求完整响应
     */
    getEditData?: ({ res }: { res: ApiResponse }) => void

    /**
     * 删除请求后钩子
     * @param object.res 请求完整响应
     */
    delete?: ({ res }: { res: ApiResponse }) => void

    /**
     * 双击单元格操作执行后钩子
     * @param object.row 当前行数据
     * @param object.column 当前列数据
     */
    columnDblclick?: ({ row, column }: { row: TableRow; column: TableColumn }) => void

    /**
     * 表单切换后钩子
     * @param object.operate 当前操作标识:create=添加,update=更新
     * @param object.operatePKs 被操作的主键集合
     */
    toggleForm?: ({ operate, operatePKs }: { operate: string; operatePKs: string[] }) => void

    /**
     * 表单提交后钩子
     * @param object.res 请求完整响应
     */
    submitForm?: ({ res }: { res: ApiResponse }) => void

    /**
     * 表格内事件响应后钩子
     * @param object.event 事件名称
     * @param object.data 事件携带的数据
     */
    tableEvent?: ({ event, data }: { event: TableEventName; data: AnyObj }) => void

    // 可自定义其他钩子
    [key: string]: Function | undefined
}

表格管家的深度理解

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

  1. 你可以找它拿数据,比如 表格行数据、当前被编辑行的数据、公共搜索表单数据、快速搜索关键词、当前表单操作标识、加载状态、页码、每页显示数 等,几乎与表格相关的所有数据都能从这里拿到。
  2. 你可以找它更新数据,你能拿到的数据自然也能直接修改,这些数据通常都具备响应性。
  3. 你可以找它做操作,比如 刷新表格、发起公共/快速搜索、调整排序、分页、打开编辑表单 等。
  4. 监控与拦截操作,几乎所有事件均可拦截或监控,比如 获取表格数据前后、获取编辑行数据前后、双击单元格前后、打开/提交表单、刷新、删除 等,除我们定义的钩子外,el-table 也内置有各种单击、双击、hover 事件。
  5. 随时待命,通过表格管家实例你可以在代码任何位置对任意属性进行修改。
  6. 高度可定制化,您可以随时重写它的方法,追加自定义扩展数据(方便数据随整个类在上下文中流通)等,想怎么改就怎么改。
  7. 它是一个 ts 工厂函数,但更是表格状态商店,是表格事件巴士,是万能入口与工具库,它是真正的好管家。

以下是一些使用示例:

ts
// 您可以先设置好一些表格属性,再获取表格数据
// 表格所有可用属性本文下方有:https://doc.ai-go-hub.com/senior/web/table.html#%E8%A1%A8%E6%A0%BC%E6%89%80%E6%9C%89%E5%8F%AF%E7%94%A8%E5%B1%9E%E6%80%A7
const example1 = () => {
    // 修改显示条数和页码
    tableManager.table.filter!.limit = 20
    tableManager.table.filter!.page = 2

    // 仅示例,实际场景中【设置查看请求的筛选数据】是自动完成的
    const com = tableManager.getComSearchData() // 获取当前公共搜索数据
    if (com !== false) {
        tableManager.setFilterWheres([com]) // 设置查看请求的筛选数据
    }

    // 最后再获取数据
    tableManager.getData()
}
ts
// 数据加载完成后打开公共搜索
tableManager.getData()?.then(() => {
    tableManager.table.showComSearch = true
})
ts
/**
 * 利用钩子在修改数据前对数据进行预处理
 */
tableManager.opts.after!.getEditData = () => {
    if (tableManager.form.items && !tableManager.form.items.icon) {
        tableManager.form.items.icon = 'lucide-circle-small'
    }
}
ts
import { auth } from '/@/utils/common'

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

// auth 是全局公共函数,它的文档请参考:https://doc.ai-go-hub.com/senior/web/utils.html#%E5%89%8D%E7%AB%AF%E9%89%B4%E6%9D%83
// tableManager.auth 是表格内部鉴权方法,参数 node 是表格内部需要鉴权的节点,比如 create、update、delete 等
ts
// 参数类型是 AnyObj,框架本身未对参数进行任何识别处理,只是习惯性传递
tableManager.refresh({ event: 'submit-form', items: items })

表格所有可用属性

ts
// 一般通过 useTableManager.table.* 访问

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

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

    /**
     * 获取表格数据时的过滤条件(含公共搜索、快速搜索、分页、排序等数据)
     * 公共搜索数据可使用 useTableManager.setComSearchData 和 useTableManager.getComSearchData 进行管理
     */
    filter?: {
        page?: number
        limit?: number
        sort?: string
        order?: 'desc' | 'asc'
        wheres?: WhereGroup[]
        quickSearchKeywords?: string
        [key: string]: any
    }

    /**
     * 不需要双击编辑的列;
     * 禁用全部列的双击编辑,可使用 ['all'];
     * type=selection 的列为 undefined,将自动禁用
     */
    dblClickNotEditColumn?: string[]

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

    // 表格 ref,通常在页面 onMounted 时赋值,可选的
    ref?: InstanceType<typeof Table> | null
    // 表格对应数据表的主键字段,默认 id
    pk?: string
    // 表格加载状态
    loading?: boolean
    // 当前选中行
    selections?: TableRow[]
    // 数据总量
    total?: number
    // 接受 url 的 query 参数并自动触发公共搜索
    acceptQuery?: boolean
    // 显示公共搜索
    showComSearch?: boolean
    // 是否展开所有子项,树状表格专用属性
    expandAll?: boolean
    // 当前表格所在页面的路由 path
    routePath?: string
    // 拖动排序限位字段,例如拖动行 pid=1,那么拖动目的行 pid 也需要为 1
    dragSortLimitField?: string
    // 拖动排序权重字段,进行拖拽排序时,必需先以此字段排序,系统将修改此字段的值来完成新顺序落盘,留空则取 `weigh`
    dragSortWeighField?: string
}
ts
// 一般通过 useTableManager.form.* 访问

interface FormInterface {
    /**
     * 当前表单项数据
     */
    items?: AnyObj

    /**
     * 当前表单操作标识:create=添加,update=更新
     */
    operate?: string

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

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

    // 表单 ref,实例化表格时通常无需传递
    ref?: FormInstance | null
    // 表单项 label 的宽度
    labelWidth?: number
    // 被操作数据主键,支持批量更新:create=[],update=[1,2,n]
    operatePKs?: string[]
    // 提交按钮状态
    submitLoading?: boolean
    // 表单数据的加载状态
    loading?: boolean
}
ts
// 一般通过 useTableManager.comSearch.* 访问

interface ComSearchInterface {
    // 公共搜索表单项数据
    form: AnyObj
    // 字段搜索配置,搜索操作符(operator)、公共搜索渲染方式(comSearchRender)、字段渲染方式(render)
    fieldData: Map<string, any>
}
ts
// 一般通过 useTableManager.table.column[index].* 访问

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

    // render=slot 时,slot 的名称
    slotName?: string
    // render=customRender 时,要渲染的组件或已注册组件名称的字符串
    customRender?: string | Component
    // render=customTemplate 时,自定义渲染 html,应谨慎使用: 请返回 html 内容,务必确保返回内容是 xss 安全的
    customTemplate?: (row: TableRow, columnConfig: TableColumn, column: TableColumnCtx<TableRow>, cellValue: any, 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>
        link?: TableContextDataFun<LinkProps>
        [key: string]: any
    }

    // render=buttons 时,按钮数据数组
    buttons?: OptButton[]

    /**
     * 单元格渲染器需要的其他任意自定义数据
     * 1. render=tag 时,可单独指定每个不同的值 tag 的 type 属性 { open: 'success', close: 'info', disable: 'danger' }
     * 2. render=datetime 时,可指定时间日期的格式化模板(dayjs().format 模板,如 { format: 'YYYY-MM-DD HH:mm:ss' })
     */
    custom?: {
        format?: string
        [key: string]: any
    }

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

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

        remoteURL: string
        remoteParams?: AnyObj
        remoteSearchFields?: string[]
    }
}
ts
/**
 * 公共搜索事件返回的 Data
 * 同时也是发送给服务端的单条 Where 条件类型定义
 */
interface ComSearchData {
    field: string
    value: string | string[] | number | number[]
    operator: string
}

/**
 * Where 查询条件分组
 * 服务端对 `字段是否存在、操作符合是否合法` 进行检查,并对 `值` 使用 `预处理语句参数占位符` 拼接
 */
interface WhereGroup {
    wheres: ComSearchData[]
    or?: boolean // 组内条件是否使用 OR 连接,值为 false 则使用 AND 连接条件
}

表格公共搜索

  1. 表格公共搜索就是列的公共搜索,所以它在定义 tableManager.table.column 列数据时设定,主要是表格列的 operator、comSearchRender、其他以 comSearch* 开头的 属性共同参与公共搜索框的渲染。
  2. 公共搜索表单源码位置 src\components\table\header\comSearch.vue,其本质很简单:遍历 column,根据 operator、comSearchRender 渲染一个表单出来。

如下示例代码:

ts
const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: {
        column: [
            {
                label: '性别',
                prop: 'gender',
                align: 'center',
                // 公共搜索渲染为 select
                comSearchRender: 'select',
                // 字典数据,它将作为 select 的选项列表(同时也是单元格渲染时的值替换列表)
                dict: { '0': '未知', '1': '女', '2': '男' },
                // 公共搜索操作符号
                operator: 'eq',
                // 单元格渲染为 tag
                render: 'tag',
            },
        ],
    },
})

operator

即公共搜索操作符号,支持的符号有:

ts
type OperatorStr =
    | 'eq' // 等于,默认值
    | 'ne' // 不等于
    | 'gt' // 大于
    | 'egt' // 大于等于
    | 'lt' // 小于
    | 'elt' // 小于等于
    | 'LIKE' // 模糊查询
    | 'NOT LIKE'
    | 'ILIKE' // 不区分大小写的模糊查询
    | 'NOT ILIKE'
    | 'IN'
    | 'NOT IN'
    | 'BETWEEN' // 范围,将生成两个输入框,可以输入最小值和最大值
    | 'NOT BETWEEN'
    | 'NULL' // 是否为NULL,将生成单个复选框
    | 'NOT NULL'
    | false

其中比较常见的用法是:

  • operator=false 则表示关闭此字段的公共搜索
  • operator=ILIKE 不区分大小写的模糊匹配
  • operator=BETWEEN 为范围查询,生成两个输入框(一般是对数字或时间日期使用)

operator 和 show 属性配合使用

有时,您可能需要同一列有两次配置机会,比如在公共搜索和表格中使用不同的 label,如下 id公共搜索单元格渲染 的配置完全独立,一个配置用于表格、一个配置用于公共搜索。

ts
column: [
    { label: 'ID-公共搜索', prop: 'id', show: false, operator: 'LIKE' },
    { label: 'ID-列表显示', prop: 'id', operator: false, width: 70 },
]

comSearchRender

comSearchRender 指定公共搜索渲染方案,支持 remoteSelect、select、time、date 等,特别是最终保底方案还可以将公共搜索渲染为 slot,即自定义插槽渲染,您可以在插槽内部选择自己喜欢的输入框和样式,详细使用方法:公共搜索配置示例代码完全自定义公共搜索的渲染

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

comSearchRenderoperator公共搜索渲染
任意false关闭公共搜索,不渲染
任意BETWEEN、NOT BETWEEN生成 A-B 两个输入框,可搜索 从 A 到 B 的范围值
任意NULL、NOT NULL生成一个复选框,勾选则搜索值为 NULL、NOT NULL 的情况
remoteSelecteq、ne、gt 等比较符号,可参考 operator远程下拉,需通过 comSearchRemote 设定好远程下拉的必填属性
selecteq、ne、gt 等比较符号下拉框,选项列表请参考 表格列dict 字典数据
timeeq、ne、gt 等比较符号时间选择器(纯时间)
timeBETWEEN、NOT BETWEEN时间范围选择器(纯时间)
dateeq、ne、gt 等比较符号日期选择器(纯日期无时间)
dateBETWEEN、NOT BETWEEN日期范围选择器(纯日期无时间)
datetimeeq、ne、gt 等比较符号时间日期选择器
datetimeBETWEEN、NOT BETWEEN时间日期范围选择器
string任意字符串输入框(它还是默认的公共搜索渲染方案)
customRender任意自定义渲染组件或 slot,请参考下方示例

公共搜索配置示例代码

ts
import { useTableManager } from '@/hooks/useTableManager'
import { TableManagerAPI } from '@/api/table'

const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: {
        // 定义表格列数据、同时定义公共搜索数据
        column: [
            // 关闭这个字段的公共搜索
            { type: 'selection', align: 'center', operator: false },
            // 此字段是模糊查找,并为公共搜索输入框设置了 placeholder
            { label: 'ID', prop: 'id', align: 'center', operator: 'ILIKE', comSearchPlaceholder: '模糊搜索', width: 70 },
            // 此字段是图片,建议关闭公共搜索
            { label: '头像', prop: 'avatar', align: 'center', render: 'image', operator: false },
            // 此字段将生成一个下拉框选择进行搜索,拥有三个值
            {
                label: '性别',
                prop: 'gender',
                align: 'center',
                render: 'tag',
                comSearchRender: 'select',
                dict: { 'unknown': '未知', 'male': '男', 'woman': '女' },
            },
            // 此字段将生成一个时间范围选择框,选择时间日期进行搜索
            {
                label: '创建时间',
                prop: 'createtime',
                align: 'center',
                render: 'datetime',
                sortable: 'custom',
                comSearchRender: 'datetime',
                operator: 'BETWEEN',
                width: 160,
            },
            // 此字段将生成一个日期范围选择框,选择日期进行搜索(请注意渲染还是 datetime 若需自定义单元格渲染请参考 `表格列` 一节)
            {
                label: '创建时间',
                prop: 'updatetime',
                align: 'center',
                render: 'datetime',
                sortable: 'custom',
                operator: 'BETWEEN',
                width: 160,
                comSearchRender: 'date',
            },
            // 远程下拉选择框
            {
                label: '会员',
                prop: 'user_id',
                comSearchRender: 'remoteSelect',
                comSearchRemote: {
                    // 主键,下拉 value
                    pk: 'id',
                    // 字段,下拉 label
                    field: 'username',
                    // 远程接口URL
                    // 比如想要获取 admin(管理员)表的数据,后台 管理员管理 列表方法 的 URL 为 /admin/auth/admin/list
                    remoteURL: '/admin/auth/admin/list',
                    // 额外的请求参数
                    remoteParams: {},
                },
            },
        ],
    },
})

自动获取表格筛选条件

有时您需要通过 URL 带着一些筛选条件跳转到表格,实现直接获取筛选后的数据(直接在 URL 中填写公共搜索数据),如:/#/admin/auth/admin?username=test&id=2,3

只要 URL 中的 query 字段名表格列名对应,且该列开启了公共搜索,URL 上的参数,可以被自动获取到并作为公共搜索的筛选条件。

如下是跳转时拼接 query 的方法,手动字符串拼接也是可以的。

ts
import router from '@/router/index'

// 跳转到 auth/admin 页面,并携带 id 和 username 参数
router.push({
    name: 'auth/admin',
    query: {
        // id 和 username 字段存在于 auth/admin 页面表格的公共搜索中
        // auth/admin 页面内的表格公共搜索,会自动获取下列参数,如果在列定义中存在则自动填充并搜索
        id: '2,3', // 范围搜索使用 , 号分割
        username: 'test',
    },
})

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

切换以下代码块的 Tab 来查看多份示例代码。

vue
<template>
    <div class="default-main">
        <TableHeader
            :manager="tableManager"
            v-model:com-search="tableManager.comSearch"
            :buttons="['refresh', 'add', 'edit', 'delete', 'comSearch', 'quickSearch', 'columnDisplay']"
        >
            <!-- 请注意 #test 它是自定义的插槽名称 -->
            <template #test>
                <!-- 在插槽内,您可以随意发挥,通常渲染一个输入框供用户输入内容 -->
                <!-- 输入组件的 v-model="baTable.comSearch.form[item.prop!]" 即可在baTable上下文获取用户输入的关键词 -->
                我是公共搜索的slot渲染内容
            </template>
        </TableHeader>

        <Table :manager="tableManager" />
    </div>
</template>

<script setup lang="ts">
import { TableManagerAPI } from '@/api/table'
import TableHeader from '@/components/table/header/index.vue'
import Table from '@/components/table/index.vue'
import { useTableManager } from '@/hooks/useTableManager'

const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: {
        // 定义表格列数据、同时定义公共搜索数据
        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,分别是:renderColumnConfig=列表配置数据,renderColumnProp=列数据,renderValue=搜索框绑定值,manager=表格管家实例
const searchId = {
    render(context: any) {
        console.log(context.$attrs.renderColumnConfig, context.$attrs.renderColumnProp, context.$attrs.renderValue, context.$attrs.manager)

        // 使用原生元素渲染
        return h('input', { class: 'id-h1' }, context.$attrs.renderValue)

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

const tableManager = useTableManager({
    api: new TableManagerAPI('/admin/test/'),
    table: {
        // 定义表格列数据、同时定义公共搜索数据
        column: [
            { label: 'id', prop: 'id', comSearchRender: 'customRender', comSearchCustomRender: h(searchId) }
        ],
    },
})
vue
<!-- 1. 表格顶部菜单的 buttons 里边不要有 comSearch,表示不启用默认的公共搜索 -->
<TableHeader
    :manager="tableManager"
    v-model:com-search="tableManager.comSearch"
    :buttons="['refresh', 'add', 'edit', 'delete', 'quickSearch', 'columnDisplay']"
></TableHeader>

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

TableManagerAPI 类

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

使用示例

vue
<script>
import { TableManagerAPI } from '@/api/table'

const api = new TableManagerAPI('/admin/test/')

// 修改请求地址 - 只修改地址不能满足需求的,可直接于 \src\api 目录下自定义请求函数后导入使用,可参考网络请求文档
api.actionURL.set('get', '/admin/test/get-new')
api.actionURL.set('list', '/admin/test/list-new')
api.actionURL.set('create', '/admin/test/create-new')
api.actionURL.set('update', '/admin/test/update-new')
api.actionURL.set('delete', '/admin/test/delete-new')
api.actionURL.set('sort', '/admin/test/sort-new')

/**
 * 主动执行请求
 * 注意:TableManagerAPI 类的方法通常都是供 useTableManager 使用的,以下仅为方便读者理解它是什么,一般无需主动执行
 */
api.list({}) // 请求 list,参数为筛选条件,具体使用请参考 useTableManager
api.post({}) // 向指定接口 post 数据
api.delete({}) // 请求删除,参数为被删除数据的 ids
// ...

const tableManager = useTableManager({
    api: api,
    table: {},
})
</script>

表格顶部组件

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

属性列表

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

插槽列表

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

支持的菜单按钮

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

表格顶部菜单示例代码

vue
<template>
    <div>
        <TableHeader
            :manager="tableManager"
            v-model:com-search="tableManager.comSearch"
            :buttons="['refresh', 'add', 'edit', 'delete', 'comSearch', 'quickSearch', 'columnDisplay']"
        >
            <!-- 可以在此处以插槽的方式设置一些自定义按钮 -->

            <template #refreshPrepend>
                <!-- 刷新按钮前插槽内容 -->
            </template>

            <!-- 默认插槽 -->
            <template #default>
                <el-button :disabled="tableManager.table.selections!.length > 0 ? false : true" class="table-header-operate" type="success">
                    <Icon color="#ffffff" size="18" name="el-refresh-right" />
                    <!-- 自定义了一个还原按钮 -->
                    <span class="table-header-operate-text">还原</span>
                </el-button>
            </template>
        </TableHeader>
    </div>
</template>

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

常见问题

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

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