先上效果图
表头的时间首个是搜索项的endTime,后面的时间则是startTime到endTime-1的正序排列,折线图则是时间的正序排列
1、先实现月份表头,用的是项目封装后的el-table,但是跟直接在页面上实现没有啥区别
//数据结构
{"status":200,"message":"SUCCESS","data":{"records":
[{
"customerName":"客户A",
"applicationId":"11111111",
"statisticNum":
{"2024-01_stock":0,
"2023-09_incr":1,
"2023-10_stock":3,
"2023-09_stock":1,
"2023-12_stock":2,
"2024-02_incr":0,
"2023-12_incr":2,
"2023-11_incr":1,
"2023-11_stock":1,
"2024-01_incr":0,
"2024-02_stock":0,
"2023-10_incr":3}
},],"total":"xxx","size":"xxx","current":"xxx","searchCount":true,"pages":"xxxx"}}
//存量&增量下拉框值v-model="incrOrstock"
<el-option value="6" label="存量&增量"></el-option>
<el-option value="2" label="存量"></el-option>
<el-option value="3" label="增量"></el-option>
//methods
//在调取列表接口前调用获取头部方法
getList(){
const params = {
aa:xxx,
bb:xxx
};
this.generateCols();//调用表头方法
tableApi(params).then((res)=>{
//处理完下面的表头数据这的this.tableDefs里就有了cols属性,页面上展示的数据用的也是cols里的
this.tableDefs.data = res.data.records; //给el-table赋值
//从这是后面设置eacharts时用的
if (res.data.records.length) {
this.incrArr = [];//增量
this.stockArr = [];//存量
this.statisticNum = [];//总的数据
res.data.records.forEach((ele) => {
this.statisticNum.push(ele.statisticNum);
});
}
//调用处理echarts
this.initData();
})
},
//处理时间数据,将endTime放在第一个,其余的正序排
getMonthDifference(start_date, end_date) {
const start = new Date(start_date);
const end = new Date(end_date);
const difference = [];
difference.push(end_date);//先将endTime放进去
while (start < end) {
const currentMonth = `${start.getFullYear()}-${(start.getMonth() + 1)
.toString()
.padStart(2, "0")}`; //补零
difference.push(currentMonth);
//时间小于endTime-1时结束循环
start.setMonth(start.getMonth() + 1);
}
//difference就是除endTime的其余时间正序排列
return difference;
},
generateCols() {
const months = this.getMonthDifference(
this.dateRange[0],//搜索项的startTime
this.dateRange[1]//搜索项的endTime
);
const cols = [];
//第一列表头
this.$set(cols, cols.length, {
name: "客户名称",
field: "customerName",//field就是el-table-column上的prop
align: "center",
});
const chartItem = {
name: "统计图",
align: "center",
cols: [],
};
//将第一块的二级表头放在统计图下
if (this.incrOrstock % 3 === 0) {//下拉框值能整除3就是增量
this.$set(chartItem.cols, chartItem.cols.length, {
name: "增量",
field: "chartIncr",
//图表插槽
});
}
if (this.incrOrstock % 2 === 0) {//下拉框值能整除2就是存量
this.$set(chartItem.cols, chartItem.cols.length, {
name: "存量",
field: "chartStock",
});
}
this.$set(cols, cols.length, chartItem);
//跟上面一样,将二级表头放在时间一级表头下
months.forEach((month) => {
const newItem = {
name: month,
field: "month",
align: "center",
cols: [],
};
if (this.incrOrstock % 3 === 0) {
this.$set(newItem.cols, newItem.cols.length, {
name: "增量",
field: "statisticNum." + month + "_incr",
align: "center",
});
}
if (this.incrOrstock % 2 === 0) {
this.$set(newItem.cols, newItem.cols.length, {
name: "存量",
field: "statisticNum." + month + "_stock",
align: "center",
});
}
this.$set(cols, cols.length, newItem);
});
this.tableDefs.cols = cols;//放到el-table-column上
},
2、实现将charts放到el-table中(主要就是想办法给你要放图标的元素指定id,因为echarts需要获取到指定元素才能初始化)
//这是在el-table中,因为我这是公共table组件,需要加一些判断来防止影响到其他页面
//html
<div v-if="item.hasOwnProperty('cols')">
<el-table-column
v-for="(item2, index2) in item.cols" //item就是遍历的tableData
:key="index2"
:type="item2.type"
:label="item2.name"
:prop="item2.field"
:align="item.align || 'center'"
>
<template slot-scope="scope">
<div
v-if="item2.field == 'chartStock'"
class="chart_box"
:id="
item2.field == 'chartStock'//重要的就是给id这,想办法给到你要展示图表的元素唯一id值
? 'chart_stock_' + scope.$index
: ''
"
></div>
<div
v-if="item2.field == 'chartIncr'"
class="chart_box"
:id="
item2.field == 'chartIncr'
? 'chart_incr_' + scope.$index
: ''
"
></div>
//这下面的判断是为了防止切换单个存量or增量时数据展示错误
<div v-if="item2.field.split('_')[1] == 'stock'">
{{
item.cols.length == 2
? scope.row.statisticNum[item.cols[1].field.split(".")[1]]
: scope.row.statisticNum[item.cols[0].field.split(".")[1]]
}}
</div>
<div v-if="item2.field.split('_')[1] == 'incr' && item.cols">
{{ scope.row.statisticNum[item.cols[0].field.split(".")[1]] }}
</div>
</template>
</el-table-column>
</div>
//methods
initData() {
this.$nextTick(() => {
//判断是否下拉选项带着存量
if (this.incrOrstock == 6 || this.incrOrstock == 2) {
//this.tableDefs.pages.size就是当前table展示几条数据,我这默认是一页十条
for (let i = 0; i < this.tableDefs.pages.size; i++) {
let stockArr = this.statisticNum[i];
let sortArr = [];
//归类数组并按时间排序
for (let [key, value] of Object.entries(stockArr)) {
if (key.split("_")[1] == "stock") {//截取判断一下是否是存量的数据
//将存量数据归类按照时间前后排序
sortArr.push({ key: key, value: value });
}
}
sortArr.sort((a, b) => {
const dateA = new Date(a.key.split("_")[0]);
const dateB = new Date(b.key.split("_")[0]);
return dateA - dateB;
});
let dataArr = [];
//排序完不需要key值了
sortArr.forEach((ele) => {
dataArr.push(ele.value); //图标需要的数据
});
//在el-table-column中已经定义好的id直接获取初始图标就行
let myChart = echarts.init(
document.getElementById("chart_stock_" + i)
);
// 绘制图表
myChart.setOption({
xAxis: {
type: "category",
boundaryGap: false,
data: [],
},
yAxis: {
type: "value",
axisLabel: {
show: false, // 不显示坐标轴上的文字
},
splitLine: {
show: false, // 不显示网格线
},
axisTick: {
show: false, // 不显示坐标轴刻度线
},
},
series: [
{
data: dataArr,
type: "line",
areaStyle: {},
},
],
});
//监听容器大小发生变化重绘图表
const element = document.getElementById("chart_stock_" + i);
const resizeObserver = new ResizeObserver((entries) => {
myChart.resize();
});
resizeObserver.observe(element);
}
}
//增量处理方式相同
if (this.incrOrstock == 6 || this.incrOrstock == 3) {
........
}
});
},