记录一次el-table动态添加删除列导致表格样式错误问题
- 需求背景
- 出现的问题
- 解决方案
- 理论:在el-table中设置key值,重新赋值表格数据之后,更新key值,达到动态更新效果
需求背景
一个电商类商品管理平台(类似shopify产品编辑特征管理);
具体需求点:根据用户角色显示不同的列信息,并可以根据规则自动创建或自动删除特定的列 。同时每一列要求可以批量修改。
使用的组件库:element-ui中的el-table
伪代码
<div>
<el-table
ref="table"
:data="table"
:key="Math.random()"
stripe
>
<!-- 临时列,避免动态添加列和行后横向自动滚动 -->
<el-table-column label="id" width="5" v-show="false" fixed >
<template slot-scope="scope">
</template>
</el-table-column>
<template v-for="(item,key) in tableColumn">
<el-table-column v-if="item=='Image'" :key="key" :label="item" width="100" fixed>
<template slot-scope="scope">
<div v-if="item=='Image'" class="tableImage">
<el-upload
:action="uploadApi"
:disabled="scope['row']['isDeleted']"
class="avatar-uploader"
list-type="picture-card"
accept=".jpeg,.png,.jpg"
:file-list="skuFileList[scope.$index]"
:key="key"
:limit="1"
:on-preview="handlePictureCardPreview"
:on-remove="(file, fileList)=>handleSkuRemove(file,fileList,scope.$index)"
:on-success="(response, file, fileList)=>handleSkuSuccess(response,file,fileList,scope.$index)">
<img v-if="scope['row'][item]" :src="scope['row'][item]" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</div>
</template>
</el-table-column>
<el-table-column v-else-if="item=='Brand SKU ID'" :key="key" :label="item" width="200" >
<template slot-scope="scope">
<div class="table-cell-input">
<el-input v-model="scope['row'][item]" ref="inputRef" @keydown.native="clickBrandSkuCode($event)" @input="(event)=>changeBrandSkuCode(event,scope['row'])" :disabled="scope['row']['isDeleted']"></el-input>
</div>
<template v-if="!scope['row']['isDeleted']">
<div class="required-error" v-show="!scope['row'][item]">Brand SKU ID can't be blank</div>
<div class="required-error" v-if="scope['row'][item] && scope['row']['isRepeatBrandSku']">Duplicate SKU ID, please modify</div>
</template>
</template>
</el-table-column>
<el-table-column v-else-if="item=='Case'" :key="key" :label="item" width="80" >
<template slot="header" slot-scope="scope">
<div class="cust-title">
Case
<i class="el-icon-edit" @click="skuSetDiglog('Case')" data-key="Case"></i>
</div>
</template>
<template slot-scope="scope">
<div class="table-cell-input">
<el-input
:disabled="scope['row']['isDeleted']"
oninput="if(value){value=value.replace(/^(0+)|[^\d]+/g,'')} if(value<=0){value=1} if(value>100){value=100}"
:maxlength="3" :minlength="1" v-model="scope['row'][item]"></el-input>
<sku-bluk-set field="Case" v-if="scope.$index==0 && skuIsDiglog && curSkuBlukField == 'Case'"></sku-bluk-set>
</div>
</template>
</el-table-column>
<el-table-column v-else-if="item=='FOB Price($)'" :key="key" :label="item" width="180"
class="cust-table-column">
<template slot="header" slot-scope="scope">
<div class="cust-title">
FOB Price($)
<i class="el-icon-edit" @click="skuSetDiglog('FOB Price')" data-key="FOB Price"></i>
</div>
</template>
<template slot-scope="scope">
<div class="table-cell-input">
<el-input style="width: 70px" @input="(event)=>blurFob(event,scope.$index,scope['row'])" :disabled="scope['row']['isDeleted']"
oninput="if(value){value=value.replace(/[^0-9.]/g,'')} if(value){value=value.replace(/^()*(\d+)\.(\d\d).*$/, '$1$2.$3')}"
v-model="scope['row'][item]">
</el-input>
<template v-if="!scope['row']['isDeleted']">
<div class="required-error" v-show="!scope['row'][item]">FOB Price can’t be blank</div>
<div class="required-error" style="margin-top: 13px;"
v-if="scope['row']['svGoodsReviewLogList'] && (scope['row']['svGoodsReviewLogList'].filter(f=>f['reviewContent']=='fob_price').length>0)">
modify is {{ scope['row']['svGoodsReviewLogList'] | disUpdateAfterValue('fob_price') }}, under
review
</div>
</template>
<sku-bluk-set field="FOB Price" v-if="scope.$index==0 && skuIsDiglog && curSkuBlukField == 'FOB Price'"></sku-bluk-set>
</div>
</template>
</el-table-column>
<el-table-column v-else-if="item=='Cost Price($)'" :key="key" :label="item" width="180"
class="cust-table-column">
<template slot="header" slot-scope="scope">
<div class="cust-title">
Cost Price($)
<i class="el-icon-edit" @click="skuSetDiglog('Cost Price')" data-key="Cost Price"></i>
</div>
</template>
<template slot-scope="scope">
<div class="table-cell-input">
<el-input style="width: 70px" @input="(event)=>blurCostPriceFob(event,scope.$index,scope['row'])" :disabled="scope['row']['isDeleted']"
oninput="if(value){value=value.replace(/[^0-9.]/g,'')} if(value){value=value.replace(/^()*(\d+)\.(\d\d).*$/, '$1$2.$3')}"
v-model="scope['row'][item]">
</el-input>
<template v-if="!scope['row']['isDeleted']">
<div class="required-error" v-show="!scope['row'][item]">Cost Price can’t be blank</div>
<div class="required-error" style="margin-top: 13px;"
v-if="scope['row']['svGoodsReviewLogList'] && (scope['row']['svGoodsReviewLogList'].filter(f=>f['reviewContent']=='source_price').length>0)">
modify is {{ scope['row']['svGoodsReviewLogList'] | disUpdateAfterValue('source_price') }},
under review
</div>
</template>
<sku-bluk-set field="Cost Price" v-if="scope.$index==0 && skuIsDiglog && curSkuBlukField == 'Cost Price'"></sku-bluk-set>
</div>
</template>
</el-table-column>
<el-table-column v-else-if="item==='Wholesale Price($)'" width="180" :key="key" :label="item">
<template slot="header" slot-scope="scope">
<div class="cust-title">
Wholesale Price($)
</div>
</template>
<template slot-scope="scope">
<div>{{ scope['row'][item] }}</div>
</template>
</el-table-column>
<el-table-column v-else-if="item=='Procurement Price($)'" :key="key" :label="item" width="200"
class="cust-table-column">
<template slot="header" slot-scope="scope">
<div class="cust-title">
Procurement Price($)
<i class="el-icon-edit" @click="skuSetDiglog('Procurement Price')" data-key="Procurement Price"></i>
</div>
</template>
<template slot-scope="scope">
<div class="table-cell-input">
<el-input style="width: 70px"
:disabled="scope['row']['isDeleted']"
oninput="
if(value){value=value.replace(/[^0-9.]/g,'')}
if(value){value=value.replace(/^()*(\d+)\.(\d\d).*$/, '$1$2.$3')}
"
@change="(val) => {scope['row'][item] = val || 0}"
v-model="scope['row'][item]">
</el-input>
<sku-bluk-set field="Procurement Price" v-if="scope.$index==0 && skuIsDiglog && curSkuBlukField == 'Procurement Price'"></sku-bluk-set>
</div>
</template>
</el-table-column>
<el-table-column v-else-if="item=='Wholesale Settlement Price($)'" :key="key" :label="item" width="250"
class="cust-table-column">
<template slot="header" slot-scope="scope">
<div class="cust-title">
Wholesale Settlement Price($)
<i class="el-icon-edit" @click="skuSetDiglog('Wholesale Settlement Price')" data-key="Wholesale Settlement Price"></i>
</div>
</template>
<template slot-scope="scope">
<div class="table-cell-input">
<el-input style="width: 70px"
oninput="
if(value){value=value.replace(/[^0-9.]/g,'')}
if(value){value=value.replace(/^()*(\d+)\.(\d\d).*$/, '$1$2.$3')}"
@change="(val) => {scope['row'][item] = val || 0}"
:disabled="scope['row']['isDeleted']"
v-model="scope['row'][item]">
</el-input>
<sku-bluk-set field="Wholesale Settlement Price" v-if="scope.$index==0 && skuIsDiglog && curSkuBlukField == 'Wholesale Settlement Price'"></sku-bluk-set>
</div>
</template>
</el-table-column>
<el-table-column v-else-if="item==='Retail Price($)'" width="200" :key="key" :label="item">
<template slot="header" slot-scope="scope">
<div class="cust-title">
Retail Price($)
<i class="el-icon-edit" @click="skuSetDiglog('Retail Price')" data-key="Retail Price"></i>
</div>
</template>
<template slot-scope="scope">
<!-- <div >{{scope['row'][item]}}</div>
<div class="required-error" style="margin-top: 22px;" v-if="scope['row']['svGoodsReviewLogList'] && (scope['row']['svGoodsReviewLogList'].filter(f=>f['reviewContent']=='retail_price').length>0)">
modify is {{ scope['row']['svGoodsReviewLogList'] | disUpdateAfterValue('retail_price') }}, under review
</div>-->
<div class="table-cell-input">
<el-input style="width: 70px"
@change="(event)=>blurRetailPriceFob(event,scope.$index,scope['row'])"
:disabled="scope['row']['isDeleted']"
oninput="if(value){value=value.replace(/[^0-9.]/g,'')} if(value){value=value.replace(/^()*(\d+)\.(\d\d).*$/, '$1$2.$3')}"
v-model="scope['row'][item]">
</el-input>
<template v-if="!scope['row']['isDeleted']">
<div class="required-error" style="margin-top: 13px;"
v-show=" scope['row']['Retail Price($)']<=scope['row']['Wholesale Price($)'] ">Must Retail price >
Wholesale Price ${{ scope['row']['Wholesale Price($)'] }}
</div>
</template>
<sku-bluk-set field="Retail Price" v-if="scope.$index==0 && skuIsDiglog && curSkuBlukField == 'Retail Price'"></sku-bluk-set>
</div>
</template>
</el-table-column>
<el-table-column v-else-if="item=='Weight(g)'" :key="key" :label="item" width="110">
<template slot="header" slot-scope="scope">
<div class="cust-title">
Weight(g)
<i class="el-icon-edit" @click="skuSetDiglog('Weight')" data-key="Weight"></i>
</div>
</template>
<template slot-scope="scope">
<div class="table-cell-input">
<el-input @change="(event)=>blurWeight(event,scope.$index,scope['row'])" :disabled="scope['row']['isDeleted']"
oninput="if(value){value=value.replace(/^(0+)|[^\d]+/g,'')} if(value>10000){value=10000}"
v-model="scope['row'][item]"></el-input>
</div>
<template v-if="!scope['row']['isDeleted']">
<div class="required-error" v-show="!scope['row'][item]">Can't be blank</div>
<div class="required-error" style="margin-top: 13px;"
v-if="scope['row']['svGoodsReviewLogList'] && (scope['row']['svGoodsReviewLogList'].filter(f=>f['reviewContent']=='weight').length>0)">
modify is {{
scope['row']['svGoodsReviewLogList'] | disUpdateAfterValueObj('weight') | disUpdateAfterValueWeight
}}, under review
</div>
</template>
<sku-bluk-set field="Weight" v-if="scope.$index==0 && skuIsDiglog && curSkuBlukField == 'Weight'"></sku-bluk-set>
</template>
</el-table-column>
<el-table-column v-else-if="item=='Inventory'" :key="key" :label="item" width="190">
<template slot="header" slot-scope="scope">
<div class="cust-title">
Inventory
<i class="el-icon-edit" @click="skuSetDiglog('Inventory')" data-key="Inventory"></i>
</div>
</template>
<template slot-scope="scope">
<div class="table-cell-input">
<el-input :maxlength="5" :minlength="1"
style="width: 100px"
:disabled="scope['row']['isDeleted']"
oninput="if(value){value=value.replace(/[^\d]/g,'')} if(value>10000){value=10000}"
v-model="scope['row'][item]"></el-input>
<!-- <el-button type="text" style="margin-left: 4px" @click="handleInventory(scope.row.id)">
View
</el-button> -->
</div>
<template v-if="!scope['row']['isDeleted']">
<div class="required-error" v-show="!scope['row'][item]">Can't be blank</div>
</template>
<sku-bluk-set field="Inventory" v-if="scope.$index==0 && skuIsDiglog && curSkuBlukField == 'Inventory'"></sku-bluk-set>
</template>
</el-table-column>
<el-table-column v-else-if="item=='status'" :key="key" label="Availability" width="180">
<template slot="header" slot-scope="scope">
<div class="cust-title">
Availability
<i class="el-icon-edit" @click="skuSetDiglog('Availability')" data-key="Availability"></i>
</div>
</template>
<template slot-scope="scope">
<!-- 上架或下架状态 -->
<div class="table-cell-input" v-if="scope['row']['showStatusSelect']">
<el-select placeholder="Please Select" :disabled="goodsStatusDb == '0' ||scope['row']['isDeleted']"
v-model="scope['row'][item]" @change="e=>changeSkuStatus(e,scope['row'],scope.$index)">
<el-option
v-for="item in skuStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<!-- 已经设置过补货时间 -->
<div class="table-cell-input" v-else>
<div style="font-size: 12px;color: #FB541C;">Restock by {{ scope['row']['restockDate'] }}</div>
<div class="cust-sku-btn" style="text-align: left;" v-if="!scope['row']['isDeleted']">
<span @click="resetRestock(scope['row'],scope.$index)">Restock</span>
<span @click="e=>changeSkuRestockDate(e,scope['row']) ">Change Date</span>
</div>
<div class="cust-sku-btn" style="text-align: left;" v-else>
<span style="text-decoration: unset; ">Restock</span>
<span style="text-decoration: unset; ">Change Date</span>
</div>
</div>
<!-- 库存补货时间 -->
<div class="set-restock-date" v-show="scope['row']['tmpStatus']=='99' && !skuIsDiglog">
<p>Set restock date</p>
<el-date-picker
v-model="restockDateArr[scope.$index]"
style="margin-top: 10px;"
type="date"
:disabled="goodsStatusDb == '0'"
value-format="MM/dd/yyyy"
placeholder="MM/DD/YYYY"
format="MM/dd/yyyy"
>
</el-date-picker>
<div class="required-error" v-show="!restockDateArr[scope.$index]">Please set restock date</div>
<div class="cust-sku-btn">
<span @click="submitRestockDate(scope['row'],scope.$index,false)">Cancel</span>
<span @click="submitRestockDate(scope['row'],scope.$index,true)">Save</span>
</div>
</div>
<!-- 下架的SPU,SKU操作上架,需要提示“请先将SPU设置成上架 -->
<template v-if="!scope['row']['isDeleted']">
<div class="required-error" v-show="goodsStatusDb == '0'">Please set SPU Active first</div>
</template>
<sku-bluk-set field="Availability" v-if="scope.$index==0 && skuIsDiglog && curSkuBlukField == 'Availability'"></sku-bluk-set>
</template>
</el-table-column>
<el-table-column v-else-if=" item=='Dimensions - L/W/H(cm)'" :key="key" :label="item" width="220">
<template slot="header" slot-scope="scope">
<div class="cust-title">
Dimensions - L/W/H(cm)
<i class="el-icon-edit" @click="skuSetDiglog('Dimensions')" data-key="Dimensions"></i>
</div>
</template>
<template slot-scope="scope">
<div class="table-cell-input">
<el-input style="width: 50px;"
:disabled="scope['row']['isDeleted']"
oninput="if(value){value=value.replace(/^(0+)|[^\d]+/g,'')} if(value>1000){value=1000}"
placeholder="Length" v-model="scope['row']['length']"></el-input>
<el-input style="width: 50px;"
:disabled="scope['row']['isDeleted']"
oninput="if(value){value=value.replace(/^(0+)|[^\d]+/g,'')} if(value>1000){value=1000}"
placeholder="Width" v-model="scope['row']['width']"></el-input>
<el-input style="width: 50px;"
:disabled="scope['row']['isDeleted']"
oninput="if(value){value=value.replace(/^(0+)|[^\d]+/g,'')} if(value>1000){value=1000}"
placeholder="High" v-model="scope['row']['height']"></el-input>
</div>
<template v-if="!scope['row']['isDeleted']">
<div class="required-error"
v-show="!scope['row']['length'] || !scope['row']['width'] || !scope['row']['height']">L/W/H
(cm) can't be blank
</div>
</template>
<sku-bluk-set field="Dimensions" v-if="scope.$index==0 && skuIsDiglog && curSkuBlukField == 'Dimensions'"></sku-bluk-set>
</template>
</el-table-column>
<el-table-column v-else-if="item==='Vd_SKU ID'" :key="key" :label="item" :width="calcWidth(item)" fixed>
<template slot-scope="scope">
<div>{{ scope['row'][item] || '-' }}</div>
</template>
</el-table-column>
<el-table-column v-else-if="item!=='length' && item!=='width' && item!=='height' && item!=='countryCode'
&& item!=='serviceFee' && item!=='isDirectlyModifyNetPrice' && item!=='orlNetPrice'
&& item!=='restockDate' && item!=='skuCharacterList' &&item!=='restockDateMmddyyyy' " :key="key" :label="item" :width="calcWidth(item)">
<template slot-scope="scope">
<div>{{ scope['row'][item] }}</div>
</template>
</el-table-column>
</template>
<el-table-column label="Action" width="120" >
<template slot-scope="scope">
<i class="el-icon-delete" style="cursor: pointer;" v-if="!scope['row']['isDeleted']" @click="deleteSku(scope['row'],true)"></i>
<i class="el-icon-refresh-right" style="cursor: pointer;" v-if="scope['row']['isDeleted']" @click="deleteSku(scope['row'],false)"></i>
</template>
</el-table-column>
</el-table>
</div>
效果图
点击右上角按钮”add variant“ 效果图
以上表格中内容以及表格中color列(其他列是数据库返回),就是由这个生成的。
插入、删除列代码
//删除列
this.tableColumn = this.tableColumn.filter(f => !this.variantWithValuesListMap.get(f))
// columns = columns.filter(f => !this.variantWithValuesListMap.get(f))
//添加列
const newColumns = variantArr.map(f => f['variantName'])
for (let i = 0,len = newColumns.length; i < len ; i++) {
this.tableColumn.splice(2 + i, 0, newColumns[i])
}
以上变量:this.variantWithValuesListMap为add variant弹出框的名称+值内容
出现的问题
1、开始添加的第三列 brand sku id,无法动态显示出来 ,tableColumn信息确认已赋值
2、批量修改样式错乱
3、在新生成的表格中,输入框(包含下拉框)输入只能输入一个字符,自动失去焦点,并且此表格的横向滚动条,自动滚动到最右侧 。
解决方案
尝试了很多种方案,就不一一述说了(因为都没解决),最终解决以上问题的更改关键点为:
1、el-table中:key更改为 :key=“tableKey”
2、每次变更列的时候更改tablekey的值:
this.tableKey = Math.random()
理论:在el-table中设置key值,重新赋值表格数据之后,更新key值,达到动态更新效果
综上:是el-table中key值引发的血案。记录起来,避免很久以后后续自己又会出现。也供各位同行参考经验。