Data Table 组件进行封装,效果如下:

1、可实现基础的表格数据和分页信息的展示

2、可调整表格的样式,选择展示的列

3、可实现对列的操作功能

4、实现单选和多选操作

5、实现可编辑单元格,整行编辑操作

下载基础代码

基础代码下载地址:

或者从Git仓库中直接拷贝代码,推荐!

添加 BasicTable 组件

打开项目,找到src目录,需要导入的文件及位置如下:

Table ==> src/components/
hooks/event ==> src/hooks
settings/componentSetting.js ==> src/settings/
utils/domUtils.js ==> src/utils/
directives => src/

BasicTable组件中使用到了基础Naive UI组件,这里需要增量引入:

哪个组件打印警告信息,添加哪个组件即可。

import * as NaiveUI from 'naive-ui';

const naive = NaiveUI.create({
components: [
NaiveUI.NSwitch,
NaiveUI.NDataTable,
NaiveUI.NCheckboxGroup,
NaiveUI.NPopover
],
});
export function setupNaive(app) {
app.use(naive);
}

安装vueuse依赖

npm i @vueuse/core

基础使用

分页设置

找到src/settings/componentSetting,修改其中的字段名,和后端返回的分页信息对应

export default {
table: {
apiSetting: {
// 当前页的字段名
pageField: 'current',
// 每页数量字段名
sizeField: 'size',
// 接口返回的数据字段名
listField: 'records',
// 接口返回总页数字段名
totalField: 'pages',
// 接口返回总数据个数
itemCountField: 'total',
},
//默认分页数量
defaultPageSize: 10,
//可切换每页数量集合
pageSizes: [5, 10, 20, 30, 40],
},
};

表格列设置

通过设置列的属性可以显示对应字段,以及列的样式信息等

columns.js
import { NSwitch } from "naive-ui";
import { h } from "vue";
import { DeleteOutlined, EditOutlined } from '@vicons/antd'

export const createListColumns = ({ currentIndex, switchStatus }) => {
return [
{
type: 'selection',
key: 'selection'
},
{
title: '序号',
key: 'index',
width: 60,
render: (...[, record]) => {
return currentIndex() + record + 1;
},
},
{
title: '医院名称',
key: 'hosname',
},
{
title: '医院编号',
key: 'hoscode',
},
{
title: 'api基础路径',
key: 'apiUrl',
width: 200,
},
{
title: '联系人姓名',
key: 'contactsName',
},
{
title: '联系人手机',
key: 'contactsPhone',
},
{
title: '状态',
key: 'status',
render(row) {
return h(NSwitch, {
value: row.status === 1 ? true : false,
onClick: () => switchStatus(row),
});
},
},
]
}

export function createActions(record, {handleEdit, handleDelete}) {
return [
{
label: '删除',
type: 'error',
// 配置color会覆盖type
color: 'red',
icon: DeleteOutlined,
onClick: handleDelete.bind(null, record)
},
{
label: '编辑',
type: 'primary',
icon: EditOutlined,
onClick: handleEdit.bind(null, record)
}
];
}

页面使用

<template>
<n-card :bordered="false" class="proCard table">
<BasicTable
:columns="columns"
:request="loadDataTable"
@fetch-success="fetchSuccess"
@update:checked-row-keys="onCheckedRow"
ref="actionRef"
:actionColumn="actionColumn"
:row-key="(row) => row.id"
:scroll-x="1000">
<!-- 表格上方左侧的位置 -->
<template #tableTitle>
<n-space>
<!-- 批量删除 -->
<n-button strong secondary type="error" @click="removeRows">
批量删除
</n-button>
</n-space>
</template>
</BasicTable>
</n-card>
</template>
<script setup>
import { h, reactive, ref, unref } from 'vue'
import { getHospSetPageList } from '@/api/hospset'
import { BasicTable, TableAction } from '@/components/Table'
import { createListColumns, createActions } from './columns';
import { useDialog, useMessage } from 'naive-ui';

const dialog = useDialog()
const message = useMessage()
const actionRef = ref(null)
const currentIndex = ref(1)
// 选中的行
const selections = ref([]);

// 表格列属性
const columns = createListColumns({
currentIndex() {
return unref(currentIndex);
},
switchStatus: async (row) => {
const { id, status } = row;
const update = status === 1 ? 0 : 1;
try {
// 切换状态
row.status = update;
} catch (error) {
row.status = status
}
},
});

const actionColumn = reactive({
width: 140,
title: '操作',
key: 'action',
fixed: 'right',
align: 'center',
render(record) {
return h(TableAction, {
showLabel: false,
actions: createActions(record, { handleEdit, handleDelete }),
});
},
});

// 数据请求
const loadDataTable = async (res) => {
return await getHospSetPageList({ ...res });
}
function fetchSuccess() {
// 数据请求成功,设置索引
currentIndex.value =
(actionRef.value.pagination.current - 1) *
actionRef.value.pagination.pageSize;
}
// 表格选中行事件
function onCheckedRow(rowKeys) {
selections.value = rowKeys;
}

// 刷新表格
function reloadTable() {
actionRef.value.reload();
}

// 编辑事件
const handleEdit = (record) => { }

// 删除事件
const handleDelete = (record) => {
dialog.info({
title: '提示',
content: `您想删除:${record.hosname}`,
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
await deleteHospSet(record.id)
reloadTable();
},
onNegativeClick: () => { },
});
}

// 批量删除事件
const removeRows = () => {
const ids = unref(selections);
if (ids.length === 0) {
message.warning('请至少勾选一列');
return;
}
dialog.warning({
title: '提示',
content: `您要删除: ${ids}`,
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
await batchRemoveHospSet(ids);
reloadTable();
},
onNegativeClick: () => {},
});
}
</script>

