我們需要實(shí)現(xiàn)的功能
- 展示數(shù)據(jù)(帶邊框拟淮,緊湊型/松散型网杆,斑馬條紋)
- 選中數(shù)據(jù)(單選/全選)
- 展示排序
- 固定表頭/列
- 可展開
組件的基本結(jié)構(gòu)
- table.vue
<template>
<div>
<table>
<thead>
<tr>
<th>#</th>
<th v-for="column in columns">
{{column.text}}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in dataSource">
<td>{{index}}</td>
<template v-for="column in columns">
<!--顯示dataSource中對應(yīng)表頭字段里的內(nèi)容-->
<td>{{item[column.field]}}</td>
</template>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: "LiFaTable",
props: {
columns: {
type: Array,
required: true
},
dataSource: {
type: Array,
required: true
}
}
}
</script>
- demo.vue
<lf-table :columns="columns" :data-source="dataSource"></lf-table>
data() {
return {
columns: [
//表頭每一列顯示的文本和字段
{text: '姓名', field: 'name'},
{text: '分?jǐn)?shù)', field: 'score'}
],
dataSource: [
{id: 1, name: '發(fā)發(fā)', score: 100},
{id: 2, name: '琳琳', score: 99}
]
}
},
實(shí)現(xiàn)checkbox全選和單選
思路: 通過單向數(shù)據(jù)流删豺,外界好傳入一個selectedItem,table接受這個selectedItem骡尽,默認(rèn)為空數(shù)組
單選:對表單內(nèi)容的checkbox添加change事件onChangeItem把每一列行的index积仗,item還有原生event别渔,如果是選中狀態(tài)就把傳入的所有數(shù)據(jù)的數(shù)組dataSource作為參數(shù)通過
refs.a.indeterminate = true,其他情況都等于false
- demo.vue
<div style="margin: 28px">
<lf-table :columns="columns" :data-source="dataSource"
bordered :selected-item.sync="selectedItem"
></lf-table>
</div>
- table.vue
<template>
<div class="lifa-table-wrapper">
<table class="lifa-table" :class="{bordered,compact,striped}">
<thead>
<tr>
<th>
<input type="checkbox" @change="onChangeItemAll($event)" ref="a">
</th>
<th v-if="numberVisible">#</th>
<th v-for="column in columns">
{{column.text}}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in dataSource">
<th>
<input type="checkbox" @change="onChangeItem(item, index, $event)"
:checked="onChecked(item)"
>
</th>
<td v-if="numberVisible">{{index+1}}</td>
<template v-for="column in columns">
<!--顯示dataSource中對應(yīng)表頭字段里的內(nèi)容-->
<td>{{item[column.field]}}</td>
</template>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: "LiFaTable",
props: {
columns: {
type: Array,
required: true
},
dataSource: {
type: Array,
required: true
},
selectedItem: {
type: Array,
default: ()=>[]
},
striped: {
type: Boolean,
default: true
},
//是否顯示索引
numberVisible: {
type: Boolean,
default: false
},
//是否帶邊框
bordered: {
type: Boolean,
default: false
},
//是否是緊湊型
compact: {
type: Boolean,
default: false
}
},
methods: {
onChangeItem(item, index, e){
let copy = JSON.parse(JSON.stringify(this.selectedItem))
if(e.target.checked){
copy.push(item)
}else{
copy.splice(copy.indexOf(item),1)
}
this.$emit('update:selectedItem',copy)
},
onChangeItemAll(e){
if(e.target.checked){
this.$emit('update:selectedItem',this.dataSource)
}else{
this.$emit('update:selectedItem',[])
}
},
onChecked(item){
return this.selectedItem.filter(n=>n.id === item.id).length > 0 ? true : false
}
},
watch: {
selectedItem(){
if(this.selectedItem.length === this.dataSource.length){
this.$refs.a.indeterminate = false
this.$refs.a.checked = true
}else if(this.selectedItem.length === 0){
this.$refs.a.indeterminate = false
}else{
this.$refs.a.indeterminate = true
}
}
}
}
</script>
<style scoped lang="scss">
@import 'var';
.lifa-table{
border-collapse: collapse;
border-spacing: 0;
border-bottom: 1px solid $gray;
width: 100%;
&.bordered{
border: 1px solid $gray;
td,th{
border: 1px solid $gray;
}
}
&.compact{
td,th{
padding: 4px;
}
}
&.striped{
tbody{
> tr{
&:nth-child(odd){
background: white;
}
&:nth-child(even){
background: #fafafa;
}
}
}
}
th,td{
border-bottom: 1px solid $gray;
text-align: left;
padding: 8px;
}
th{
color: #909399;
}
td{
color: #606266;
}
}
</style>
解決取消選中異常的bug
當(dāng)我們?nèi)x的時候稻据,比如我點(diǎn)擊第二個取消選中可最后一個也跟著取消選中了艾猜,原因就是因?yàn)樯羁截惖脑颍驗(yàn)樯羁截惡髷?shù)組里的對象和原先的不是同一個索引捻悯,所以不能通過indexOf查找匆赃,而要通過取消選中的時候,當(dāng)前取消選中項(xiàng)的id今缚,找到深拷貝后數(shù)組里的id不等于當(dāng)前選中項(xiàng)的id的元素算柳,然后傳給父組件
let copy = JSON.parse(JSON.stringify(this.selectedItem))
if(e.target.checked){
copy.push(item)
}else{
//取消選中狀態(tài):點(diǎn)擊當(dāng)前的checkbox保留數(shù)組中id不等于當(dāng)前id的項(xiàng)
copy= copy.filter(i=>i.id !== item.id)
}
this.$emit('update:selectedItem',copy)
},
watch: {
selectedItem(){
if(this.selectedItem.length === this.dataSource.length){
this.$refs.a.indeterminate = false
}else if(this.selectedItem.length === 0){
this.$refs.a.indeterminate = false
}else{
this.$refs.a.indeterminate = true
}
}
}
實(shí)現(xiàn)選中所有單選的checkbox后,全選的checkbox也跟著選中
錯誤寫法:
直接判斷selectedItem.length和dataSource.length的長度是否相等姓言,這樣雖然可以實(shí)現(xiàn)我們要的功能瞬项,但是是錯誤的寫法
<input type="checkbox" @change="onChangeItemAll($event)" ref="a" :checked="areAllItemChecked">
computed: {
areAllItemChecked(){
return this.selectedItem.length === this.dataSource
}
}
正確寫法:
我們需要判斷這兩個數(shù)組里的所有項(xiàng)的id是否都相等來判斷是否全選。
那么現(xiàn)在的問題是我們?nèi)绾闻袛鄡蓚€數(shù)組是一樣的何荚。比如this.dataSource=[{id:1},{id:2}]
和this.selectedItem = [{id:2},{id:1}]
我們該如何判斷兩個數(shù)組是一樣的哪囱淋?
我們無法通過遍歷判斷第一個dataSource的第一個是否等于selectedItem的第一個來判斷是否相等,因?yàn)樯厦娴捻樞虿灰粯邮奁彩窍嗟鹊摹?br>
所以我們需要
- 先對這兩個數(shù)組里的id進(jìn)行排序
this.dataSource.sort((a,b)=>a.id - b.id)
但是sort會改變原來的數(shù)組绎橘,所以我們需要先用map生成一個新的數(shù)組,然后在排序
areAllItemChecked(){
const a = this.dataSource.map(n=>n.id).sort()
const b = this.selectedItem.map(n=>n.id).sort()
if(a.length === b.length){
for(let i = 0;i<a.length;i++){
if(a[i] !== b[i]){
return false
}else{
return true
}
}
}
}
表格排序的實(shí)現(xiàn)
補(bǔ)充知識:在vue里唠倦,你不可能根據(jù)一個屬性檢查另一個屬性的合法性,因?yàn)槟阍趘alidate中拿不到實(shí)例(this)
export default{
props: {
name: 'lifa'
},
orderBy: {
type: Object,
default: ()=>({}),
validator(object){
console.log(this)//undefined
}
}
}
定義排序規(guī)則:
通過外界傳入一個ordreBy對象來定義
orderBy: {//true:開啟排序涮较,但是不確定asc desc
name: true,
score: 'desc'
},
如果想要某個列按升序排列就在當(dāng)前字段后加一個'asc'稠鼻,降序就加'score',默認(rèn)開啟排序狂票,但是不是升序也不是降序就用true候齿,然后在組件中接受這個orderBy
<th v-for="column in columns" :key="column.field">
<div class="lifa-table-header">
{{column.text}}
<!--如果對應(yīng)的key在orderBy這個對象里,就顯示-->
<span class="lifa-table-sorter" v-if="column.field in orderBy" @click="changeOrderBy(column.field)">
<lf-icon name="asc" :class="{active:orderBy[column.field] === 'asc'}"></lf-icon>
<lf-icon name="desc" :class="{active: orderBy[column.field] === 'desc'}"></lf-icon>
</span>
</div>
</th>
props: {
//通過什么排序
orderBy: {
type: Object,
default: ()=>({})
}
},
methods: {
changeOrderBy(key){
const copy = JSON.parse(JSON.stringify(this.orderBy))
if(copy[key] === 'asc'){
copy[key] = 'desc'
}else if(copy[key] === 'desc'){
copy[key] = true
}else{
copy[key] = 'asc'
}
this.$emit('update:orderBy',copy)
}
}
上面的代碼可以實(shí)現(xiàn)狀態(tài)的切換闺属,當(dāng)點(diǎn)擊的時候如果是升序就會變成降序慌盯,如果是降序就會變成默認(rèn)狀態(tài),否則就會變成升序掂器,然后需要在外界.sync一下
<lf-table :order-by.sync="orderBy"></lf-table>
然后我們只需要監(jiān)聽這個update:orderBy事件亚皂,當(dāng)事件觸發(fā)的時候執(zhí)行一個方法,來修改我們dataSource里的數(shù)據(jù)
<lf-table :order-by.sync="orderBy"
@update:orderBy="changeOrder"
></lf-table>
methods: {
changeOrder(data){
this.$nextTick(()=>{
let type
let arr = this.dataSource.map(item=>{
type = typeof item[this.key]
return item[this.key]
})
if( data[this.key]=== 'asc'){
if(type === 'number'){
arr.sort((a,b)=>a-b)
}else{
arr.sort((a, b) => b.localeCompare(a, 'zh-Hans-CN', {sensitivity: 'accent'}))
}
}else if(data[this.key] === 'desc'){
if(type === 'number'){
arr.sort((a,b)=>b-a)
}else{
arr.sort((a, b) => a.localeCompare(b, 'zh-Hans-CN', {sensitivity: 'accent'}))
}
}
arr.map((item,index)=>{
this.dataSource[index][this.key]=item
})
})
},
},
watch: {
//監(jiān)聽orderBy的變化国瓮,如果新值里的屬性值不等于舊值的屬性值說明當(dāng)前屬性變了灭必,拿到這個key狞谱,比如我一開始是{name: '發(fā)發(fā)',score:100}現(xiàn)在是{name: '發(fā)發(fā)',score:90},那么我們就可以拿到score這個key
orderBy(val,oldVal){
for(let key in this.orderBy){
if(val[key] !== oldVal[key]){
this.key = key
}
}
}
},
實(shí)現(xiàn)表頭固定
思路:
- 對table拷貝一份禁漓,然后把拷貝后的table中的tbody刪除跟衅,只留一個thead固定在頂部(這里因?yàn)閠head必須在table中,所以我們不能像div一樣把它單獨(dú)拿出來)
- 復(fù)制后的table刪除tbody后播歼,寬度會和之前的不一致伶跷,這時候你需要獲取拷貝前的table里每一個th的寬度,然后設(shè)置給拷貝后的th
- table.vue
<template>
<div class="lifa-table-wrapper" ref="warpper">
<div :style="{height,overflow:'auto'}">
<table class="lifa-table" :class="{bordered,compact,striped}" ref="table">
<thead>
<tr>
<th>
<input type="checkbox" @change="onChangeItemAll($event)" ref="a" :checked="areAllItemChecked">
</th>
<th v-if="numberVisible">#</th>
<th v-for="column in columns" :key="column.field">
<div class="lifa-table-header">
{{column.text}}
<!--如果對應(yīng)的key在orderBy這個對象里秘狞,就顯示-->
<span class="lifa-table-sorter" v-if="column.field in orderBy"
@click="changeOrderBy(column.field)">
<lf-icon name="asc" :class="{active:orderBy[column.field] === 'asc'}"></lf-icon>
<lf-icon name="desc" :class="{active: orderBy[column.field] === 'desc'}"></lf-icon>
</span>
</div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in dataSource" :key="item.id">
<th>
<input type="checkbox" @change="onChangeItem(item, index, $event)"
:checked="onChecked(item)" class="checkbox"
>
</th>
<td v-if="numberVisible">{{index+1}}</td>
<template v-for="column in columns">
<!--顯示dataSource中對應(yīng)表頭字段里的內(nèi)容-->
<td :key="column.field">{{item[column.field]}}</td>
</template>
</tr>
</tbody>
</table>
</div>
<div class="lifa-table-loading" v-if="loading">
<lf-icon name="loading"></lf-icon>
</div>
</div>
</template>
props: {
height: {
type: [Number, String],
}
},
mounted(){
let oldTable = this.$refs.table
let newTable = oldTable.cloneNode(true)
this.newTable = newTable
this.updateHeadersWidth()
//窗口改變時重新獲取一下各個th的寬度叭莫,重新賦值
window.addEventListener('resize', this.onWindowResize)
newTable.classList.add('lifa-table-copy')
this.$refs.warpper.appendChild(newTable)
},
beforeDestroy() {
window.removeEventListener('resize', this.onWindowResize)
this.newTable.remove()
},
methods: {
onWindowResize() {
this.updateHeadersWidth()
},
updateHeadersWidth() {
let tableHeader = Array.from(this.$refs.table.children).filter(node => node.nodeName.toLocaleLowerCase() === 'thead')[0]
let tableHeader2
Array.from(this.newTable.children).map((node) => {
if (node.nodeName.toLocaleLowerCase() === 'tbody') {
node.remove()
} else {
//也就是tableHeader=<thead>
tableHeader2 = node
}
})
Array.from(tableHeader.children[0].children).map((node, index) => {
let {width} = node.getBoundingClientRect()
console.log(width);
tableHeader2.children[0].children[index].style.width = `${width}px`
})
},
}
上面的代碼雖然實(shí)現(xiàn)了頂部表頭固定,但是因?yàn)槟闶菑?fù)制了一個谒撼,所以表頭的點(diǎn)擊事件并不會起作用食寡。
解決方法:
我們在拷貝一個新的table的時候不全部拷貝只拷貝table不拷貝table里面的子元素,然后我們把原有的thead添加到拷貝后的table中廓潜,這樣就還是原來的thead抵皱,而且可以單獨(dú)拿出來,然后我們通過讓用戶給每一列傳一個寬度辩蛋,通過這個寬度來設(shè)置原來的table里的寬度呻畸,并且拷貝后的table因?yàn)闀采w表格內(nèi)容的第一行,所以還要通過margin-top移下來thead的高度的距離
data(){
return {
columns: [
//表頭每一列顯示的文本和字段
{text: '姓名', field: 'name', width: 100},
{text: '分?jǐn)?shù)', field: 'score',width: 100},
{text: '年齡', field: 'age'}
],
orderBy: {//true:開啟排序悼院,但是不確定asc desc
name: true,
score: 'desc'
},
dataSource: [
{id: 1, name: '發(fā)發(fā)', score: 100, age:18},
{id: 2, name: '琳琳', score: 99, age: 16},
{id: 3, name: '西西', score: 99, age: 20},
{id: 4, name: '泳兒', score: 99, age: 21},
{id: 5, name: '美美', score: 99, age: 22},
{id: 6, name: '阿宇', score: 99, age: 26},
{id: 7, name: '發(fā)發(fā)', score: 100, age:18},
{id: 8, name: '琳琳', score: 99, age: 16},
{id: 9, name: '西西', score: 99, age: 20},
{id: 10, name: '泳兒', score: 99, age: 21},
{id: 11, name: '美美', score: 99, age:18},
{id: 12, name: '阿宇', score: 99, age: 16}
],
}
}
- table.vue
<template>
<div class="lifa-table-wrapper" ref="wrapper">
<div :style="{height: `${height}px`,overflow:'auto'}" ref="tableContent">
<table class="lifa-table" :class="{bordered,compact,striped}" ref="table">
<thead>
<tr>
<th :style="{width: '50px'}">
<input type="checkbox" @change="onChangeItemAll($event)" ref="a" :checked="areAllItemChecked">
</th>
<th v-if="numberVisible" :style="{width: '50px'}">#</th>
<th v-for="column in columns" :key="column.field" :style="{width: `${column.width}px`}">
<div class="lifa-table-header">
{{column.text}}
<!--如果對應(yīng)的key在orderBy這個對象里伤为,就顯示-->
<span class="lifa-table-sorter" v-if="column.field in orderBy"
@click="changeOrderBy(column.field)">
<lf-icon name="asc" :class="{active:orderBy[column.field] === 'asc'}"></lf-icon>
<lf-icon name="desc" :class="{active: orderBy[column.field] === 'desc'}"></lf-icon>
</span>
</div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in dataSource" :key="item.id">
<th :style="{width: '50px'}">
<input type="checkbox" @change="onChangeItem(item, index, $event)"
:checked="onChecked(item)" class="checkbox"
>
</th>
<td v-if="numberVisible" :style="{width: '50px'}">{{index+1}}</td>
<template v-for="column in columns">
<!--顯示dataSource中對應(yīng)表頭字段里的內(nèi)容-->
<td :key="column.field" :style="{width: `${column.width}px`}">{{item[column.field]}}</td>
</template>
</tr>
</tbody>
</table>
</div>
<div class="lifa-table-loading" v-if="loading">
<lf-icon name="loading"></lf-icon>
</div>
</div>
</template>
mounted() {
let oldTable = this.$refs.table
let newTable = oldTable.cloneNode()
let {height} = oldTable.children[0].getBoundingClientRect()
newTable.appendChild(oldTable.children[0])
this.$refs.tableContent.style.marginTop = height + 'px'
this.$refs.wrapper.style.height = this.height - height + 'px'
newTable.classList.add('lifa-table-copy')
this.$refs.wrapper.appendChild(newTable)
},
實(shí)現(xiàn)展開行功能
實(shí)現(xiàn)思路:通過用戶在dataSource里傳入description指定展開要顯示的字段,然后通過一個expend-field傳入我們的description字段据途,也就是expend-field="description"
绞愚。因?yàn)槲覀円诿恳粋€tr下面再加一個tr,但是你不能再單獨(dú)v-for那就不能同步了颖医,所以要想同時遍歷多個外層就要用template位衩,然后給每一列前面加一個展開的按鈕,點(diǎn)擊這個按鈕的時候把當(dāng)前的id傳給一個數(shù)組熔萧,然后通過判斷數(shù)組里是否有這個id糖驴,沒有就加到數(shù)組里,有就刪除佛致,之后再給展開的這一欄通過判斷數(shù)組里是否有當(dāng)前id來讓它是否展示
- table.vue
<template v-for="(item,index) in dataSource">
<tr :key="item.id">
<td :style="{width: '50px'}" @click="expendItem(item.id)"
class="lifa-table-center" :class="{expend: expendVisible(item.id)}"
>
<lf-icon name="right"></lf-icon>
</td>
<td :style="{width: '50px'}" class="lifa-table-center">
<input type="checkbox" @change="onChangeItem(item, index, $event)"
:checked="onChecked(item)" class="checkbox"
>
</td>
<td v-if="numberVisible" :style="{width: '50px'}">{{index+1}}</td>
<template v-for="column in columns">
<!--顯示dataSource中對應(yīng)表頭字段里的內(nèi)容-->
<td :key="column.field" :style="{width: `${column.width}px`}">{{item[column.field]}}</td>
</template>
</tr>
<tr v-if="expendVisible(item.id)">
<td :key="`${item.id}-1`" :colspan="columns.length+2">
{{item[expendField] || '空'}}
</td>
</tr>
</template>
methods: {
expendItem(id){
if(this.expendIds.indexOf(id) >= 0){
this.expendIds.splice(this.expendIds.indexOf(id),1)
}else{
this.expendIds.push(id)
}
},
expendVisible(id){
return this.expendIds.indexOf(id) >= 0
},
}
<lf-table :columns="columns" :data-source="dataSource"
bordered :selected-item.sync="selectedItem" :order-by.sync="orderBy"
@update:orderBy="changeOrder" :loading="loading" :height="400"
expend-field="description"
></lf-table>
dataSource: [
{id: 1, name: '發(fā)發(fā)', score: 100, age:18, description: '你最帥'},
{id: 2, name: '琳琳', score: 99, age: 16, description: '為啥不做我媳婦'},
{id: 3, name: '西西', score: 99, age: 20, description: '好累啊'},
{id: 4, name: '泳兒', score: 99, age: 21},
{id: 5, name: '美美', score: 99, age: 22},
{id: 6, name: '阿宇', score: 99, age: 26},
{id: 7, name: '發(fā)發(fā)', score: 100, age:18},
{id: 8, name: '琳琳', score: 99, age: 16},
{id: 9, name: '西西', score: 99, age: 20},
{id: 10, name: '泳兒', score: 99, age: 21},
{id: 11, name: '美美', score: 99, age:18},
{id: 12, name: '阿宇', score: 99, age: 16}
],
上面的展開行里的列數(shù)是寫死的贮缕,而我們有可能沒有展開按鈕或者沒有選擇框,那么這個列數(shù)就不對了俺榆,所以我們需要通過計(jì)算屬性來計(jì)算
<tr v-if="expendVisible(item.id)">
<td :key="`${item.id}-1`" :colspan="columns.length+ expendedCellColSpan">
{{item[expendField] || '空'}}
</td>
</tr>
computed: {
expendedCellColSpan(){
let result = 0
if(this.checkable){
result += 1
}
if(this.expendField){
result += 1
}
return result
}
},
props: {
//是否顯示選擇框
checkable: {
type: Boolean,
default: false
}
}
table里面可以有按鈕
思路通過用戶傳入一個template感昼,然后在table中通過slot來把你template里的按鈕放到指定位置
<lf-table :columns="columns" :data-source="dataSource"
bordered :selected-item.sync="selectedItem" :order-by.sync="orderBy"
@update:orderBy="changeOrder" :loading="loading" :height="400"
expend-field="description" checkable
>
<template slot-scope="xxx">
<button @click="edit(xxx.item.id)">編輯</button>
<button @click="view(xxx.item.id)">查看</button>
</template>
</lf-table>
- table.vue
<td>
<slot :item="item"></slot>
</td>
methods: {
edit(id){
alert(`正在編輯第${id}個`)
},
view(id){
alert(`正在查看第${id}個`)
}
}
升級table組件,支持自定義
問題:我們table里只能傳入data而不能傳入a標(biāo)簽肋演,我們現(xiàn)在想給每一列添加一個a標(biāo)簽抑诸,使用vue就沒法實(shí)現(xiàn)
思路:使用jsx
- 在vue中使用JSX
(1).lang="jsx"
(2).render返回一個標(biāo)簽
(3).使用css直接還是用class
<script lang="jsx">
export default {
data() {
return {
n: 13,
items: [2, 3, 4, 5, 6]
}
},
name: 'demo2',
render(h) {
return (
<div class="xxx">
{this.n > 10 ? <span>大</span> : <span>小</span>}
<ul>
{this.items.map(i =>
<li>{i}</li>
)}
</ul>
</div>
)
}
}
</script>
<style scoped lang="scss">
.xxx {
color: red;
}
</style>
問題:我們必須得將之前的代碼都改成jsx烂琴,而且使用的人也必須使用jsx格式,限制性太多
- 使用slot插槽自定義
相關(guān)補(bǔ)充:我們可以在組件里寫任何組件標(biāo)簽蜕乡,這些組件標(biāo)簽會默認(rèn)轉(zhuǎn)為插槽奸绷,獲取他們的方式有兩種,一:在mounted中通過this.children拿到他們
將原來的columns數(shù)組去掉,使用組件table-columns來代替
- table-columns.vue
<template>
<div></div>
</template>
<script>
export default {
name: "table-column.vue",
props: {
text: {
type: String,
required: true
},
field: {
type: String,
required: true
},
width: {
type: Number
}
}
}
</script>
- demo.vue
<lf-table :data-source="dataSource"
bordered :selected-item.sync="selectedItem" :height="400"
expend-field="description" checkable>
<!-- <template slot-scope="xxx">-->
<!-- <lf-button @click="edit(xxx.item.id)">編輯</lf-button>-->
<!-- <lf-button @click="view(xxx.item.id)">查看</lf-button>-->
<!-- </template>-->
<lf-table-column text="姓名" field="name" :width="100">
<template slot-scope="scope">
<a href="#">{{scope.value}}</a>
</template>
</lf-table-column>
<lf-table-column text="分?jǐn)?shù)" field="score"></lf-table-column>
</lf-table>
import LfTable from './table'
import LfTableColumn from './table-column'
export default {
name: "demo",
components: {
LfTable,
LfTableColumn
},
data() {
return {
dataSource: [
{id: 1, name: '發(fā)發(fā)', score: 100, age:18, description: '你最帥辛块,將來一定會成為一個了不起的演員'},
{id: 2, name: '琳琳', score: 99, age: 16, description: '為啥不做我媳婦'},
{id: 3, name: '西西', score: 99, age: 20, description: '好累啊'},
{id: 4, name: '泳兒', score: 99, age: 21},
{id: 5, name: '美美', score: 99, age: 22},
{id: 6, name: '阿宇', score: 99, age: 26},
{id: 7, name: '泳兒', score: 99, age: 21},
{id: 8, name: '美美', score: 99, age: 22},
{id: 9, name: '阿宇', score: 99, age: 26}
],
selectedItem: [],
}
},
- table.vue
<template v-for="column in columns">
<!--顯示dataSource中對應(yīng)表頭字段里的內(nèi)容-->
<td :key="column.field" :style="{width: `${column.width}px`}">
// 在原來遍歷td內(nèi)容的地方直接加判斷
{{column.render ? column.render({value: item[column.field]}) : item[column.field]}}
</td>
</template>
data() {
+ columns: []
},
mounted() {
// this.$slots遍歷拿到每一個table-columns組件
this.columns = this.$slots.default.map(node => {
// node.componentOptions.propsData拿到組件中外界傳進(jìn)來的props的值
let { text, field, width } = node.componentOptions.propsData
// 如果組件里面使用了插槽那么就可以拿到插槽里對應(yīng)的render函數(shù)也就是
// <template slot-scope="scope">
// <a href="#">{{scope.value}}</a>
// </template>
let render = node.data.scopedSlots && node.data.scopedSlots.default
return {
text, field, width, render
}
})
// 這里之所以要render里面?zhèn)饕粋€對象key是value畔派,是因?yàn)槲覀兦懊鎸懙膕cope.value,
// scope就是我們的形參也就是下面的{value:'立發(fā)'}
let result = this.columns[0].render({value: '立發(fā)'})
console.log(result)
}
問題: 上面的代碼如果直接在原來的td渲染的時候修改會報錯
單獨(dú)將this.columns[0].render({value: '立發(fā)'})打印我們發(fā)現(xiàn),他是一個Vnode
問題:那么我們該如何將vnode展示在template里哪
方法:
(1).聲明一個vnodes組件如下
components: {
LfIcon,
vnodes: {
function: true,
render: (h, ctx) => ctx.props.vnodes
}
},
(2).對vnodes組件傳入一個vnodes润绵,綁定的數(shù)據(jù)就是你要傳入的value
<template v-for="column in columns">
<!--顯示dataSource中對應(yīng)表頭字段里的內(nèi)容-->
<td :key="column.field" :style="{width: `${column.width}px`}">
<template v-if="column.render">
<vnodes :vnodes="column.render({value: item[column.field]})"></vnodes>
</template>
<template v-else>
{{item[column.field]}}
</template>
</td>
</template>