1.使用步驟
1.1 安裝(個(gè)人建議安裝12.2及之后的版本)
npm install handsontable @handsontable/vue
1.2 使用
import { HotTable } from '@handsontable/vue'
<hot-table :settings="hotSettings" class="w-100 h-100"></hot-table>
hotSettings里面配置需要的屬性和回調(diào)函數(shù)
例如:
hotSettings: {
data: [
[1, 2, 3, 4],
[a, b, c, d]
]
}
2.基礎(chǔ)配置
2.1 data
data里面有兩種形式:
a. 二維數(shù)組 [[], []] (里面的每個(gè)數(shù)組代表一行數(shù)據(jù))
b. 對(duì)象數(shù)組 [{}, {}] (里面的每個(gè)對(duì)象代表一行數(shù)據(jù), 但是這是需要結(jié)合columns屬性一起使用)
2.2 columns
是一個(gè)對(duì)象數(shù)組([{}, {}]), 里面的每一個(gè)對(duì)象數(shù)據(jù)都代表對(duì)應(yīng)的這一列(hot-table的列排列位置就是根據(jù)這個(gè))
例如:
{
type: 'numeric', // 限制這一列的值得類型(data配置項(xiàng)中數(shù)據(jù)第一次渲染的時(shí)候不會(huì)報(bào)錯(cuò),修改的時(shí)候會(huì)提示)
readOnly: true, // 這一列只讀
data: 'column property' // 如果data配置項(xiàng)中數(shù)據(jù)是對(duì)象數(shù)組類型的話赘被,就可以使用這個(gè)屬性來(lái)制定這一列是對(duì)應(yīng)哪個(gè)屬性
title: 'column title', // 這一列的標(biāo)題(體現(xiàn)在表頭上)
dateFormat: 'YYYY-MM-DD', // 這一列如果type: date(會(huì)出現(xiàn)一個(gè)日期插件)口渔,這一列的選擇后的日期的形式就是'2022-11-09'
}
2.3 列寬, 行高
colWidths: 150, // 有多種格式(可以是數(shù)字,也可以是字符串('150px'), 也可以是數(shù)組([100, 200, ...])給每一列都設(shè)置一個(gè)寬度,也可以是方法(需要有個(gè)返回值return)淤堵, 也可以是undefined(由modifyColWidth鉤子使用))
rowHeights: '150px', // 同上
autoRowSize: true/false, // 給每一列設(shè)置一個(gè)自適應(yīng)寬度
autoColumnSize: true/false // 同上
2.4 表頭
rowHeaders: true/false/[]/function, // 行表頭 true采用默認(rèn)的表頭('1', '2', '3', ...) false禁用表頭 []自定義表頭([1, 2, 3, 4]) function自定義表頭(function(){ return 'AB'})
colHeaders: true/false/[]/function, // 列表頭 同上
2.5 hot-table根據(jù)寬度橫向擴(kuò)展
stretchH: 'all'/'last'/'none', // all擴(kuò)展全部列 last只擴(kuò)展最后一列 none默認(rèn)不擴(kuò)展
2.6 viewportColumnRenderingOffset
在網(wǎng)格視口之外呈現(xiàn)的列數(shù)
hot-table為了渲染的效率,它采用的默認(rèn)渲染方式是只渲染可視區(qū)域部分的內(nèi)容捶枢,有超出可視區(qū)域內(nèi)容通過(guò)橫向滾動(dòng)條來(lái)加載,就會(huì)出現(xiàn)幾個(gè)問(wèn)題(1: 用戶體驗(yàn)飞崖。橫向滾動(dòng)條會(huì)反復(fù)橫跳烂叔;2:預(yù)先設(shè)置的一些樣式不起作用(比如:合并行))
viewportColumnRenderingOffset: 100, // 可以通過(guò)設(shè)置超出最大列數(shù)的值,來(lái)讓hot-table一次性渲染所有的數(shù)據(jù)
2.7 設(shè)置hot-table的寬高
width: 500, // 可以是數(shù)字固歪,方法(需要一個(gè)返回值return)蒜鸡,CSS Unit
height: 500, // 同上
2.8 mergeCells(合并單元格)
是一個(gè)對(duì)象數(shù)組([{}, {}, ...]),里面的對(duì)象包含四個(gè)屬性:
row: 1, // 合并部分開(kāi)頭的行索引
col: 1, // 合并部分開(kāi)頭的列索引
rowspan: 2, // 合并的行數(shù)
colspan: 2, // 合并的列數(shù)
2.9 選中拖拽復(fù)制
fillHandle: true/false/'vertical'/'horizontal', // 'vertical'啟用垂直自動(dòng)填充 'horizontal'啟用橫向自動(dòng)填充
2.10 回調(diào)鉤子函數(shù)
beforeCreateRow(index) {} // index新行索引
新增行之前回調(diào),return false代表取消掉本次新增行操作
afterCreateRow(index, amount) {} // index新行索引 amount新增行數(shù)目
添加行后被調(diào)用
afterChange(changes, source) {} // changes是一個(gè)二維數(shù)組([[row, prop, oldValue, newValue]]) row修改單元格所在的行 prop修改單元格列對(duì)應(yīng)的屬性 oldValue修改前的值 newValue修改后的值
在一個(gè)或多個(gè)單元格被更改后觸發(fā)牢裳,當(dāng)使用編輯器輸入值或使用API更改值時(shí)逢防,會(huì)在任何情況下觸發(fā)更改
beforeRemoveRow(index, amount, physicalRows) {} // index刪除行索引 amount刪除行數(shù)量 physicalRows從數(shù)據(jù)源中刪除的物理行數(shù)組
行刪除前回調(diào),return false代表取消掉本次刪除行操作
afterOnCellMouseDown(event, value) {} // event點(diǎn)擊事件 value單元格對(duì)應(yīng)的位置信息
單元格點(diǎn)擊回調(diào)(包括左鍵蒲讯,右鍵忘朝,滾輪)
2.11 右鍵功能
contextMenu: false/true/[]/{items: {}} // 給右鍵配置功能
false 禁用ContextMenu插件(禁止掉右鍵功能)
true 啟用ContextMenu插件(使用默認(rèn)設(shè)置的右鍵功能)
['row_above', 'row_below'] 修改單個(gè)菜單選項(xiàng)('row_above'既是名字也是對(duì)應(yīng)的功能)
{items: {}} 可以自定義每個(gè)鍵
{
items: {
row_above: {
name: '上面插入一行'
}, // 鍵名是插件原本就有(也有已經(jīng)對(duì)應(yīng)好的操作)
remove_row: {
name: '移除當(dāng)前行'
}, // 鍵名是插件原本就有(也有已經(jīng)對(duì)應(yīng)好的操作)
insert_above_rows: {
name() {
const insertBox = document.getElementsByClassName('insert_input')[0]
if(insertBox) {
insertBox.addEventListener('keyup', (e) => {
console.log('e', e)
const event = e || window.event
const keyValue = event.which || event.keyCode || event.charCodes
if (keyValue === 13) {
const number = Number(event.target.value)
if (number !== NaN && number >= 0) {
this.alter('insert_row_above', window.clickRowIndex, number) // this指向hot-table alter方法允許你通過(guò)在指定位置添加或刪除行個(gè)列來(lái)更改網(wǎng)格結(jié)構(gòu)
const elMenu = document.getElementsByClassName('htMenu')[0]
elMenu.style.display = 'none' // 關(guān)閉右鍵的菜單欄
} else {
this.$message.warning('請(qǐng)輸入正確的數(shù)字')
}
} // 監(jiān)聽(tīng)回車事件
})
}
return `在上方插入<input class="insert_input" style="width: 40px; height: 20px;"></input>行`
},
isCommand: false, // 防止后續(xù)點(diǎn)擊命令關(guān)閉菜單
// 自定義時(shí)renderer與name的區(qū)別
renderer(hot, wrapper, row, col, prop, itemValue){
// 參數(shù)里面的hot是指向菜單欄的,所以沒(méi)辦法調(diào)用alter方法
}
}, // 鍵名是自定義的(操作也是自定義的--插入多行)
}
}
2.11 定義某些單元格特殊的配置或邏輯
cells(roww, column, prop) {} // row單元格所在行 column單元格所在列 prop(如果data是二維數(shù)組的話判帮,就是與column相同的數(shù)字局嘁;如果是對(duì)象數(shù)組的話,就是所在列的屬性)
使用案例:
cells() {
const cellProperties = {
readOnly: false
}
return cellProperties
}
cells會(huì)出現(xiàn)重復(fù)渲染的問(wèn)題
3. 其它用法
handsontable類似于el-table晦墙,對(duì)列表可以實(shí)現(xiàn)其它操作
3.1 hot-column類似于el-table的el-column
<hot-table :settings="hotSettings">
<hot-column :width="120" title="headerTitle" data="dataColumnProperty"></hot-column>
</hot-table>
這一塊比較靈活悦昵,它可以單獨(dú)設(shè)置某一些屬性,像title晌畅、data這種旱捧,也可以像hot-table一樣一個(gè)settings掛在所有的參數(shù)配置項(xiàng),
還是單獨(dú)設(shè)置一部分屬性踩麦,再使用settings屬性枚赡,hot-table也可以同樣如此操作。
3.2 類似于插槽的操作
要將組件標(biāo)記為Handsontable渲染器谓谦,只需為其添加一個(gè)hot-renderer屬性贫橙,通常作為渲染函數(shù)的參數(shù)將被注入到渲染組件的$data對(duì)象中,有以下幾個(gè)數(shù)據(jù):
row 行索引
col 列索引
prop 如果data是二維數(shù)組的話反粥,就是與column相同的數(shù)字卢肃;如果是對(duì)象數(shù)組的話,就是所在列的屬性
TD 單元格HTML節(jié)點(diǎn)
cellProperties 已編輯單元格的cellProperties對(duì)象
例子:
<hot-table :settings="hotSettings">
<hot-column>
<div hot-renderer>類似于插槽操作</div>
</hot-column>
</hot-table>
特殊點(diǎn):使用這種操作才顿,并且想拿到數(shù)據(jù)(插槽數(shù)據(jù)),就不能直接將節(jié)點(diǎn)掛載上去莫湘,需要如下操作:
1. 定義一個(gè)組件實(shí)例
const CustomRenderer = {
template: '<div>{{ row }} {{ col }} {{ value }}</div>'
}
2. 將這個(gè)組件實(shí)例在components里聲明
components: {
CustomRenderer
}
3. 使用
<hot-table :settings="hotSettings">
<hot-column>
<CustomRenderer hot-renderer>類似于插槽操作</CustomRenderer>
</hot-column>
</hot-table>
插槽數(shù)據(jù)會(huì)被自動(dòng)注入到渲染組件的$data對(duì)象中
3.3 還可以通過(guò)配置項(xiàng)columns實(shí)現(xiàn)類似于插槽的操作
columns: [
{
renderer(instance, td, row, col, prop, value) {
const img = document.createElement('img')
img.src = value
td.innerText = ''
td.appendChild(img)
return td
}
}
]
4. 注意點(diǎn)
4.1 表頭數(shù)據(jù)只能進(jìn)行列合并,不能進(jìn)行行合并
4.2 獲取hot-table數(shù)據(jù)this.$refs.hotTable.hotInstance.getData() // 這個(gè)方法拿到的只是每個(gè)單元格里面的數(shù)據(jù)
所以這兒如果使用的是簡(jiǎn)單類型的表格郑气,并且數(shù)據(jù)還是對(duì)象數(shù)組類型的幅垮,可以采用這種方法:
采用事先定義好的columns,然后使用this.$set(this.hotSettings, 'data', this.dataList)就可以在dataList中拿到實(shí)時(shí)變化的數(shù)據(jù)
4.3 如果一個(gè)單元格要區(qū)分單擊事件和雙擊事件:
可以使用afterOnCellMouseDown(單元格點(diǎn)擊回調(diào))回調(diào)方法尾组,再利用 節(jié)流 方法來(lái)分別執(zhí)行
例子:
hotSettings: {
afterOnCellMouseDown: (event, val) => {
// event.button 判斷是左鍵點(diǎn)擊
if (event.button === 0) {
this.indexCount += 1 // indexCount是data中聲明的變量忙芒,用來(lái)記錄點(diǎn)擊了幾次
if (this.indexCount % 2 === 0) {
// 點(diǎn)擊次數(shù)為雙數(shù)
this.preTime = this.newTime // preTime是data中聲明的變量示弓,用來(lái)記錄上一次點(diǎn)擊的時(shí)間
this.newTime = Date.now() // preTime是data中聲明的變量,用來(lái)記錄當(dāng)前點(diǎn)擊的時(shí)間
} else {
this.newTime = Date.now()
}
// event事件 val坐標(biāo)位置
console.log('val', val)
this.debouncedTip(event, val) // debouncedTip是data中聲明的變量呵萨,用來(lái)綁定節(jié)流函數(shù)返回的結(jié)果(閉包)
}
}, // 單元格點(diǎn)擊回調(diào)
}
在created中:
const throttle = (fn, wait) => {
let timer = null
return (event, val) => {
if (!this.preTime) {
this.preTime = 0
}
console.log('nowTime - preTime', this.newTime, this.preTime)
if (!timer) {
timer = setTimeout(() => {
if (this.newTime - this.preTime > wait) {
fn(event, val)
} // wait是在debouncedTip設(shè)置的節(jié)流時(shí)間奏属,如果this.newTime - this.preTime < wait則判斷定位單擊
timer = null
}, 300)
}
}
} // 節(jié)流函數(shù)
this.debouncedTip = throttle((event, val) => {
// 要執(zhí)行的操作
}, 300)