element-ui table 实现表格嵌套多层表格
项目需求:要求点击行中的“其他”单元格中的字段展开相应的子表
1.点击“扩展属性”出现"扩展属性"子表,点击“国家/地区”出现国家/地区子表
2.在国家/地区子表中还有个“生命周期字段”,点击“生命周期”出现“生命周期”的子表
实现方案:使用table 中的type="expand" 同时把展开图标隐藏,给表格文字“扩展属性”、“国家/地区”添加点击事件,通过点击事件实现不同子表的切换
当时做的时候是用v-if来实现对各级子表的显示和隐藏但是出现了问题就是,当点击顺序是 扩展属性-->国家/地区-->生命周期时会出现生命周期子表没有展示出来,如图:
当时使用$nextTick()/$forceUpdate()都没有用,把v-if换成v-show就没有问题了。看来频繁切换的显示和隐藏就要使用v-show
js代码:
css:对type=expand属性的箭头展开样式隐藏
同时要让width=1 的那一列的边框去掉
这时当缩放浏览器时会出现表头错位的情况,需要再加一个样式
下面是完整代码,搜索条件和表格字段有点多,大家可以忽略。若有不对的地方,欢迎指出,谢谢!
<template> <div> <el-form size="mini" :model="searchData" class="searchBox"> <el-row :gutter="24"> <el-col :xs="24" :sm="24" :md="12" :lg="6" :xl="4"> <el-form-item label="skuId" class="textArea"> <el-input v-model="searchData.skuIds" type="textarea" autosize placeholder="请输入" ></el-input> </el-form-item> </el-col> <el-col :xs="24" :sm="24" :md="12" :lg="6" :xl="4"> <el-form-item label="sku编码" class="textArea"> <el-input v-model="searchData.skuCodes" type="textarea" autosize placeholder="请输入" ></el-input> </el-form-item> </el-col> <el-col :xs="24" :sm="24" :md="12" :lg="6" :xl="4"> <el-form-item label="sku名称(中文)"> <el-input v-model="searchData.skuNameCn" placeholder="请输入"></el-input> </el-form-item> </el-col> <el-col :xs="24" :sm="24" :md="12" :lg="6" :xl="4"> <el-form-item label="Sku名称(英文)"> <el-input v-model="searchData.skuNameEn" placeholder="请输入"></el-input> </el-form-item> </el-col> <el-col :xs="24" :sm="24" :md="12" :lg="6" :xl="4"> <el-form-item> <el-button type="primary" @click="getTableData('init')">查询</el-button> <el-button @click="reset">重置</el-button> </el-form-item> </el-col> </el-row> </el-form> <el-table v-loading="loading" row-key="skuId" size="mini" class="tableBox" :data="tableList" style="width:100%;" border :header-cell-style="{ background: '#fafafa' }" :expand-row-keys="expands" > <el-table-column type="expand" width="1"> <template slot-scope="scopeOut"> <el-table v-show="scopeOut.row.countryShow" ref="countryArea" :data="scopeOut.row.nations" border size="mini" row-key="id" :expand-row-keys="expandsInner" :header-cell-style="{ background: '#fafafa' }" > <el-table-column type="expand" width="1"> <template slot-scope="scopeInner"> <el-table v-show="scopeInner.row.lifeShow" :data="scopeInner.row.lifecycles" border size="mini" :header-cell-style="{ background: '#fafafa' }" > </el-table-column> <el-table-column prop="lifecycleNameCn" label="生命周期中文名" :show-overflow-tooltip="true" > </el-table-column> <el-table-column prop="lifecycleTime" label="生命周期时间" :show-overflow-tooltip="true" > </el-table-column> <el-table-column prop="skuLifecycleStatus" label="生命周期状态"> <template slot-scope="scopes"> <el-tag v-if="scopes.row.skuLifecycleStatus === 0" type="danger">失效</el-tag> <el-tag v-if="scopes.row.skuLifecycleStatus === 1" type="info">有效</el-tag> </template> </el-table-column> </el-table> </template> </el-table-column> <el-table-column prop="nationTwoAbbr" label="国家地区二位码" :show-overflow-tooltip="true" > </el-table-column> <el-table-column prop="nationNameCn" label="国家地区中文名" :show-overflow-tooltip="true" > </el-table-column> <el-table-column label="其他" :show-overflow-tooltip="true"> <template slot-scope="scopeInner"> <span style="color:#09F;cursor: pointer;" @click="expandTable(scopeInner.row)" >生命周期</span > </template> </el-table-column> </el-table> <el-table v-show="scopeOut.row.extenShow" :data="scopeOut.row.extendAttributes" row-key="id" border size="mini" :header-cell-style="{ background: '#fafafa' }" > <el-table-column prop="attrValueCn" label="扩展属性值(中文)" :show-overflow-tooltip="true" > </el-table-column> <el-table-column prop="attrValueEn" label="扩展属性值(英文)" :show-overflow-tooltip="true" > </el-table-column> <el-table-column prop="valueDataType" label="数据类型"> <template slot-scope="scopes"> <el-tag v-if="scopes.row.valueDataType === 1" size="mini">字符串</el-tag> <el-tag v-if="scopes.row.valueDataType === 2" type="info" size="mini" >带单位实数等</el-tag > </template> </el-table-column> </el-table> </template> </el-table-column> <el-table-column property="createTime" label="创建时间" :show-overflow-tooltip="true" :render-header="renderHeader" ></el-table-column> <el-table-column property="updateTime" label="更新时间" :show-overflow-tooltip="true" :render-header="renderHeader" ></el-table-column> <el-table-column label="其他" :show-overflow-tooltip="true" width="140"> <template slot-scope="scopeOut"> <span style="color:#09F;cursor: pointer;" @click="expandExTable(scopeOut.row)" >扩展属性</span > <span>|</span> <span style="color:#09F;cursor: pointer;" @click="expandCounTable(scopeOut.row)" >国家/地区</span > </template> </el-table-column> </el-table> <el-pagination class="pagination" :current-page="pageNum" :page-sizes="[10, 20, 30, 50]" :page-size="pageSize" layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange" > </el-pagination> </div> </template> <script> import Crypto from '@/utils/crypto.js'; import diySelect from '@/components/Select/diySelect.vue'; export default { components: { diySelect, }, data() { return { searchData: {}, tableList: [], pageNum: 1, pageSize: 10, total: 0, loading: false, secretKey: '', pickerOptions: { shortcuts: [ { text: '最近一周', onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 7); picker.$emit('pick', [start, end]); }, }, { text: '最近一个月', onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 30); picker.$emit('pick', [start, end]); }, }, { text: '最近三个月', onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 90); picker.$emit('pick', [start, end]); }, }, { text: '最近六个月', onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 180); picker.$emit('pick', [start, end]); }, }, ], }, dataList: [], expands: [], expandsCoun: [], lifeShow: false, countryShow: false, expandsInner: [], // 生命周期展开数组 counFlag: true, colorCnsList: [], colorEnsList: [], colorLocalsList: [], carrierNameCnsList: [], carrierNameEnsList: [], certifiedModelsList: [], }; }, created() { this.getTableData('init'); this.getSkuItem(); }, methods: { renderHeader(h, data) { return h('span', [ h( 'el-tooltip', { attrs: { class: 'item', effect: 'dark', content: data.column.label, placement: 'top', }, }, [h('span', data.column.label)] ), ]); }, expandTable(row) { row.lifeShow = !row.lifeShow; if (row.lifeShow) { this.expandsInner.push(row.id); } else { this.expandsInner = this.expandsInner.filter(item => item !== row.id); } console.log(this.expandsInner, '生命周期'); }, expandCounTable(row) { row.countryShow = !row.countryShow; if (row.countryShow) { this.expands.push(row.skuId); } else { this.expands = this.expands.filter(item => item !== row.skuId); } row.extenShow = false; console.log(this.expands, '国家/地区'); }, expandExTable(row) { row.extenShow = !row.extenShow; if (row.extenShow) { this.expands.push(row.skuId); } else { this.expands = this.expands.filter(item => item !== row.skuId); } row.countryShow = false; console.log(this.expands, '扩展属性'); }, reset() { this.searchData = {}; this.getTableData('init'); }, getTableData(init) { this.expandsInner = []; this.expands = []; let creatObj = {}; if (this.searchData.createDate !== undefined && this.searchData.createDate !== null) { creatObj = { startCreateTime: this.searchData.createDate[0], endCreateTime: this.searchData.createDate[1], }; } else { delete this.searchData.createDate; creatObj = {}; } let updateObj = {}; if (this.searchData.updateDate !== undefined && this.searchData.updateDate !== null) { updateObj = { startUpdateTime: this.searchData.updateDate[0], endUpdateTime: this.searchData.updateDate[1], }; } else { delete this.searchData.updateDate; updateObj = {}; } const obj = { skuIds: this.searchData.skuIds ? this.searchData.skuIds.split(/[(\r\n)\r\n]+/) : [], skuCodes: this.searchData.skuCodes ? this.searchData.skuCodes.split(/[(\r\n)\r\n]+/) : [], carrierIds: this.searchData.carrierIds ? this.searchData.carrierIds.split(/[(\r\n)\r\n]+/) : [], carrierCodes: this.searchData.carrierCodes ? this.searchData.carrierCodes.split(/[(\r\n)\r\n]+/) : [], normalizationIds: this.searchData.normalizationIds ? this.searchData.normalizationIds.split(/[(\r\n)\r\n]+/) : [], normalizationCodes: this.searchData.normalizationCodes ? this.searchData.normalizationCodes.split(/[(\r\n)\r\n]+/) : [], skuNameCn: this.searchData.skuNameCn, skuNameEn: this.searchData.skuNameEn, colorCns: this.searchData.colorCns, colorEns: this.searchData.colorEns, colorLocals: this.searchData.colorLocals, carrierNameCns: this.searchData.carrierNameCns, carrierNameEns: this.searchData.carrierNameEns, certifiedModels: this.searchData.certifiedModels, ...updateObj, ...creatObj, }; if (init) { this.pageNum = 1; this.pageSize = 10; } this.loading = true; const param = { pageNum: this.pageNum, pageSize: this.pageSize, ...obj, }; this.$api.productTree.skuList({ ...param }).then(res => { if (res.data) { this.tableList = res.data.content; this.tableList.forEach(item => { this.$set(item, 'countryShow', false); this.$set(item, 'extenShow', false); }); this.total = res.data.total; } this.loading = false; }); }, handleCurrentChange(page) { this.pageNum = page; this.getTableData(); }, handleSizeChange(pageSize) { this.pageSize = pageSize; this.getTableData(); }, getSkuItem() { this.$api.productTree.skuItem().then(res => { if (res.data) { this.colorCnsList = []; this.colorEnsList = []; this.colorLocalsList = []; this.carrierNameCnsList = []; this.carrierNameEnsList = []; this.certifiedModelsList = []; for (const key in res.data) { const itemArr = []; res.data[key].forEach(item => { const obj = { name: item, }; itemArr.push(obj); }); if (key === 'colorCns') { this.colorCnsList = itemArr; } else if (key === 'colorEns') { this.colorEnsList = itemArr; } else if (key === 'colorLocals') { this.colorLocalsList = itemArr; } else if (key === 'carrierNameCns') { this.carrierNameCnsList = itemArr; } else if (key === 'carrierNameEns') { this.carrierNameEnsList = itemArr; } else if (key === 'certifiedModels') { this.certifiedModelsList = itemArr; } } } }); }, }, }; </script> <style lang="scss" scoped> .mainContent { margin: 20px; } .form-content { font-weight: 600; } .pagination { float: right; margin-top: 10px; } .import { margin: 0 0 20px 0; } .el-dialog .el-dialog__body { padding-top: 0 !important; } .el-form-item { margin-bottom: 15px !important; } .el-select, .el-cascader, .el-date-editor.el-input { width: 100%; } .searchBox >>> .el-form-item { display: flex; width: 100%; height: 30px; .el-form-item__label { white-space: nowrap; } .el-range-editor--mini.el-input__inner { height: 30px; } .el-form-item__content { flex: 1; .el-date-editor--daterange.el-input, .el-date-editor--daterange.el-input__inner, .el-date-editor--timerange.el-input, .el-date-editor--timerange.el-input__inner { width: 100% !important; } } } .tableBox >>> .el-table__expand-icon:after { content: ''; } .tableBox >>> .el-table__expand-icon > i { display: none; } .tableBox >>> .el-table__expand-column { border-right: none; } </style>
故不积跬步,无以至千里;不积小流,无以成江海。