IOT平台的后端管理前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

653 lines
21 KiB

<template>
<vxe-grid
ref="tableRef"
v-bind="gridOptions"
:loading="props.loading"
:columns="props.columns"
:data="props.tableData"
@cell-dblclick="cellDBLClickEvent"
:row-style="getRowStyle"
@edit-actived="onEditActivated"
@page-change="pageChange"
>
<template #toolbar_buttons>
<vxe-button size="small" status="primary" icon="vxe-icon-add" @click="addRow()" v-if="hasAddRows">
{{ addRowsText }}
</vxe-button>
<vxe-button size="small" status="danger" icon="vxe-icon-delete-fill" @click="delSelect" v-if="hasDelSelect">
删除
</vxe-button>
<vxe-button size="small" status="success" icon="vxe-icon-save" @click="allSave" v-if="props.hasAllSave">
保存
</vxe-button>
</template>
<template #toolbar_buttons_new>
<vxe-button size="small" status="primary" icon="vxe-icon-add" @click="addRow()" v-if="hasAddRows">
{{ addRowsText }}
</vxe-button>
<vxe-button size="small" status="danger" icon="vxe-icon-delete-fill" @click="delSelect" v-if="hasDelSelect">
删除
</vxe-button>
</template>
<template #statusDef="{ row }">
<vxe-switch v-model="row.status" size="mini"></vxe-switch>
</template>
<template #text="{ row, column }">
<el-input
@mousedown.stop
v-model="row[column?.field]"
placeholder="请输入内容"
disabled
/>
</template>
<template #input="{ row, column }">
<el-input
@mousedown.stop
v-model="row[column?.field]"
placeholder="请输入内容"
/>
</template>
<template #treeSelect="{ row, column }">
<el-tree-select
@mousedown.stop
v-model="row[column?.field]"
:data="typeof column.slots.data === 'function' ? column.slots.data(row,column) : column.slots.data"
:props="column.slots.props"
check-strictly
:default-expand-all="true"
:value-key="column.slots.key"
:size="column.slots.size"
@change="($event) => { column.slots.change($event, row, column)}"
/>
</template>
<template #select="{ row, column }">
<el-select @mousedown.stop v-model="row[column?.field]" @change="column?.slots.change($event,row,column)" :disabled="column?.slots.disabled" :filterable="column?.slots.filterable" @visible-change="column?.slots.visibleChange($event,row,column)">
<el-option v-for="dict in column?.slots.options" :key="dict[column?.slots.key]" :label="dict[column?.slots.label]"
:value="dict[column?.slots.value]" />
</el-select>
</template>
<template #operate="{$table, row, column }">
<template v-if="hasEditRows(row)">
<vxe-button size="small" type="text" content="取消" icon="vxe-icon-close" @click="cancelRowClick"></vxe-button>
<vxe-button size="small" type="text" status="primary" content="保存" icon="vxe-icon-check"
@click="saveRowClick(row)"></vxe-button>
</template>
<template v-else>
<vxe-button v-if="getButtonShow($table,row,'newButton')" size="small" type="text" :content="getButtonInfo($table,row,'name','newButton')" icon="vxe-icon-edit" @click="getButtonInfo($table,row,'btn','newButton')"></vxe-button>
<vxe-button v-if="getButtonShow($table,row,'newButtonTwo')" size="small" type="text" :content="getButtonInfo($table,row,'name','newButtonTwo')" icon="vxe-icon-edit" @click="getButtonInfo($table,row,'btn','newButtonTwo')"></vxe-button>
<vxe-button size="small" type="text" content="编辑" icon="vxe-icon-edit" v-if="hasEditRow"
@click="editRowClick(row)"></vxe-button>
</template>
<vxe-button size="small" type="text" status="danger" content="删除" icon="vxe-icon-delete" v-if="hasdelRow"
@click="delRowClick(row)"></vxe-button>
</template>
<template #operateNew="{$table, row, column }">
<template v-if="hasEditRows(row)">
<vxe-button size="small" type="text" content="取消" icon="vxe-icon-close" @click="cancelRowClick"></vxe-button>
<vxe-button size="small" type="text" status="primary" content="保存" icon="vxe-icon-check"
@click="saveRowClick(row)"></vxe-button>
</template>
<template v-else>
<vxe-button v-if="getButtonShow($table,row,'newButton')" size="small" type="text" :content="getButtonInfo($table,row,'name','newButton')" icon="vxe-icon-edit" @click="getButtonInfo($table,row,'btn','newButton')"></vxe-button>
<vxe-button v-if="getButtonShow($table,row,'newButtonTwo')" size="small" type="text" :content="getButtonInfo($table,row,'name','newButtonTwo')" icon="vxe-icon-edit" @click="getButtonInfo($table,row,'btn','newButtonTwo')"></vxe-button>
<vxe-button size="small" type="text" content="编辑" icon="vxe-icon-edit" v-if="hasEditRow"
@click="editRowClick(row)"></vxe-button>
</template>
<vxe-button size="small" type="text" status="danger" content="删除" icon="vxe-icon-delete" v-if="hasdelRow"
@click="delRowClick(row)"></vxe-button>
</template>
</vxe-grid>
</template>
<script setup>
import { reactive,defineExpose } from "vue";
import { VXETable } from "vxe-table";
const { proxy } = getCurrentInstance();
const tableRef = ref();
const tableHeight = ref('0px');
const rowHeight = ref(44);
const headerHeight = ref(66);
const emit = defineEmits([
"insertRecords",
"updateRecords",
"delSelectData",
"allSaveRowData",
"saveRowData",
"delRowData",
"btnClick",
"cellDBLClick",
"pageChange",
"beforeEditRowsCallBack",
"validateFormMethod",
"beforeAddRowsCallBack",
"addRowsCallBack",
]);
const props = defineProps({
config: {
//表格配置项
type: Object,
default: {
//表格配置项
id: "table", //唯一标识
height: "auto", //表格的高度;支持铺满父容器或者固定高度
align: "center", //所有的列对齐方式
border: "none", //是否带有边框:false|default 默认显示边框,true|full 显示完整边框,outer 显示外边框,inner 显示内边框,none
round: true, //是否为圆角边框
stripe: true, //是否带有斑马纹
size: "medium", //表格的尺寸:medium, small, mini
// loading: false, //表格是否显示加载中
showHeader: true, //是否显示表头
columnConfig: {
//列配置信息
isCurrent: false, //当鼠标点击列头时,是否要高亮当前列
isHover: true, //当鼠标移到列头时,是否要高亮当前头
},
rowConfig: {
//行配置信息
keyField: "id", //自定义行数据唯一主键的字段名
isCurrent: false, //当鼠标点击行时,是否要高亮当前行
isHover: true, //当鼠标移到行时,是否要高亮当前行
},
editConfig: {
//可编辑配置项
trigger: "dblclick", //触发方式click(点击触发编辑),dblclick(双击触发编辑)
mode: "row", //编辑模式cell(单元格编辑模式),row(行编辑模式)
},
pagerConfig: {
//分页配置项
enabled: true, //是否启用
currentPage: 1, //当前页
pageSize: 10, //每页大小
total: 0, //总条数
autoHidden: true, //当只有一页时自动隐藏
pageSizes: [10, 15, 20, 50, 100], //每页大小选项列表
},
toolbarConfig: {
//工具栏配置
refresh: true, // 显示刷新按钮
import: false, // 显示导入按钮
export: false, // 显示导出按钮
zoom: true, // 显示全屏按钮
custom: true, // 显示自定义列按钮
},
},
},
colField: {
//新增时默认可编辑字段
type: String,
default: "",
},
additional: {
//新增时增加默认显示字段
type: Object,
default: {},
},
columns: {
//表格表头数据
type: Array,
default: [],
},
tableData: {
//表格数据
type: Array,
default: [],
},
loading: {
type: Boolean,
default: false,
},
addRowsText: {
//新增按钮文字
type: String,
default: "新增",
},
addRowsType: {
//新增按钮类型,1新增功能,2纯按钮用于其他操作
type: Number,
default: 1,
},
hasAddRows: {
//是否显示新增按钮
type: Boolean,
default: true,
},
hasDelSelect: {
//是否显示删除按钮
type: Boolean,
default: true,
},
hasAllSave: {
//是否显示保存
type: Boolean,
default: true,
},
hasEditRow: {
//是否显示操作编辑按钮
type: Boolean,
default: true,
},
hasdelRow: {
//是否显示操作删除按钮
type: Boolean,
default: true,
},
pagination: {
//分页
type: Object,
default: {
total: 0,
currentPage: 1,
pageSize: 10,
},
},
});
const { config, colField, addRowsType, hasAddRows, hasDelSelect } = props;
const gridOptions = reactive({
id: config.id, //唯一标识
height: tableHeight, //表格的高度;支持铺满父容器或者固定高度
maxHeight: config.maxHeight,
minHeight: 200, //最小高度
autoResize: true, //自动监听父元素的变化去重新计算表格
align: config.align, //所有的列对齐方式
border: config.border, //是否带有边框
round: config.round, //是否为圆角边框
stripe: config.stripe, //是否带有斑马纹
size: config.size, //表格的尺寸:medium, small, mini
// loading: config.loading, //表格是否显示加载中
showHeader: config.showHeader, //是否显示表头
showOverflow: true, //设置所有内容过长时显示为省略号
headerRowClassName: "headerRowClass", //表头的行附加 className
headerCellClassName: "headerCellClass", //头的单元格附加 className
rowClassName: "rowClass", //行附加 className
cellClassName: "cellClass", //单元格附加 className
keepSource: true, //保持原始值的状态,被某些功能所依赖,比如编辑状态、还原数据等
columnConfig: {
//列配置信息
isCurrent: config.columnConfig.isCurrent, //当鼠标点击列头时,是否要高亮当前列
isHover: config.columnConfig.isHover, //当鼠标移到列头时,是否要高亮当前头
resizable: true, //每一列是否启用列宽调整
},
rowConfig: {
//行配置信息
keyField: config.rowConfig.keyField, //自定义行数据唯一主键的字段名
isCurrent: config.rowConfig.isCurrent, //当鼠标点击行时,是否要高亮当前行
isHover: config.rowConfig.isHover, //当鼠标移到行时,是否要高亮当前行
},
sortConfig: {
//排序配置项
trigger: "cell", //触发方式
multiple: false, //是否启用多列组合筛选
remote: false, //所有列是否使用服务端排序,如果设置为 true 则不会对数据进行处理
},
filterConfig: {
//筛选配置项
remote: true, //所有列是否使用服务端筛选,如果设置为 true 则不会对数据进行处理
},
editConfig: {
//可编辑配置项
trigger: config.editConfig.trigger, //触发方式click(点击触发编辑),dblclick(双击触发编辑)
mode: config.editConfig.mode, //编辑模式cell(单元格编辑模式),row(行编辑模式)
showStatus: true, //只对 keep-source 开启有效,是否显示单元格新增与修改状态
beforeEditMethod(param) {
let shouldContinue;
emit("beforeEditRowsCallBack", param);
emit("validateFormMethod", param, val => {
shouldContinue = val
});
if (shouldContinue || shouldContinue === undefined) {
return true;
} else {
return false;
}
}
},
pagerConfig: {
//分页配置项
enabled: config.pagerConfig.enabled, //是否启用
currentPage: config.pagerConfig.currentPage, //当前页
pageSize: config.pagerConfig.pageSize, //每页大小
total: config.pagerConfig.total, //总条数
autoHidden: config.pagerConfig.autoHidden, //当只有一页时自动隐藏
pageSizes: config.pagerConfig.pageSizes, //每页大小选项列表
},
toolbarConfig: {
//工具栏配置
slots: {
//插槽
buttons: "toolbar_buttons",
},
refresh: config.toolbarConfig.refresh, // 显示刷新按钮
import: config.toolbarConfig.import, // 显示导入按钮
export: config.toolbarConfig.export, // 显示导出按钮
// print: true, // 显示打印按钮
zoom: config.toolbarConfig.zoom, // 显示全屏按钮
custom: config.toolbarConfig.custom, // 显示自定义列按钮
},
importConfig: {
//导入配置项
remote: false, //是否服务端导入
types: ["csv", "html", "xml", "txt"],
modes: ["covering", "insert"],
},
exportConfig: {
//导出配置项
remote: false, //是否服务端导出
types: ["csv", "html", "xml", "txt"], //可选文件类型列表
modes: ["current", "selected", "all"], //输出数据的方式列表
},
editRules: config.editRules, //校验规则配置项
// columns: config.columns,
// data: config.data,
});
const addRow = async (row) => {
const $table = tableRef.value;
//新增按钮
if (addRowsType === 1) {
//新增功能
if ($table) {
let shouldContinue = true;
emit("beforeAddRowsCallBack", $table, val => { shouldContinue = val });
if (shouldContinue || shouldContinue === undefined) {
const record = props.additional;
const { row: newRow } = await $table.insertAt(record, row);
await $table.setEditCell(newRow, colField);
emit("addRowsCallBack", newRow);
calculateTableHeight(null);
} else {
// 不继续执行下面的代码
return;
}
}
} else if (addRowsType === 2) {
//新增功能
if ($table) {
let shouldContinue = true;
console.log("shouldContinue");
emit("beforeAddRowsCallBack", $table, val => { shouldContinue = val });
if (shouldContinue || shouldContinue === undefined) {debugger
//纯按钮功能
emit("btnClick", "")
}
}
}
};
const delSelect = async () => {
//多选删除
const $table = tableRef.value;
const selectRecords = $table.getCheckboxRecords();
if ($table && selectRecords.length != 0) {
const type = await VXETable.modal.confirm("您确定要删除该数据?");
if (type === "confirm") {
emit("delSelectData", selectRecords);
$table.removeCheckboxRow();
}
} else {
VXETable.modal.message({
content: "请选择要删除的数据!",
status: "warning",
});
}
};
const allSave = async () => {
//多行编辑保存
const $table = tableRef.value;
if ($table) {
const $grid = tableRef.value;
const errMap = await $grid.validate(true);
if (errMap) {
VXETable.modal.message({ status: "error", content: "请填写相关内容!" });
} else {
// const updateRecords = $table.getUpdateRecords();
const { insertRecords, removeRecords, updateRecords } =
$grid.getRecordset();
if (insertRecords.length != 0) {
console.log(insertRecords);
//新增
emit("insertRecords", insertRecords);
} else if (updateRecords.length != 0) {
//修改
emit("updateRecords", updateRecords);
}
}
}
};
const hasEditRows = (row) => {
//显示编辑或保存按钮
const $grid = tableRef.value;
if ($grid) {
return $grid.isEditByRow(row);
}
return false;
};
const editRowClick = async (row) => {
//点击编辑
const $grid = tableRef.value;
if ($grid) {
const payload = {
grid: $grid,
row: row
};
$grid.setEditRow(row);
}
};
const cancelRowClick = () => {
//点击取消
const $grid = tableRef.value;
if ($grid) {
$grid.clearEdit();
}
};
const saveRowClick = async (row) => {
//编辑保存
const $grid = tableRef.value;
if ($grid) {
const errMap = await $grid.validate(true);
if (errMap) {
VXETable.modal.message({ status: "error", content: "请填写相关内容!" });
} else {
await $grid.clearEdit();
const { insertRecords, removeRecords, updateRecords } =
$grid.getRecordset();
if (insertRecords.length != 0) {
//新增
emit("insertRecords", insertRecords)
} else if (updateRecords.length != 0) {
//修改
emit("updateRecords", updateRecords)
}
}
}
};
const saveRowClickNew = async (row) => {
//编辑保存
const $grid = tableRef.value;
if ($grid) {
const errMap = await $grid.validate(true);
if (errMap) {
VXETable.modal.message({ status: "error", content: "请填写相关内容!" });
} else {
await $grid.clearEdit();
}
}
};
const delRowClick = async (row) => {
//单行删除
const type = await VXETable.modal.confirm("您确定要删除该数据?");
const $grid = tableRef.value;
if ($grid) {
if (type === "confirm") {
emit("delRowData", row);
await $grid.remove(row);
}
}
};
const cellDBLClickEvent = ({ row, column, $event }) => {
//双击单元格事件
emit("cellDBLClick", row, column, $event);
};
const pageChange = ({ currentPage, pageSize }) => {
//分页
emit("pageChange", currentPage, pageSize);
};
// 动态计算函数
function calculateTableHeight(rowHeightNew) {
const $table = tableRef.value
let newRowNum = 0;
let pageHeight = 0;
if (rowHeightNew) {
rowHeight.value = rowHeightNew;
}
if ($table) {
const insertRecords = $table.getInsertRecords()
newRowNum = insertRecords.length;
}
if (props.pagination.total > 10) {
pageHeight = 44;
}
const rowCount = props.tableData?.length
headerHeight.value = tableRef.value.$el.querySelector('.vxe-header--row')?.offsetHeight
tableHeight.value = (newRowNum + rowCount) * (rowHeight.value + 1.8) + (48.8 + pageHeight + 5) + headerHeight.value + 'px'
}
//监控config的变化,目前重新计算了表格高度
watch(config, (newConfig) => {
console.log('newConfig');
console.log(newConfig);
// 监听 config 变化的处理逻辑
gridOptions.minHeight = newConfig.minHeight;
gridOptions.maxHeight = newConfig.maxHeight;
rowHeight.value = newConfig.rowHeight;
gridOptions.pagerConfig.currentPage = newConfig.pagerConfig.currentPage;
gridOptions.pagerConfig.pageSize = newConfig.pagerConfig.pageSize;
gridOptions.pagerConfig.total = newConfig.pagerConfig.total;
}, { deep: true });
//自定义表格高度根据行数行高自动调节,监控props.tableData的变化
watch(() => props.tableData, (newTableData) => {
calculateTableHeight(config.rowHeight);
}, { deep: true });
//动态行样式
//1、获取响应式变量高度,其中rowHeight=ref(config.rowHeight);
function getRowStyle() {
return { height: rowHeight.value + 'px' };
}
function onEditActivated(params) {
const { row, $table } = params;
// 遍历每一列
$table.getColumns().forEach(column => {
// 判断是否包含某属性
if (column && column?.slots!==undefined) {
if(column.slots.hasOwnProperty('editActiveMethod')){
//当包含编辑激活回调函数时则调用该函数
column.slots.editActiveMethod(row,column);
}
}
});
}
function getButtonInfo($table, row, operate, buttonDom) {
const columns = $table.getColumns();
for (let i = 0; i < columns.length; i++) {
let column = columns[i];
if(column?.title !== '操作'){
continue;
}
if (column && column?.slots!==undefined) {
if(column.slots.hasOwnProperty(buttonDom)){
if (column.slots[buttonDom]) {
const template = column.slots[buttonDom];
if(operate === 'name'){
if(template.hasOwnProperty('name')){
return template.name;
}
}else if(operate === 'btn'){
if(template.hasOwnProperty('click')){
return template.click(row);
}
}
}
}
}
}
return '';
}
function getButtonShow($table, row, buttonDom) {
const columns = $table.getColumns();
for (let i = 0; i < columns.length; i++) {
let column = columns[i];
if(column?.title !== '操作'){
continue;
}
if (column && column?.slots!==undefined) {
if(column.slots.hasOwnProperty(buttonDom)){
return true;
}
}
}
return false;
}
async function addRows(num) {
const $table = tableRef.value;
const record = props.additional;
let records = new Array(num).fill(record);
emit("addRowsCallBack",records, results => records = results);
await $table.insertAt(records, null);
calculateTableHeight();
}
//自定义表格高度根据行数行高自动调节
// watch(() => tableRef?.value?.getInsertRecords(), (newTableData) => {
// calculateTableHeight();
// }, { deep: true });
function getInsertRows() {
const $table = tableRef.value;
return $table.getInsertRecords();
}
const allSubmit = async (callback) => {
//多行编辑保存
const $table = tableRef.value;
if ($table) {
const $grid = tableRef.value;
const errMap = await $grid.validate(true);
if (errMap) {
VXETable.modal.message({ status: "error", content: "请填写相关内容!" });
} else {
// const updateRecords = $table.getUpdateRecords();
const { insertRecords, removeRecords, updateRecords } =
$grid.getRecordset();
if (insertRecords.length != 0) {
console.log(insertRecords);
//新增
emit("insertRecords", insertRecords);
} else if (updateRecords.length != 0) {
//修改
emit("updateRecords", updateRecords);
}
await callback();
}
}
};
// // 将方法暴露出去
defineExpose({ addRows,getInsertRows,allSave,allSubmit });
</script>
<style lang="scss"></style>