1.子组件
<template>
<div class="simpleTable">
<div class="table-box" :style="{ height: config.height ? config.height+'px' : '100%' }">
<el-table
ref="myTable"
:data="config.tableData"
:style="{width: '100%', position: config.height ? 'absolute' : 'relative' }"
:height="config.height"
:stripe="stripe"
:class="config.noBg?'':'custom'"
:row-key="getRowKeys"
:expand-row-keys="expands"
@expand-change="extendChange"
>
<el-table-column v-if="isExtend().show" type="expand">
<template slot-scope="props">
<slot :name="isExtend().slot" :row="props.row"></slot>
</template>
</el-table-column>
<template v-for="(item,i) in config.tableConfig">
<el-table-column
:key='i'
:prop="item.value"
:label="item.label"
v-if="display(item)"
width="auto"
:min-width="item.minWidth ? item.minWidth:'auto'"
:show-overflow-tooltip='true'
>
<template slot="header" slot-scope="scope">
<div style="height:100%; display:flex;padding:0">
<span>{{ scope.column.label }}</span>
<span v-if="item.filterable">
<Poptip content="" placement="bottom" v-model="visibleFilter[item.value]" :transfer="tip_transfer">
<i class="ivu-icon ivu-icon-funnel" @click="filterIcon(item,i,config.tableConfig)" :class="{on: getColumnFilter(item)._isFilter === 'Y'}"></i>
<div slot="content" v-if="item._isShowFilterPop">
<div class='simpopfilter'>
<div class="" style="padding: 10px 16px 4px;">
<input type="text" v-model="filterParam" @click.stop.prevent=""
@keyup="searchFiltersContent" placeholder="请搜索"
@keyup.8="searchFiltersContent" class="tip-search">
</div>
<ul>
<li class="filters-li" @click="filterTable(item, i, 'All')" >全部</li>
<template v-for="(filter,j) in filterListsBySearch">
<li class="filters-li" @click="filterTable(item, i, filter)" :key='j'
:class="{active: getColumnFilterActiveno(item)._activeKey === filter.value}" >{{filter.label}}</li>
</template>
</ul>
</div>
</div>
</Poptip>
</span>
<span v-if="item.tip">
<Tooltip placement="top" :transfer="true">
<i class="el-icon-question" style="font-size:16px;"></i>
<div slot="content">
{{ item.tip.content }}
</div>
</Tooltip>
</span>
</div>
</template>
<template slot-scope="scope">
<span v-if="!item.slot">
{{ item.value.includes('.') ? (isType(scope.row[item.value.split('.')[0]]) ? scope.row[item.value.split('.')[0]][item.value.split('.')[1]] : '-') : (isType(scope.row[item.value]) ? scope.row[item.value] : '-') }}
</span>
<slot v-if="item.slot" :name="item.slot" :row="scope.row"></slot>
</template>
</el-table-column>
</template>
</el-table>
</div>
<div v-if="config.page" class="page">
<el-pagination
small
:layout="config.layout ? config.layout : 'prev, pager, next, jumper'"
@current-change="handleCurrentChange"
:current-page.sync="config.page.currentPage"
:page-size="config.page.size"
:page-sizes="config.pageSizes ? config.pageSizes : [5, 10]"
:total="config.page.total"
:hide-on-single-page="ishide">
</el-pagination>
</div>
</div>
</template>
<script>
export default {
data(){
return {
isFilter: {},
filterLists: [],
tip_transfer: true, // 作用参见iview tip transfer属性
filterParam: '',
filterListsBySearch: [],
visibleFilter: {},
expands: [],
getRowKeys(row) {
return row.id
}
}
},
computed:{
ishide(){
return !!(this.config.tableData.length || this.config.page.total<this.config.page.size)
},
stripe() {
if (this.config.hasOwnProperty('stripe')) {
return this.config.stripe
} else {
return true
}
}
},
props:{
config:Object,
getData:Function, //获取数据函数
},
/**
* 【config】配置说明 可参考bill-cost-seo.vue页面的建议优化表
* tableData:表格数据
* stripe:斑马纹,默认true
* height:表格高度,默认不限高
* page:页码配置
* isExtend:展开行配置,注意父组件要配置slot插槽
*
* tableConfig:表格配置说明
* label:表头
* value:对应字段
* display:列是否展示,默认true
* minWidth:设置表格最小宽度
* filterable:配置筛选
* tip:添加提示,默认无
* slot:自定义插槽
*/
mounted(){
},
watch:{
$route: function (){
for (const key in this.visibleFilter) {
if (this.visibleFilter.hasOwnProperty(key)) {
this.visibleFilter[key] = false
}
}
// this.visibleFilter = false
},
},
methods: {
extendChange(row, expandedRows) {
if (expandedRows.length) {
this.expands = []
if (row) {
this.expands.push(row.id)// 每次push进去的是每行的ID
}
} else {
this.expands = []// 默认不展开
}
if (expandedRows.length) {
let func = this.config.isExtend.func
this.$parent[func](row)
}
},
isExtend() {
if (this.config.hasOwnProperty('isExtend')) {
return { show: this.config.isExtend.show , slot: this.config.isExtend.slot}
} else {
return {show:false,slot:''}
}
},
conditionChange(params,item) {
let keys = Object.keys(params)
let count = 0
this.isFilter[item.value] = true
keys.map(key=>{
if(typeof params[key] === 'object' && !params[key]){ //判断null
count++
delete params[key] //删除原来需要传的字段,现在不需要传的
}
})
if(count === keys.length){ //没有条件要传
this.isFilter[item.value] = false
}
this.$emit('onfilter', params)
},
//特殊类型过滤
popFilterFunc(item,index){
let config = item.specialFilter
config.show = true
config.index = index
},
filterIcon(item, columnNo,config) {
// 清空tip中搜索条件
this.filterParam = ''
if (item.filterable.remoteFilters) {
this.getRemoteFilterOptions(item,columnNo)
} else {
this.filterLists =item.filterable.filterLists
this.filterListsBySearch = item.filterable.filterLists
}
for (let obj in config) {
config[obj]._isShowFilterPop = false
}
item._isShowFilterPop = true
},
// 远程获取过滤条件
getRemoteFilterOptions (item) {
this.filterLists = []
this.filterListsBySearch = []
let filterAPI_string = item.filterable.filterAPI
let filterAPI = this.$validate.valueFromExpression(this.apiCenter, filterAPI_string)
this.$httpRequestEntrance.httpRequestEntrance('GET', filterAPI, '' , (responseData) => {
for (let it of responseData.data) {
let label = it[item.filterable.displayName]
// 排除掉空数据
if (this.$validate.isEmpty(label)) {
this.filterLists.push({label: label,
value: it[item.filterable.filterValue]})
}
}
// this.filterListsBySearch = this.filterLists
})
},
// 获取排序状态active状态
getColumnFilter (item) {
return {_isFilter: item._isFilter}
},
// 获取排序状态active状态中的当前状态
getColumnFilterActiveno (item) {
return {_activeKey: item._activeKey}
},
// 列筛选功能
filterTable: function (ele, i, valx) {
let params = {}
if (valx === 'All') {
ele._isFilter = 'N'
params[ele.filterable.filterParam] = ''
} else {
ele._isFilter = 'Y'
params[ele.filterable.filterParam] = valx.value
}
for (const key in this.visibleFilter) {
if (this.visibleFilter.hasOwnProperty(key)) {
this.visibleFilter[key] = false
}
}
// 设置当前状态数据key
ele._activeKey = valx.value
this.$emit('onfilter', params)
},
display(item) {
if (item.hasOwnProperty('display')) {
return item.display
} else {
return true
}
},
searchFiltersContent() {
this.filterListsBySearch = this.filterLists
this.filterListsBySearch = this.filterListsBySearch.filter((item)=> {
return item.label.toUpperCase().includes(this.filterParam.toUpperCase())
})
},
isType(data) {
if (Object.prototype.toString.call(data) === '[object Object]') {
if (Object.keys(data).length === 0) {
return false
} else {
return data
}
} else if (Object.prototype.toString.call(data) === '[object Array]') {
if (data.length === 0) {
return false
} else {
return data
}
} else {
if (data === 0) {
return true
} else {
return data
}
}
},
render(){
},
handleCurrentChange(){
this.getData()
},
},
components: {
Az
}
}
</script>
<style>
.el-table th{
padding: 0;
}
.el-table .el-table__expanded-cell {
padding: 0;
}
.el-table th>.cell{
height:40px;
font-size:14px;
font-family:PingFangSC-Medium,PingFang SC;
font-weight:500;
color:rgba(53,53,53,1);
line-height:40px;
}
.el-table td{
padding: 11px 0px 12px;
}
.el-table .cell{
height:17px;
font-size:12px;
font-family:PingFangSC-Regular,PingFang SC;
font-weight:400;
color:rgba(1,22,44,1);
line-height:17px;
}
.el-table .cell .ivu-poptip {
padding: 0 5px;
}
.el-table .cell .ivu-tooltip {
padding: 0;
}
.el-table .cell .ivu-tooltip .ivu-tooltip-rel {
padding: 0 5px;
}
.ivu-poptip-body {
padding: 0;
}
.el-table .cell .ivu-poptip .ivu-poptip-rel{
padding: 0;
}
.el-table .cell .ivu-poptip .ivu-poptip-rel i.on {
color: #2d8cf0;
}
.el-table .cell i.on {
color: #2d8cf0;
}
.el-table--striped .el-table__body tr.el-table__row--striped td {
background:rgba(249,251,253,1);
}
.el-pager li.active{
background-color: #409EFF;
color: #fff;
cursor: default;
border-radius: 3px;
}
.el-pager li,.el-pager li.btn-quicknext{
color:#8e8e8e
}
.el-tooltip__popper {
max-width: 250px;
}
</style>
<style lang="less" scoped>
.table-box{
position: relative;
overflow: hidden;
}
.simpopfilter{
max-height: 200px;
max-width: 206px;
overflow: auto;
}
// 表头筛选内容样式-开始
.filters-li {
padding: 0 16px;
line-height: 28px;
text-align: left;
cursor: pointer;
color: #595959;
}
.filters-li:hover {
background-color: @color-gray-F;
}
.active {
color: @color-blue;
background-color: @color-gray-F;
}
.ivu-icon-funnel {
color: #bbbec4;
}
.custom /deep/ th{
background:rgba(245,247,249,1);
}
.el-table::before {
height: 0;
}
.page{
float: right;
margin-top: 18px;
}
.active {
color: @color-blue;
background-color: @color-gray-F;
}
</style>
2.父组件
···
<div class="list-body">
<simpleTable :config='advise_Config' :getData='getAdvceTableData' @onfilter="doAdviseFilter">
<template slot="itemName" slot-scope="scope">
<a :href="`#/${advise_Config.hrefDetail}/${scope.row.resource_id}`">{{ advise_Config.radioValue === 'eip' ? scope.row.params.ip : scope.row.resource_name }}</a>
</template>
<template slot="cost" slot-scope="scope">
<span>{{ isType(scope.row.cost) ? `¥${scope.row.cost}` : '-' }}</span>
</template>
<template slot="params" slot-scope="scope">
<span v-if="advise_Config.radioValue === 'instance'">{{ isType(scope.row.params) ? `${scope.row.params.cpu}核-${scope.row.params.mem}GB-${scope.row.params.root_volume}GB` : '-' }}</span>
<span v-if="advise_Config.radioValue === 'volume'">{{ isType(scope.row.params) ? `${scope.row.params.size}GB` : '-' }}</span>
<span v-if="advise_Config.radioValue === 'eip'">{{ isType(scope.row.params) ? `${scope.row.params.bandwidth}Mbps` : '-' }}</span>
</template>
<template slot="params_optm" slot-scope="scope">
<span v-if="advise_Config.radioValue === 'instance'">{{ isType(scope.row.params_optm) ? `${scope.row.params_optm.cpu}核-${scope.row.params_optm.mem}GB-${scope.row.params_optm.root_volume}GB` : '-' }}</span>
<span v-if="advise_Config.radioValue === 'volume'">{{ isType(scope.row.params_optm) ? `${scope.row.params_optm.size}GB` : '-' }}</span>
<span v-if="advise_Config.radioValue === 'eip'">{{ isType(scope.row.params_optm) ? `${scope.row.params_optm.bandwidth}Mbps` : '-' }}</span>
</template>
<template slot="advice" slot-scope="scope">
<span>{{ scope.row.advice === 'erase' ? '建议释放' : (scope.row.advice === 'degrade' ? '建议降配' : (scope.row.advice === 'upgrade' ? '建议升配' : '-')) }}</span>
</template>
<template slot="provider" slot-scope="scope">
<span>{{ provider[scope.row.provider] }}</span>
</template>
<template slot="cost_optm" slot-scope="scope">
<div class="costContent">
<div>{{ isType(scope.row.cost_optm) ? `¥${scope.row.cost_optm}` : '-' }}</div>
<img v-if="scope.row.cost_optm - scope.row.cost > 0" src="../../../assets/content_icon/up.png" />
<img v-if="scope.row.cost_optm - scope.row.cost < 0" src="../../../assets/content_icon/down.png" />
</div>
</template>
<template slot="operate" slot-scope="scope">
<a @click="doIgnore(scope.row)">忽略</a>
</template>
<div slot='tableExtend'>
<extendTable :detailConfig="advise_Config.isExtend.detailConfig"></extendTable>
</div>
</simpleTable>
</div>
···
Snipaste_2020-12-08_17-51-56.png