el-table動(dòng)態(tài)渲染列、可編輯單元格、虛擬無(wú)縫滾動(dòng)的實(shí)現(xiàn)
針對(duì)日常開(kāi)發(fā)的組件二次封裝、方案設(shè)計(jì)實(shí)現(xiàn)。包括對(duì)el-table的動(dòng)態(tài)渲染、單元格編輯;對(duì)于無(wú)縫滾動(dòng)的實(shí)現(xiàn),優(yōu)化大數(shù)據(jù)量下的頁(yè)面卡頓問(wèn)題。
1. el-table實(shí)現(xiàn)動(dòng)態(tài)渲染列
常規(guī)使用el-table
<template>
<el-table
ref="multipleTable"
:data="data"
>
<el-table-column prop="family_name" label="姓名" align="center">
</el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button
@click="handleEdit(scope.row)"
type="text"
size="small"
>用戶(hù)信息</el-button
>
</template>
</el-table-column>
</el-table>
</template>
<script>
export default {
data(){
return {
data:[]
}
},
methods:{
handleEdit(){}
}
}
</script>
表格比較長(zhǎng)時(shí),需要些好多的el-table-column;所以想通過(guò)動(dòng)態(tài)渲染的方式循環(huán)渲染出列項(xiàng)。
官方給出的formatter格式化列項(xiàng)輸出的方法只能格式化文本。無(wú)法渲染VNode。
嘗試通過(guò)v-html綁定,報(bào)錯(cuò)h is not a function
// ...
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<div v-html="item.render(scope)"></div>
</template>
</el-table-column>
解決辦法,通過(guò)render方法提供CreateElement函數(shù)。新創(chuàng)建一個(gè)組件RenderColumn
RenderColumn.vue
<script>
export default {
props:{
render:{
type:Function,
default:()=>()=>{}
},
scope:{
type:Object,
default:()=>{}
}
},
render(h){
const { row, column, $index } = this.scope
return this.render(h,row,column,$index)
}
}
</script>
在渲染表格時(shí)調(diào)用,主要在于需要給render方法傳入CreateElement方法。
<template>
<el-table
ref="multipleTable"
:data="data"
>
<el-table-column v-for="item in columns" :label="item.lable" :prop="item.prop">
<template slot-scope="scope">
<render-column v-if="item.render" :render="item.render" :scope="scope" />
<span v-else>{{scope.row[item.prop]}}</span>
</template>
</el-table-column>
</el-table>
</template>
<script>
export default {
data(){
let $this = this
return {
data:[],
columns:[
{
label:'姓名',
prop:'name'
},
{
label:'操作',
render(h,row,column){
return <el-button
onClick={$this.handleEdit(row)}
type="text"
size="small"
>用戶(hù)信息</el-button>
}
}
]
}
},
methods:{
handleEdit(){}
}
}
</script>
vue-cli腳手架已經(jīng)繼承了JSX的語(yǔ)法,可以直接書(shū)寫(xiě)。
2. el-table實(shí)現(xiàn)單元格的編輯
實(shí)現(xiàn)單元格的編輯,實(shí)現(xiàn)編輯組件EditCell.vue
邏輯的核心點(diǎn):
非編輯狀態(tài)下,展示當(dāng)前列項(xiàng)值,通過(guò)點(diǎn)擊事件,單元格進(jìn)入可編輯狀態(tài)。并可通過(guò)
this.$refs.input.focus()聚焦數(shù)據(jù)
el-input主要在于處理完成輸入、enter鍵后完成編輯狀態(tài)。當(dāng)完成編輯時(shí),如果傳入了校驗(yàn)函數(shù)。則需調(diào)用函數(shù)進(jìn)行校驗(yàn)。并通過(guò)
el-popover展示。
<template>
<div class="edit-cell">
<el-popover :value="validateMsg !== ''" trigger="manual">
<div slot="reference">
<span v-if="!editable" @click="handleEditable"
>{{ editValue }}
<i class="el-icon-edit"></i>
</span>
<el-input
ref="input"
autofocus
v-else
v-model="editValue"
@change="handleEditable"
@blur="handleEditable"
/>
</div>
<span style="color: #f5222d">{{ validateMsg }}</span>
</el-popover>
</div>
</template>
<script>
export default {
name: "edit-cell",
props: {
value: String,
validator: {
type: Function,
default: () => null
}
},
data() {
return {
editValue: "",
editable: false,
validateMsg: ""
};
},
mounted() {
this.editValue = this.value;
},
methods: {
handleEditable() {
if (this.editable && typeof this.validator === "function") {
const err = this.validator(this.editValue);
if (err) {
this.validateMsg = err;
return;
}
this.validateMsg = "";
}
this.editable = !this.editable;
if (this.editable) {
this.$nextTick(() => {
this.$refs.input.focus();
});
}
this.$emit("change", this.editValue);
}
}
};
</script>
如果要實(shí)現(xiàn)整行row的編輯,可給每一行數(shù)據(jù)追加屬性editable,整合編輯時(shí)更改屬性,切換為編輯狀態(tài)。
切入編輯狀態(tài)el-input本來(lái)想通過(guò)autofocus獲取焦點(diǎn)的。但沒(méi)有用,使用了ref組件內(nèi)部的方法。
到此這篇關(guān)于el-table動(dòng)態(tài)渲染列、可編輯單元格、虛擬無(wú)縫滾動(dòng)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)el-table動(dòng)態(tài)渲染列、可編輯單元格、虛擬無(wú)縫滾動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
3. 實(shí)現(xiàn)虛擬無(wú)縫滾動(dòng)seamlessScroll
使用過(guò)vue-seamless-scroll,可實(shí)現(xiàn)數(shù)據(jù)的無(wú)縫滾動(dòng)。但當(dāng)數(shù)據(jù)量超過(guò)大幾千時(shí),頁(yè)面就會(huì)變的很卡。通過(guò)看源代碼實(shí)現(xiàn),加入5000的數(shù)據(jù)量,需要渲染10000個(gè)DOM節(jié)點(diǎn)。
通過(guò)之前虛擬列表的思想,實(shí)現(xiàn)一個(gè)虛擬無(wú)縫滾動(dòng)組件
實(shí)現(xiàn)滾動(dòng)的主要API
transform:translate(0px,0px),在水平、垂直方向上進(jìn)行平移數(shù)據(jù)列表window.requestAnimationFrame(()=>{})在瀏覽器下次重繪時(shí)調(diào)用回調(diào)函數(shù),通常為60次/s
實(shí)現(xiàn)的主要邏輯:
組件掛載或者數(shù)據(jù)
data變化時(shí)進(jìn)行數(shù)據(jù)初始化init()init方法用于調(diào)用數(shù)據(jù)切割滾動(dòng)方法。其中一個(gè)參數(shù)virtual用于顯示控制如果數(shù)據(jù)量不大時(shí),就沒(méi)必要虛擬滾動(dòng)了。在
move方法中,通過(guò)每一幀的渲染更新,回調(diào)函數(shù)處理this.translateY -= this.speed平移數(shù)據(jù)列表。在
splitData中則處理數(shù)據(jù)切割,判斷如果不需要虛擬滾動(dòng)時(shí),則加載展示所有的數(shù)據(jù)。隨后監(jiān)聽(tīng)了
translateY的變化,用于處理虛擬列表的滾動(dòng)分頁(yè)邏輯/** * 如果平移的距離大于分頁(yè)*每項(xiàng)的長(zhǎng)度,進(jìn)行數(shù)據(jù)滾動(dòng)重置 **/ handleDataScorll() { if ( Math.abs(this.translateY) < this.pageOptions.pageSize * this.itemWidth ) { return; } // this.stop(); // 第一頁(yè)已滾動(dòng)完成 if (this.virtual) { this.splitData(); } this.translateY = 0; },
核心的JS邏輯,實(shí)現(xiàn)的相關(guān)方法。
export default {
// ...
mounted() {
// 復(fù)制數(shù)據(jù),數(shù)據(jù)倉(cāng)
this.copyData = [...this.data];
// 切割數(shù)據(jù)
this.init();
},
computed: {
boxStyle() {
return {
transform: `translate(0, ${this.translateY}px )`,
};
},
total() {
return this.data.length;
},
},
watch: {
data(newData) {
this.copyData = [...newData];
this.init();
},
translateY() {
this.handleDataScorll();
},
},
methods: {
init() {
if (!this.virtual) {
// 非虛擬列表管滾動(dòng),則直接展示所有數(shù)據(jù)
this.pageOptions.pageSize = this.total;
}
if (this.total > 0) {
this.splitData();
this.move();
}
},
move() {
this.stop();
this.animationFrame = requestAnimationFrame(() => {
if (this.total > 0) {
this.translateY -= this.speed;
}
this.move();
});
},
splitData() {
if (!this.virtual) {
this.preData = [...this.copyData];
this.nextData = [...this.copyData];
return;
}
// 只有在虛擬列表時(shí),才調(diào)換數(shù)據(jù)位置
this.copyData = [...this.copyData, ...this.preData];
// pre
this.preData = this.copyData.splice(0, this.pageOptions.pageSize);
// next
this.nextData = this.copyData.slice(0, this.pageOptions.pageSize);
},
/**
* 監(jiān)聽(tīng)滾動(dòng)的距離
*/
handleDataScorll() {
if (
Math.abs(this.translateY) <
this.pageOptions.pageSize * this.itemWidth
) {
return;
}
// this.stop();
// 第一頁(yè)已滾動(dòng)完成
if (this.virtual) {
this.splitData();
}
this.translateY = 0;
},
stop() {
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame);
}
}
},
};
示例中僅實(shí)現(xiàn)豎向滾動(dòng),橫向滾動(dòng)后續(xù)會(huì)追加props屬性mode進(jìn)行邏輯處理。
4. 通過(guò)el-select實(shí)現(xiàn)聯(lián)級(jí)選擇
Element提供的Cascader,但設(shè)計(jì)師可能需要的是并排的多個(gè)下拉,進(jìn)行控制。
主要的實(shí)現(xiàn)邏輯:
通過(guò)level指定聯(lián)動(dòng)選擇的層級(jí)數(shù)量。通過(guò)循環(huán)渲染出
el-select,還有最關(guān)鍵的實(shí)現(xiàn)分級(jí)數(shù)據(jù), 從data中分級(jí)出每一級(jí)level數(shù)據(jù)。視圖中則通過(guò)
optionsData[index]獲取數(shù)據(jù)
optionsData: function () {
let arr = [[...this.data]]
for (let id of this.selectedData) {
if (!id) {
arr.push([])
break
}
let data = arr[arr.length - 1].find((item) => item.id === id)
if (!data) {
arr.push([])
break
}
arr.push(data.children || [])
}
return arr
}
最重要的是保證
selectedData為層級(jí)深度長(zhǎng)度的數(shù)組,這樣才能渲染出正確數(shù)量的el-select每一層級(jí)的事件
change通過(guò)index來(lái)更新選中的數(shù)據(jù)selelctData
<template>
<div class="cascade-select-city">
<el-select
placeholder="請(qǐng)選擇"
v-for="(val, index) in selectedData"
:key="index"
:value="selectedData[index]"
@change="handleSelect($event, index)"
>
<el-option value="">請(qǐng)選擇</el-option>
<el-option
v-for="item in optionsData[index]"
:key="item.id"
:label="item.name"
:value="item.name"
/>
</el-select>
</div>
</template>
<script>
export default {
name: 'cascade-select',
props: {
/**
* 用于自定義級(jí)聯(lián)數(shù)據(jù)
*/
data: {
type: Array,
default: () => []
},
/**
* 聯(lián)動(dòng)層級(jí)數(shù)量
*/
level: {
type: Number,
default: 1
},
/**
* 綁定數(shù)據(jù)
*/
value: {
type: Array,
default: () => []
}
},
data () {
return {
selectedData: new Array(this.level).fill('')
}
},
mounted () {
},
watch: {
value (val, oldVal) {
if (JSON.stringify([val]) !== JSON.stringify([this.selectedData])) {
//
this.selectedData = [...val]
}
}
},
computed: {
/**
* 處理層級(jí)數(shù)據(jù)
*/
optionsData: function () {
let arr = [[...this.data]]
for (let id of this.selectedData) {
if (!id) {
arr.push([])
break
}
let data = arr[arr.length - 1].find((item) => item.AreaId === id)
if (!data) {
arr.push([])
break
}
arr.push(data.children || [])
}
return arr
}
},
methods: {
/**
* 處理聯(lián)動(dòng)的select
*/
handleSelect (selected, level) {
// 更新值
this.selectedData = this.selectedData.map((val, index) => {
if (index < level) {
return val
}
return index === level ? selected : ''
})
this.$emit('change', this.selectedData)
}
}
}
</script>
組件僅實(shí)現(xiàn)了data為靜態(tài)數(shù)據(jù)時(shí)的邏輯處理,如果數(shù)據(jù)是動(dòng)態(tài)的呢,比如異步加載聯(lián)動(dòng)數(shù)據(jù)。
npm-虛擬無(wú)縫滾動(dòng)組件seamless-scroll
到此這篇關(guān)于el-table動(dòng)態(tài)渲染列、可編輯單元格、虛擬無(wú)縫滾動(dòng)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)el-table動(dòng)態(tài)渲染列、可編輯單元格、虛擬無(wú)縫滾動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Javascript獲取當(dāng)前日期的農(nóng)歷日期代碼
這篇文章主要介紹了利用Javascript獲取當(dāng)前日期的農(nóng)歷日期代碼,很實(shí)用,需要的朋友可以參考下2014-10-10
原生js實(shí)現(xiàn)焦點(diǎn)輪播圖效果
本文主要分享了原生js實(shí)現(xiàn)焦點(diǎn)輪播圖效果的示例代碼,并解析了實(shí)例中的注意點(diǎn)。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-01-01
利用hasOwnProperty給數(shù)組去重的面試題分享
obj.hasOwnProperty(attr) 判斷是否是原型中的屬性,false就是原型中的屬性,下面這篇文章主要給大家介紹了一道利用hasOwnProperty給數(shù)組去重的面試題,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-11-11
動(dòng)態(tài)調(diào)整textarea中字體的大小代碼
用js批量輸出select事件控制textarea中字體的大小的代碼。2009-12-12
JS實(shí)現(xiàn)的另類(lèi)手風(fēng)琴效果網(wǎng)頁(yè)內(nèi)容切換代碼
這篇文章主要介紹了JS實(shí)現(xiàn)的另類(lèi)手風(fēng)琴效果網(wǎng)頁(yè)內(nèi)容切換代碼,通過(guò)JavaScript響應(yīng)鼠標(biāo)事件動(dòng)態(tài)操作頁(yè)面元素樣式屬性實(shí)現(xiàn)手風(fēng)琴效果,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09

