引用vxe-table@4.5.21表格功能
目录
一. 效果展示
编辑操作
选中单元格
选中行
新增、标记删除、修改、复制
二. template
<vxe-table
ref="tableRef"
:data="tableData"
keep-source
:edit-config="{ trigger: 'dblclick', mode: 'cell', showStatus: true, enabled: canEdit }"
style="width: 100%; user-select: text"
height="100%"
class="editable-vxe"
:row-class-name="setRowClassName"
:cell-class-name="setCellClassName"
:row-config="{ useKey: true }"
v-loading="loading"
border
:scroll-x="{ enabled: true, gt: 10 }"
:scroll-y="{ enabled: true, gt: 100 }"
@cell-click="cellClickEvent"
>
<vxe-column
v-for="(item, index) in tableColumn"
:key="index"
:field="item.prop"
:title="item.label"
show-header-overflow="title"
min-width="180"
show-overflow="title"
:edit-render="{ enabled: !!item.label, autofocus: '.el-input__inner' }"
>
<template #edit="{ row }">
<el-input v-model="row[item.prop]" type="text" :placeholder="setDefaultPlaceHolder(row, item)"></el-input>
</template>
</vxe-column>
</vxe-table>
1. vxe-table属性解析
keep-source:需区分编辑样式、做临时删除及还原数据操作的要加上
edit-config:此处选用单元格编辑模式cell,视情况可选行编辑row;showStatus配合keep-source属性可实现区分新增行、修改单元格或行与原数据的样式区分
row-class-name:给原选中行附加类名,修改默认样式
cell-class-name:给原选中单元格附加类名,修改默认样式
row-config:useKey可默认表格每行数据的唯一标识
scroll-x/scroll-y:虚拟滚动配置,大数据量可配
2. vxe-column属性解析
edit-render:autofocus实现打开编辑立刻聚焦文本
placeholder:可设置编辑文本区的默认提示值
三. script
tip: 以下规则均按单一选择设置
1. data
import { VxeTableInstance, VxeTableEvents } from 'vxe-table';
const initTableData = ref<any[]>([]); // 初始数据(用于还原)
const tableData = ref<any[]>([]); // 表格绑定数据
const tableColumn = ref<any[]>([]); // 表格每列相关值(表头、默认提示值、是否可编辑等)
const previewUpdateParams = ref<any>({}); // 预览需携带的相关参数
const tableRef = ref<VxeTableInstance>(); // 表格ref绑定
const currSelectedCell = ref<any[]>([]); // 当前选中(可单一单元格,单行,多单元格,多行)
2. computed
// 是否可编辑
const canEdit = computed(() => {
// 接口返回是否可编辑状态
return ...;
});
// 是否可复制
const copyDisabled = computed(() => {
return !currSelectedCell.value[0]?.row;
});
// 是否可删除
const delDisabled = computed(() => {
return !currSelectedCell.value[0]?.row;
});
// 是否可撤销所选条目
const cancelDisabled = computed(() => {
const $table = tableRef.value;
if ($table && currSelectedCell.value[0]) {
// isInsertByRow isUpdateByRow可能存在性能问题
// 复制或新增行
if (!!$table.isInsertByRow(currSelectedCell.value[0].row)) return false;
if (
currSelectedCell.value[0].field &&
$table.isUpdateByRow(currSelectedCell.value[0].row, currSelectedCell.value[0].field)
) {
// cell
return false;
}
if (!currSelectedCell.value[0].field && $table.isUpdateByRow(currSelectedCell.value[0].row)) {
// row
return false;
}
// 标记删除
if (isPendingRecords(currSelectedCell.value[0].row)) {
return false;
}
}
return true;
});
3. methods
表格属性及事件绑定
onMounted(() => {
window.addEventListener('dblclick', dblclickEventListenerFn);
});
onUnmounted(() => {
window.removeEventListener('dblclick', dblclickEventListenerFn);
});
// 此处采取双击表格外区域取消选中
const dblclickEventListenerFn = (event?: any) => {
currSelectedCell.value = [];
};
const cellClickEvent: VxeTableEvents.CellClick = ({ row, rowIndex, column, columnIndex, $event }) => {
if (!canEdit.value) return;
// 默认选中行
const cellOrRow = {
row,
rowIndex,
};
if (columnIndex != 0) {
// 选中单元格
Object.assign(cellOrRow, { field: column.field, columnIndex });
}
// 此处可补充多选(ctrl/shift等操作)
currSelectedCell.value = [cellOrRow];
};
const setRowClassName = ({ rowIndex }: any) => {
if (!currSelectedCell.value[0]?.columnIndex && currSelectedCell.value[0]?.rowIndex == rowIndex) {
return '选中行类名';
}
return '';
};
const setCellClassName = ({ rowIndex, columnIndex }: any) => {
if (currSelectedCell.value[0]?.rowIndex == rowIndex && currSelectedCell.value[0]?.columnIndex == columnIndex) {
return '选中单元格类名';
}
return '';
};
const setDefaultPlaceHolder = (row: any, item: any) => {
let result = '';
const $table = tableRef.value;
if ($table) {
// 此处设置默认提示值
...
}
return result;
};
表格数据获取及操作方法
/**
* 表格数据获取
* @param headers 表头数据
* @param datas 数据映射
*/
const getTableData = (headers: any[], datas: any[]) => {
const tablePropsMap = {} as any;
for (let i in headers) {
tablePropsMap[i] = headers[i].prop;
}
let result = [] as any[];
for (let i in datas) {
let temp = {} as any;
for (let j in datas[i]) {
temp[tablePropsMap[j]] = datas[i][j];
}
result.push(temp);
}
tableData.value = result;
initTableData.value = JSON.parse(JSON.stringify(result));
}
/**
* 表格数据是否新增、编辑、复制及临时删除
*/
const isEdited = () => {
const $table = tableRef.value;
let res = false;
if ($table) {
const records = $table.getRecordset();
const { insertRecords, updateRecords, pendingRecords } = records;
res = [...insertRecords, ...updateRecords, ...pendingRecords].length > 0;
}
return res;
};
/**
* 新增行
* @param row 指定位置、null从第一行插入、-1 从最后插入
*/
const insertEvent = async (row?: number) => {
const $table = tableRef.value;
if ($table) {
const insertRecords = $table.getInsertRecords();
const record: any = {};
tableColumn.value.map((item: any) => {
record[item.prop] = null;
})
record['Row Number'] = tableData.value[tableData.value.length - 1]['Row Number'] * 1 + insertRecords.length + 1
const { row: newRow } = await $table.insertAt(record, row);
await $table.setEditCell(newRow, tableColumn.value[1].prop);
}
};
/**
* 复制行
*/
const copyEvent = async () => {
const $table = tableRef.value;
if ($table && currSelectedCell.value[0]?.row) {
const insertRecords = $table.getInsertRecords();
const record = {
...currSelectedCell.value[0].row,
'Row Number': tableData.value[tableData.value.length - 1]['Row Number'] * 1 + insertRecords.length + 1,
};
record['_X_ROW_KEY'] && delete record['_X_ROW_KEY'];
const { row: newRow } = await $table.insertAt(record, -1);
await $table.setEditCell(newRow, tableColumn.value[1].prop);
}
};
/**
* 删除行(原数据做临时删除,新增或复制则直接删除)
* @param status true:标记为删除,false:还原
*/
const delEvent = (status: boolean) => {
const $table = tableRef.value;
if ($table) {
if (!!$table.isInsertByRow(currSelectedCell.value[0].row)) {
// 删除复制或新增行
$table.remove(currSelectedCell.value[0].row);
} else {
// 还原数据
$table.revertData(currSelectedCell.value[0].row);
$table.setPendingRow(currSelectedCell.value[0].row, status);
}
}
};
/**
* 撤销所选项目
*/
const cancelSelectedEvent = () => {
const $table = tableRef.value;
if ($table) {
if (isPendingRecords(currSelectedCell.value[0].row)) {
// 还原临时删除
delEvent(false);
} else if (!!$table.isInsertByRow(currSelectedCell.value[0].row)) {
// 删除复制或新增行
$table.remove(currSelectedCell.value[0].row);
} else if (currSelectedCell.value[0].field) {
// cell
$table.revertData(currSelectedCell.value[0].row, currSelectedCell.value[0].field);
} else {
// row
$table.revertData(currSelectedCell.value[0].row);
}
}
};
/**
* 预览修改
*/
const previewModify = () => {
if (!isEdited()) return;
const $table = tableRef.value;
if ($table) {
const records = $table.getRecordset();
// 新增、临时删除、编辑数据
const { insertRecords, pendingRecords, updateRecords } = records;
...
}
};
/**
* 提交修改
*/
const submitModify = () => {
if (!isEdited()) return;
...
};
四. style
以下为参考样式,除选中样式需添加,其余可沿用默认样式
.editable-vxe {
.selected-row {
// 选中行样式
box-shadow: inset 0 -0.5px 0 2px #00f;
}
.selected-cell {
// 选中单元格样式
box-shadow: inset 0 0 0 2px #00f;
}
.col--dirty {
background-color: 编辑后的背景色;
&::before {
// 屏蔽原样式
display: none;
}
}
.row--new {
background-color: 新增或复制后的背景色;
.vxe-body--column {
&::before {
// 屏蔽原样式
display: none;
}
}
}
.row--pending {
background-color: 临时删除的背景色;
// 覆盖原样式
color: inherit;
text-decoration: none;
cursor: default;
.vxe-body--column::after {
display: none;
}
}
}