柱状图用百分比实现非线性结构
This commit is contained in:
parent
94c6812662
commit
8de15e50dd
|
|
@ -137,7 +137,7 @@
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<!-- 柱状图 -->
|
<!-- 柱状图 -->
|
||||||
<div v-else ref="barRef" style="width: 100%; height: calc(100vh - 580px); margin-top: 20px"></div>
|
<div v-else ref="barRef" style="width: 100%; height: calc(100vh - 480px); margin-top: 20px"></div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -331,261 +331,168 @@ export default {
|
||||||
chart.setOption(option)
|
chart.setOption(option)
|
||||||
},
|
},
|
||||||
initBarChart() {
|
initBarChart() {
|
||||||
const barDom = this.$refs.barRef
|
const barDom = this.$refs.barRef;
|
||||||
const barChart = echarts.init(barDom)
|
const barChart = echarts.init(barDom);
|
||||||
|
|
||||||
const companyNames = this.tableList.map((item) => item.companyName)
|
const companyNames = this.tableList.map((item) => item.companyName);
|
||||||
const equipTotals = this.tableList.map((item) => item.maNum)
|
const equipTotals = this.tableList.map((item) => item.maNum);
|
||||||
const toolTotals = this.tableList.map((item) => item.toolNum)
|
const toolTotals = this.tableList.map((item) => item.toolNum);
|
||||||
|
|
||||||
// 定义固定的y轴刻度
|
// 双系列专用-固定刻度
|
||||||
const yAxisTicks = ['0', '200', '500', '1000', '10000', '100000', '200000+']
|
const yAxisTicks = ['0', '200', '500', '1000', '10000', '100000', '200000+'];
|
||||||
|
const yAxisValues = [0, 200, 500, 1000, 10000, 100000, 200000];
|
||||||
|
|
||||||
// 定义刻度对应的实际数值
|
// 双系列数值映射(预计算映射后的数据)
|
||||||
const yAxisValues = [0, 200, 500, 1000, 10000, 100000, 200000]
|
const mapToStepHeight = (value) => {
|
||||||
|
let i = 0;
|
||||||
|
while (i < yAxisValues.length - 1 && value > yAxisValues[i + 1]) i++;
|
||||||
|
const rangeStart = yAxisValues[i];
|
||||||
|
const rangeEnd = yAxisValues[i + 1] || yAxisValues[i];
|
||||||
|
const ratio = (value - rangeStart) / (rangeEnd - rangeStart || 1);
|
||||||
|
return i + ratio;
|
||||||
|
};
|
||||||
|
const mappedEquipTotals = equipTotals.map(mapToStepHeight);
|
||||||
|
const mappedToolTotals = toolTotals.map(mapToStepHeight);
|
||||||
|
|
||||||
// 数据映射函数:将原始数据映射到对应的y轴刻度索引
|
// ---------------------- 初始化图表 ----------------------
|
||||||
const mapDataToYAxis = (value) => {
|
const initShowEquip = equipTotals.some(v => v > 0);
|
||||||
for (let i = 0; i < yAxisValues.length; i++) {
|
const initShowTool = toolTotals.some(v => v > 0);
|
||||||
if (value <= yAxisValues[i]) {
|
const initShowBoth = initShowEquip && initShowTool;
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return yAxisValues.length - 1 // 超过最大值的都映射到最后一个刻度
|
|
||||||
}
|
|
||||||
|
|
||||||
// 映射后的系列数据
|
barChart.setOption({
|
||||||
const mappedEquipTotals = equipTotals.map(mapDataToYAxis)
|
|
||||||
const mappedToolTotals = toolTotals.map(mapDataToYAxis)
|
|
||||||
|
|
||||||
// 判断是否同时显示两个系列
|
|
||||||
const showBothSeries = equipTotals.some(val => val > 0) && toolTotals.some(val => val > 0)
|
|
||||||
|
|
||||||
// 动态生成y轴配置
|
|
||||||
let yAxisConfig
|
|
||||||
if (showBothSeries) {
|
|
||||||
// 同时显示两个系列时,使用固定刻度的category类型y轴
|
|
||||||
yAxisConfig = {
|
|
||||||
type: 'category',
|
|
||||||
data: yAxisTicks,
|
|
||||||
name: '数量(个)',
|
|
||||||
nameTextStyle: { fontSize: 14 },
|
|
||||||
|
|
||||||
axisTick: {
|
|
||||||
show: true,
|
|
||||||
alignWithLabel: true,
|
|
||||||
length: 4
|
|
||||||
},
|
|
||||||
|
|
||||||
axisLabel: {
|
|
||||||
fontSize: 12,
|
|
||||||
margin: 8 // 增加标签与坐标轴的间距
|
|
||||||
},
|
|
||||||
|
|
||||||
// 调整位置
|
|
||||||
offset: 0,
|
|
||||||
|
|
||||||
// 强制将坐标轴显示在网格边缘
|
|
||||||
boundaryGap: false,
|
|
||||||
|
|
||||||
// 分割线配置
|
|
||||||
splitLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
type: 'dashed',
|
|
||||||
color: '#e0e0e0'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 只显示一个系列时,使用value类型y轴
|
|
||||||
yAxisConfig = {
|
|
||||||
type: 'value',
|
|
||||||
name: '数量(个)',
|
|
||||||
nameTextStyle: { fontSize: 14 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 动态生成series配置
|
|
||||||
const series = []
|
|
||||||
|
|
||||||
// 添加装备总数系列(如果有数据)
|
|
||||||
if (equipTotals.some(val => val > 0)) {
|
|
||||||
series.push({
|
|
||||||
name: '装备总数',
|
|
||||||
type: 'bar',
|
|
||||||
barWidth: 35,
|
|
||||||
barGap: '0%', // ← 两个柱子紧贴
|
|
||||||
barCategoryGap: '0%', // ← 类目间无空隙
|
|
||||||
itemStyle: {
|
|
||||||
color: '#5470C6',
|
|
||||||
borderRadius: [4, 4, 0, 0],
|
|
||||||
},
|
|
||||||
data: showBothSeries ? mappedEquipTotals : equipTotals,
|
|
||||||
coordinateSystem: 'cartesian2d',
|
|
||||||
xAxisIndex: 0,
|
|
||||||
yAxisIndex: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加工具总数系列(如果有数据)
|
|
||||||
if (toolTotals.some(val => val > 0)) {
|
|
||||||
series.push({
|
|
||||||
name: '工具总数',
|
|
||||||
type: 'bar',
|
|
||||||
barWidth: 35,
|
|
||||||
barGap: '0%',
|
|
||||||
barCategoryGap: '0%',
|
|
||||||
itemStyle: {
|
|
||||||
color: '#91CC75',
|
|
||||||
borderRadius: [4, 4, 0, 0],
|
|
||||||
},
|
|
||||||
data: showBothSeries ? mappedToolTotals : toolTotals,
|
|
||||||
coordinateSystem: 'cartesian2d',
|
|
||||||
xAxisIndex: 0,
|
|
||||||
yAxisIndex: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const option = {
|
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
formatter: function (params) {
|
formatter: (params) => {
|
||||||
// 自定义tooltip显示原始数值
|
let res = `${params[0].name}<br/>`;
|
||||||
let result = params[0].name + '<br/>'
|
params.forEach(p => {
|
||||||
params.forEach(param => {
|
const val = p.seriesName === '装备总数'
|
||||||
// 获取原始数据
|
? equipTotals[p.dataIndex]
|
||||||
const originalValue = param.seriesName === '装备总数'
|
: toolTotals[p.dataIndex];
|
||||||
? equipTotals[param.dataIndex]
|
res += `${p.seriesName}: ${val}个<br/>`;
|
||||||
: toolTotals[param.dataIndex]
|
});
|
||||||
result += `${param.seriesName}: ${originalValue}个<br/>`
|
return res;
|
||||||
})
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// ✅ 永久双图例,永不消失
|
||||||
legend: {
|
legend: {
|
||||||
top: 0,
|
top: 0,
|
||||||
right: 20,
|
right: 20,
|
||||||
data: series.map(s => s.name), // 动态设置图例数据
|
data: ['装备总数', '工具总数'],
|
||||||
|
selected: {
|
||||||
|
'装备总数': initShowEquip,
|
||||||
|
'工具总数': initShowTool
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
grid: { left: '3%', right: '3%', bottom: '3%', top: '15%', containLabel: true },
|
||||||
grid: {
|
|
||||||
left: '3%',
|
|
||||||
right: '3%',
|
|
||||||
bottom: '3%',
|
|
||||||
top: '15%',
|
|
||||||
containLabel: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: companyNames,
|
data: companyNames,
|
||||||
|
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
fontSize: 13,
|
fontSize: 13, interval: 0, lineHeight: 16,
|
||||||
interval: 0, // 强制全部显示
|
formatter: (name) => {
|
||||||
lineHeight: 16,
|
if (name.length <= 4) return name;
|
||||||
formatter: function (name) {
|
let res = '';
|
||||||
const maxLen = 3 // 每行6字符,可调整
|
for (let i = 0; i < Math.ceil(name.length / 4); i++) {
|
||||||
if (name.length <= maxLen) return name
|
res += name.substring(i * 4, (i + 1) * 4) + '\n';
|
||||||
const rows = Math.ceil(name.length / maxLen)
|
|
||||||
let res = ''
|
|
||||||
for (let i = 0; i < rows; i++) {
|
|
||||||
res += name.substring(i * maxLen, (i + 1) * maxLen) + '\n'
|
|
||||||
}
|
}
|
||||||
return res
|
return res;
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
yAxis: yAxisConfig, // 使用动态生成的y轴配置
|
|
||||||
|
|
||||||
series: series, // 使用动态生成的系列配置
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
barChart.setOption(option)
|
},
|
||||||
|
yAxis: initShowBoth
|
||||||
// 监听图例点击事件
|
? { // 双系列-固定阶梯刻度
|
||||||
barChart.on('legendselectchanged', (params) => {
|
type: 'value',
|
||||||
// 获取当前显示的系列
|
|
||||||
const selectedSeries = Object.keys(params.selected).filter(key => params.selected[key]);
|
|
||||||
|
|
||||||
// 判断是否同时显示两个系列
|
|
||||||
const showBothSeries = selectedSeries.length > 1;
|
|
||||||
|
|
||||||
// 重新生成y轴配置
|
|
||||||
let newYAxisConfig;
|
|
||||||
if (showBothSeries) {
|
|
||||||
// 同时显示两个系列时,使用固定刻度的category类型y轴
|
|
||||||
newYAxisConfig = {
|
|
||||||
type: 'category',
|
|
||||||
data: yAxisTicks,
|
|
||||||
name: '数量(个)',
|
name: '数量(个)',
|
||||||
nameTextStyle: { fontSize: 14 },
|
nameTextStyle: { fontSize: 14 },
|
||||||
|
min: 0, max: 6, interval: 1,
|
||||||
axisTick: {
|
axisLabel: { fontSize:12, margin:8, formatter: idx => yAxisTicks[Math.floor(idx)] },
|
||||||
show: true,
|
splitLine: { lineStyle: { type: 'dashed', color: '#e0e0e0' } }
|
||||||
alignWithLabel: true,
|
|
||||||
length: 4
|
|
||||||
},
|
|
||||||
|
|
||||||
axisLabel: {
|
|
||||||
fontSize: 12,
|
|
||||||
margin: 8 // 增加标签与坐标轴的间距
|
|
||||||
},
|
|
||||||
|
|
||||||
// 调整位置
|
|
||||||
offset: 0,
|
|
||||||
|
|
||||||
// 强制将坐标轴显示在网格边缘
|
|
||||||
boundaryGap: false,
|
|
||||||
|
|
||||||
// 分割线配置
|
|
||||||
splitLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
type: 'dashed',
|
|
||||||
color: '#e0e0e0'
|
|
||||||
}
|
}
|
||||||
}
|
: { // 单系列-原生线性轴
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// 只显示一个系列时,使用value类型y轴
|
|
||||||
newYAxisConfig = {
|
|
||||||
type: 'value',
|
type: 'value',
|
||||||
name: '数量(个)',
|
name: '数量(个)',
|
||||||
nameTextStyle: { fontSize: 14 }
|
nameTextStyle: { fontSize: 14 }
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
...(initShowEquip ? [{
|
||||||
|
name: '装备总数', type: 'bar', barWidth:35,
|
||||||
|
barGap:'0%', barCategoryGap:'0%',
|
||||||
|
itemStyle: { color:'#5470C6', borderRadius:[4,4,0,0] },
|
||||||
|
data: initShowBoth ? mappedEquipTotals : equipTotals
|
||||||
|
}] : []),
|
||||||
|
...(initShowTool ? [{
|
||||||
|
name: '工具总数', type: 'bar', barWidth:35,
|
||||||
|
barGap:'0%', barCategoryGap:'0%',
|
||||||
|
itemStyle: { color:'#91CC75', borderRadius:[4,4,0,0] },
|
||||||
|
data: initShowBoth ? mappedToolTotals : toolTotals
|
||||||
|
}] : [])
|
||||||
|
]
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
// ---------------------- 严格按你指定格式编写【核心修复Y轴不生效问题】 ----------------------
|
||||||
|
barChart.on('legendselectchanged', (params) => {
|
||||||
|
// 获取当前显示的系列
|
||||||
|
const selectedSeries = Object.keys(params.selected).filter(key => params.selected[key]);
|
||||||
|
// 判断是否同时显示两个系列
|
||||||
|
const showBothSeries = selectedSeries.length > 1;
|
||||||
|
|
||||||
|
// ✅ 核心修复:单系列清空所有固定配置,强制线性轴生效
|
||||||
|
let newYAxisConfig;
|
||||||
|
if (showBothSeries) {
|
||||||
|
// 双系列:固定阶梯刻度(完整配置)
|
||||||
|
newYAxisConfig = {
|
||||||
|
type: 'value',
|
||||||
|
name: '数量(个)',
|
||||||
|
nameTextStyle: { fontSize: 14 },
|
||||||
|
min: 0, // 固定最小值
|
||||||
|
max: 6, // 固定最大值
|
||||||
|
interval: 1, // 固定刻度间隔
|
||||||
|
axisTick: { show: true, alignWithLabel: true, length: 4 },
|
||||||
|
axisLabel: { fontSize: 12, margin:8, formatter: idx => yAxisTicks[Math.floor(idx)] },
|
||||||
|
splitLine: { lineStyle: { type: 'dashed', color: '#e0e0e0' } }
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// ✅ 单系列:彻底清空所有固定配置,强制原生线性轴【必生效】
|
||||||
|
newYAxisConfig = {
|
||||||
|
type: 'value',
|
||||||
|
name: '数量(个)',
|
||||||
|
nameTextStyle: { fontSize: 14 },
|
||||||
|
min: undefined, // 清空固定最小值
|
||||||
|
max: undefined, // 清空固定最大值
|
||||||
|
interval: undefined, // 清空固定间隔
|
||||||
|
axisLabel: { // 清空自定义格式化,恢复原生数值显示
|
||||||
|
fontSize: 12,
|
||||||
|
margin: 8,
|
||||||
|
formatter: undefined
|
||||||
|
},
|
||||||
|
splitLine: { lineStyle: { type: 'solid', color: '#e0e0e0' } }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新y轴配置
|
// ✅ 关键:更新Y轴时添加 replaceMerge: true,强制覆盖旧配置(解决不生效核心)
|
||||||
barChart.setOption({
|
barChart.setOption({ yAxis: newYAxisConfig }, { replaceMerge: ['yAxis'], silent: true });
|
||||||
yAxis: newYAxisConfig
|
|
||||||
});
|
|
||||||
|
|
||||||
// 更新系列数据
|
// 更新系列数据
|
||||||
const newSeries = [];
|
const newSeries = [];
|
||||||
|
|
||||||
// 更新装备总数系列(如果显示)
|
|
||||||
if (params.selected['装备总数']) {
|
if (params.selected['装备总数']) {
|
||||||
newSeries.push({
|
newSeries.push({
|
||||||
name: '装备总数',
|
name: '装备总数',
|
||||||
|
type: 'bar', barWidth:35,
|
||||||
|
barGap:'0%', barCategoryGap:'0%',
|
||||||
|
itemStyle: { color:'#5470C6', borderRadius:[4,4,0,0] },
|
||||||
data: showBothSeries ? mappedEquipTotals : equipTotals
|
data: showBothSeries ? mappedEquipTotals : equipTotals
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新工具总数系列(如果显示)
|
|
||||||
if (params.selected['工具总数']) {
|
if (params.selected['工具总数']) {
|
||||||
newSeries.push({
|
newSeries.push({
|
||||||
name: '工具总数',
|
name: '工具总数',
|
||||||
|
type: 'bar', barWidth:35,
|
||||||
|
barGap:'0%', barCategoryGap:'0%',
|
||||||
|
itemStyle: { color:'#91CC75', borderRadius:[4,4,0,0] },
|
||||||
data: showBothSeries ? mappedToolTotals : toolTotals
|
data: showBothSeries ? mappedToolTotals : toolTotals
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
barChart.setOption({ series: newSeries });
|
||||||
barChart.setOption({
|
|
||||||
series: newSeries
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue