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" >用戶信息</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" >用戶信息</el-button> } } ] } }, methods:{ handleEdit(){} } } </script>
vue-cli
腳手架已經(jīng)繼承了JSX的語(yǔ)法,可以直接書(shū)寫。
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-12JS實(shí)現(xiàn)的另類手風(fēng)琴效果網(wǎng)頁(yè)內(nèi)容切換代碼
這篇文章主要介紹了JS實(shí)現(xiàn)的另類手風(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