Handsontable官网 Options API reference - JavaScript Data Grid | Handsontable
使用Handsontable插件,下拉累计叠加功能
HotTableDialog.vue组件
<template>
<el-dialog
v-model="visible"
title="批量编辑"
width="630px"
:close-on-click-modal="false"
append-to-body
:lock-scroll="false"
>
<my-hot-table
v-if="visible"
width="100%"
height="auto"
ref="myHotTableComponentRef"
:solutionId="solutionId"
@change-data="submitFormOk"
></my-hot-table>
<template #footer>
<el-button @click="handleCancel">取 消</el-button>
<el-button type="primary" @click="submitForm">确 定</el-button>
</template>
</el-dialog>
</template>
<script lang="ts">
import myHotTable from "./myHotTable.vue";
import {
ref,
reactive,
onMounted,
toRefs,
getCurrentInstance,
computed,
nextTick,
} from "vue";
export default {
props: {
visible: {
type: Boolean,
default: false,
},
id: {
type: String,
default: "",
},
solutionId: {
type: String,
default: "",
},
data: {
type: Array,
default: () => {
return [];
},
},
},
components: { myHotTable },
emits: ["update:visible", "submit-ok"],
setup(props, ctx) {
const { proxy }: any = getCurrentInstance();
const myHotTableComponentRef = ref<any>(null);
const visible = computed({
get: () => {
if (props.visible) {
nextTick(()=>{
myHotTableComponentRef.value.getList();
})
}
return props.visible;
},
set: (val) => {
ctx.emit("update:visible", val);
},
});
onMounted(() => {
});
const handleCancel = () => {
visible.value = false;
};
const submitForm = () => {
myHotTableComponentRef.value.submitForm();
};
const submitFormOk=()=>{
ctx.emit('submit-ok')
}
return {
submitForm,
submitFormOk,
handleCancel,
visible,
myHotTableComponentRef,
};
},
};
</script>
myHotTable组件
<template>
<hot-table
width="100%"
height="auto"
:settings="hotSettings"
:minRows="tableData.length"
ref="hotTableComponent"
></hot-table>
</template>
<script lang="ts">
import { HotTable } from '@handsontable/vue3';
import 'handsontable/dist/handsontable.full.css'; //表格样式
import { registerAllModules } from 'handsontable/registry'; // 在线编辑样式
import 'handsontable/languages/zh-CN'; //汉语包
import { cloneDeep } from 'lodash';
import { useHotTable } from './hotTableData';
registerAllModules();
import {
ref,
reactive,
onMounted,
toRefs,
getCurrentInstance,
computed,
nextTick,
} from "vue";
import { ElMessage } from "element-plus";
export default {
props: {
id: {
type: String,
default: '',
},
solutionId: {
type: String,
default: "",
},
data: {
type: Array,
default: () => {
return [];
},
},
},
components: { HotTable },
emits: ['update:visible', 'change-data'],
setup(props, ctx) {
const { proxy }: any = getCurrentInstance();
const hotTableComponent = ref<any>(null);
const tableData = ref<Array<any>>([]);
const { afterChange, afterSelectionByProp, afterSelectionEndByProp } =
useHotTable(hotTableComponent);
const hotSettings = ref({
language: 'zh-CN', // 官方汉化
licenseKey: 'non-commercial-and-evaluation', //去除底部非商用声明
colHeaders: ['名称', '点属性', '高(米)', '类型'],
columns: [
{
data: 'name',
readOnly: false,
type: 'text',
dateFormat: null,
source: null,
strict: false,
numericFormat: null,
},
{
data: 'typeText',
readOnly: false,
type: 'dropdown',
dateFormat: null,
source: ["转角", "直角"],
// editor: 'select',
// selectOptions: ["转角", "直角"],
strict: true,
numericFormat: null,
},
{
data: 'height',
readOnly: false,
type: 'numeric',
dateFormat: null,
source: null,
strict: false,
numericFormat: null,
},
{
data: 'insulatorType',
readOnly: false,
type: 'text',
dateFormat: null,
source: null,
strict: true,
numericFormat: null,
},
],
data: [],
currentRowClassName: 'currentRow', // 突出显示行
currentColClassName: 'currentCol', // 突出显示列
minSpareRows: 0, //行留白
autoWrapRow: false, //自动换行
trimWhitespace: false, //去除空格
rowHeaderWidth: 100, //单元格宽度
stretchH: 'all',
renderAllRows: false,
rowHeaders: true, // 行标题 布尔值 / 数组/ 函数
formulas: false, //公式
copyable: true, // 允许键盘复制
wordWrap: false, // 自动换行
copyPaste: true, //复制粘贴
filters: false, //允许通过内置组件或API过滤表数据
search: false,
fixedColumnsLeft: 0, // 固定左边列数
fixedRowsTop: 0, // 固定上边列数
columnSorting: true, // 排序
contextMenu: false, //右键菜单
afterChange: afterChange,
afterSelectionByProp: afterSelectionByProp,
afterSelectionEndByProp: afterSelectionEndByProp,
});
onMounted(() => {
getList();
});
const getList = () => {
if (!props.solutionId) return;
getTowersList(props.solutionId)
.then((res) => {
loading.close();
let data = cloneDeep(res.data);
data.map((item: any) => {
item.typeText = item.type == 0 ? "转角" : "直线";
});
tableData.value = data;
nextTick(() => {
hotTableComponent.value?.hotInstance.loadData(tableData.value);
});
})
.catch((e) => loading.close());
};
const submitForm = () => {
let data = hotTableComponent.value.hotInstance.getSourceData();
console.log(">>>>>>>>",data);
let newTowerList = cloneDeep(data);
newTowerList.map((item: any) => {
item.type = item.typeText == "转角" ? 0 : 1;
delete item.typeText;
});
console.log(newTowerList);
if (!newTowerList.length) {
ElMessage.warning("数据不能为空");
return;
}
};
return {
submitForm,
hotSettings,
tableData,
hotTableComponent,
};
},
};
</script>
<style>
.htContextMenu:not(.htGhostTable) {
z-index: 9999999 !important;
}
</style>
hotTableData.ts
import { cloneDeep } from "lodash";
import { Ref, ref, ToRef } from "vue";
export const useHotTable = (hot: any) => {
let tableList: { name: any }[] = []; //获取的初始状态,只得到一次
let autofillTarget = false;
/**
* 更改触发
* @param changeData
* @param source
*/
const afterChange = (changeData: any, source: any) => {
if (source == "Autofill.fill") {
autofillTarget = true;
} else {
autofillTarget = false;
}
};
/**
* 获取拖动放下记录下来
* @param r 选中的起始纵坐标
* @param p 选中的结束的纵坐标
* @param r2 p为起始的属性
* @param p2 结束的属性
* @returns
*/
const afterSelectionByProp = (r: any, p: any, r2: any, p2: any) => {
console.log(">>>>>>>>>r", r);
console.log(">>>>>>>>>r2", r2);
tableList = [];
if (p != p2 || p != "name") {
//横向拖动表格不做任何效果,且只有名称需要递增效果
autofillTarget = false;
return false;
}
if (r == r2) {
//如果只点击一个格子,不做任何操作
autofillTarget = false;
return false;
}
tableList = cloneDeep(hot.value.hotInstance.getSourceData()); //获取表格当前的数据
console.log(">>>>>>>>>>>>", tableList);
let name = hot.value.hotInstance.getSourceData()[r].name; //当前单元格中的箱号值
for (let j = r; j < r2; j++) {
//循环选中单元格
name = tableList[j].name ? tableList[j].name : name;
let numList = name.match(/\d+(.\d+)?/g); //提取字符串的所有数字,得到数字数组 // ["2.75","3.65"]
if (numList) {
let lastNumber = numList[numList.length - 1]; //获取最后一个连续的数字
let lastIndex = name.lastIndexOf(lastNumber); //查找最后一个数字在name中的下标位置
let newNumber = lastNumber * 1 + 1; //数字叠加
let newStr =
name.substring(0, lastIndex) +
newNumber +
name.substring(lastNumber.length + lastIndex, name.length);
console.log(">>>>>>>>>>新数值", newStr);
tableList[j * 1 + 1].name = newStr;
}
}
};
/**
* 在选择一个或多个单元格后激发(例如鼠标悬停时)。此钩子函数为了获取拖动放下记录下来
* @param index
* @param direction
* @param data
* @param baseRange
*/
const afterSelectionEndByProp = (
index: any,
direction: any,
data: any,
baseRange: any
) => {
if (autofillTarget && tableList.length > 0) {
hot.value.hotInstance.loadData(tableList);
autofillTarget = false;
}
};
return {
afterChange,
afterSelectionEndByProp,
afterSelectionByProp,
};
};