行列编辑

实现每个单元格编辑或者整行编辑,效果如图:

分页设置

找到src/settings/componentSetting,修改其中的字段名,和后端返回的分页信息对应

export default {
table: {
apiSetting: {
// 当前页的字段名
pageField: 'current',
// 每页数量字段名
sizeField: 'size',
// 接口返回的数据字段名
listField: 'records',
// 接口返回总页数字段名
totalField: 'pages',
// 接口返回总数据个数
itemCountField: 'total',
},
//默认分页数量
defaultPageSize: 10,
//可切换每页数量集合
pageSizes: [5, 10, 20, 30, 40],
},
};

表格列设置

通过设置列的属性可以显示对应字段,以及列的样式信息等

  • edit: true:开启编辑功能,
  • editRule: true:编辑时的校验规则,默认true为非空校验,
  • editComponent: 'NInput':编辑时渲染的组件类型,
columns.js
import { NSwitch } from "naive-ui";
import { h } from "vue";
import { DeleteOutlined, EditOutlined, SaveOutlined, CloseSquareOutlined } from '@vicons/antd'

const reg_tel = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/

export const createListColumns = ({ switchStatus }) => {
return [
{
type: 'selection',
key: 'selection'
},
{
title: '医院名称',
key: 'hosname',
edit: true,
editRule: true,
editComponent: 'NInput',
},
{
title: '医院编号',
key: 'hoscode',
edit: true,
editRule: true,
editComponent: 'NInput',
},
{
title: 'api基础路径',
key: 'apiUrl',
width: 200,
edit: true,
editRule: true,
editComponent: 'NInput',
},
{
title: '联系人姓名',
key: 'contactsName',
edit: true,
editRule: true,
editComponent: 'NInput',
},
{
title: '联系人手机',
key: 'contactsPhone',
edit: true,
editRule: (text, _) => { if (!reg_tel.test(text)) return '手机号格式错误' },
editComponent: 'NInput',
},
{
title: '状态',
key: 'status',
render(row) {
return h(NSwitch, {
value: row.status === 1 ? true : false,
onClick: () => switchStatus(row),
});
},
},
]
}

export function createActions(record, {handleSave, handleEdit, handleDelete, handleCancel}) {
if (!record.editable) {
return [
{
label: '删除',
type: 'error',
// 配置color会覆盖type
color: 'red',
icon: DeleteOutlined,
onClick: handleDelete.bind(null, record)
},
{
label: '编辑',
type: 'primary',
icon: EditOutlined,
onClick: handleEdit.bind(null, record)
}
];
} else {
return [
{
label: '保存',
type: 'success',
icon: SaveOutlined,
onClick: handleSave.bind(null, record),
},
{
label: '取消',
type: 'warning',
icon: CloseSquareOutlined,
onClick: handleCancel.bind(null, record),
},
];
}
}

页面使用

<template>
<n-card :bordered="false" class="proCard table">
<BasicTable
:columns="columns"
:request="loadDataTable"
@edit-end="cellEditEnd"
ref="actionRef"
:actionColumn="actionColumn"
:row-key="(row) => row.id"
:scroll-x="1000" />
</n-card>
</template>
<script setup>
import { h, reactive, ref, unref } from 'vue'
import { getHospSetPageList, deleteHospSet, batchRemoveHospSet, lockHospSet, updateHospSet } from '@/api/hospset'
import { BasicTable, TableAction } from '@/components/Table'
import { createListColumns, createActions } from './columns';
import { useDialog, useMessage } from 'naive-ui';

const dialog = useDialog()
const message = useMessage()
const actionRef = ref(null)

// 表格列属性
const columns = createListColumns({
switchStatus: async (row) => {
const { id, status } = row;
const update = status === 1 ? 0 : 1;
try {
await lockHospSet(id, update)
row.status = update;
} catch (error) {
row.status = status
}
},
});

// 表格操作列属性
const currentEditKeyRef = ref('');
const actionColumn = reactive({
width: 140,
title: '操作',
key: 'action',
fixed: 'right',
align: 'center',
render(record) {
return h(TableAction, {
showLabel: false,
actions: createActions(record, { handleSave, handleEdit, handleDelete, handleCancel }),
});
},
});


// 数据请求
const loadDataTable = async (res) => {
return await getHospSetPageList({ ...res });
}

// 刷新表格
function reloadTable() {
actionRef.value.reload();
}

// 单元格编辑提交事件
const cellEditEnd = async ({ record, index, key, value }) => {
const param = {
id: record.id
}
param[key] = value
try {
await updateHospSet(param)
message.success('保存成功')
} catch (error) {
console.log(error);
}
}

// 整行编辑保存事件
const handleSave = async (record) => {
const pass = await record.onEdit?.(false, true);
if (pass) {
currentEditKeyRef.value = '';
const { id, hosname, hoscode, apiUrl, contactsName, contactsPhone } = record;
// 提交更新
try {
await updateHospSet({ id, hosname, hoscode, apiUrl, contactsName, contactsPhone })
message.success('保存成功')
} catch (error) {
console.log(error);
}
}
}

// 开启整行编辑事件
const handleEdit = (record) => {
currentEditKeyRef.value = record.key;
record.onEdit?.(true);
}

// 删除事件
const handleDelete = (record) => {
dialog.info({
title: '提示',
content: `您想删除:${record.hosname}`,
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
await deleteHospSet(record.id)
reloadTable();
},
onNegativeClick: () => { },
});
}

// 取消整行编辑事件
const handleCancel = (record) => {
currentEditKeyRef.value = '';
record.onEdit?.(false, false);
}
</script>

详细使用

详细的使用教程见: