核心实体类设计
1.统一返回结果类 (Result.java)
import lombok.Data;
@Data
public class Result<T> {
private boolean success;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setSuccess(true);
result.setData(data);
return result;
}
public static <T> Result<T> error(String message) {
Result<T> result = new Result<>();
result.setSuccess(false);
result.setMessage(message);
return result;
}
}
2.图表数据实体 (ChartData.java)
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class ChartData implements Serializable {
/**
* 图表标题
*/
private String title;
/**
* X轴分类数据
*/
private List<String> categories;
/**
* 系列数据
*/
private List<SeriesConfig> series;
}
3.系列配置实体 (SeriesConfig.java)
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class SeriesConfig {
/**
* 系列名称
*/
private String name;
/**
* 图表类型:line, bar, pie, scatter等
*/
private String type;
/**
* 普通数据值 - 用于line, bar, pie等图表
*/
private List<Object> data;
/**
* 散点图专用数据 - 用于scatter图表
* 格式:List<Object[]> 其中每个Object[]包含[x, y]坐标
*/
private List<Object[]> scatterData;
/**
* 颜色
*/
private String color;
/**
* 是否为面积图
*/
private boolean areaStyle = false;
/**
* 堆叠标识
*/
private String stack;
/**
* 获取实际数据 - 根据图表类型返回对应的数据
*/
public List<Object> getActualData() {
if ("scatter".equals(type) && scatterData != null) {
// 散点图数据需要转换为ECharts格式
List<Object> result = new ArrayList<>();
for (Object[] point : scatterData) {
if (point.length >= 2) {
result.add(new Object[]{point[0], point[1]});
}
}
return result;
}
return data;
}
}
4.图表数据构建工具 (ChartUtil.java)
import org.example.flowable.entity.ChartData;
import org.example.flowable.entity.SeriesConfig;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* 图表数据构建工具类
* 提供各种图表类型的数据构建方法
*/
public class ChartUtil {
/**
* 构建通用图表数据
* @param title 图表标题
* @param categories X轴分类数据
* @param seriesConfigs 系列配置列表
* @return ChartData
*/
public static ChartData buildChartData(String title, List<String> categories, List<SeriesConfig> seriesConfigs) {
ChartData chartData = new ChartData();
chartData.setTitle(title);
chartData.setCategories(categories);
chartData.setSeries(seriesConfigs);
return chartData;
}
/**
* 构建趋势图数据(折线图)
* @param title 图表标题
* @param categories X轴分类数据
* @param seriesDataMap 系列数据映射 (系列名 -> 数据列表)
* @return ChartData
*/
public static ChartData buildTrendChart(String title, List<String> categories,
Map<String, List<Object>> seriesDataMap) {
List<SeriesConfig> configs = new ArrayList<>();
seriesDataMap.forEach((name, data) -> {
SeriesConfig config = new SeriesConfig();
config.setName(name);
config.setType("line");
config.setData(data);
configs.add(config);
});
return buildChartData(title, categories, configs);
}
/**
* 构建柱状图数据
* @param title 图表标题
* @param categories X轴分类数据
* @param seriesDataMap 系列数据映射 (系列名 -> 数据列表)
* @return ChartData
*/
public static ChartData buildBarChart(String title, List<String> categories,
Map<String, List<Object>> seriesDataMap) {
List<SeriesConfig> configs = new ArrayList<>();
seriesDataMap.forEach((name, data) -> {
SeriesConfig config = new SeriesConfig();
config.setName(name);
config.setType("bar");
config.setData(data);
configs.add(config);
});
return buildChartData(title, categories, configs);
}
/**
* 构建饼图数据
* @param title 图表标题
* @param categories 分类名称列表
* @param data 对应的数据值列表
* @return ChartData
*/
public static ChartData buildPieChart(String title, List<String> categories, List<Object> data) {
List<SeriesConfig> configs = new ArrayList<>();
SeriesConfig config = new SeriesConfig();
config.setName(title);
config.setType("pie");
config.setData(data);
configs.add(config);
return buildChartData(title, categories, configs);
}
/**
* 构建面积图数据
* @param title 图表标题
* @param categories X轴分类数据
* @param seriesDataMap 系列数据映射
* @return ChartData
*/
public static ChartData buildAreaChart(String title, List<String> categories,
Map<String, List<Object>> seriesDataMap) {
List<SeriesConfig> configs = new ArrayList<>();
seriesDataMap.forEach((name, data) -> {
SeriesConfig config = new SeriesConfig();
config.setName(name);
config.setType("line");
config.setData(data);
config.setAreaStyle(true); // 标记为面积图
configs.add(config);
});
return buildChartData(title, categories, configs);
}
/**
* 构建散点图数据
* @param title 图表标题
* @param seriesDataMap 系列数据映射 (系列名 -> 散点数据列表)
* @return ChartData
*/
public static ChartData buildScatterChart(String title, Map<String, List<Object[]>> seriesDataMap) {
List<SeriesConfig> configs = new ArrayList<>();
seriesDataMap.forEach((name, data) -> {
SeriesConfig config = new SeriesConfig();
config.setName(name);
config.setType("scatter");
config.setScatterData(data);
configs.add(config);
});
return buildChartData(title, new ArrayList<>(), configs);
}
/**
* 快速构建单系列图表
* @param title 图表标题
* @param categories X轴分类数据
* @param seriesName 系列名称
* @param seriesType 系列类型 (line, bar, pie等)
* @param data 数据列表
* @return ChartData
*/
public static ChartData buildSingleSeriesChart(String title, List<String> categories,
String seriesName, String seriesType, List<Object> data) {
SeriesConfig config = new SeriesConfig();
config.setName(seriesName);
config.setType(seriesType);
config.setData(data);
return buildChartData(title, categories, Arrays.asList(config));
}
/**
* 构建堆叠柱状图
* @param title 图表标题
* @param categories X轴分类数据
* @param seriesDataMap 系列数据映射
* @param stackName 堆叠名称
* @return ChartData
*/
public static ChartData buildStackedBarChart(String title, List<String> categories,
Map<String, List<Object>> seriesDataMap, String stackName) {
List<SeriesConfig> configs = new ArrayList<>();
seriesDataMap.forEach((name, data) -> {
SeriesConfig config = new SeriesConfig();
config.setName(name);
config.setType("bar");
config.setData(data);
config.setStack(stackName);
configs.add(config);
});
return buildChartData(title, categories, configs);
}
/**
* 构建堆叠面积图
* @param title 图表标题
* @param categories X轴分类数据
* @param seriesDataMap 系列数据映射
* @param stackName 堆叠名称
* @return ChartData
*/
public static ChartData buildStackedAreaChart(String title, List<String> categories,
Map<String, List<Object>> seriesDataMap, String stackName) {
List<SeriesConfig> configs = new ArrayList<>();
seriesDataMap.forEach((name, data) -> {
SeriesConfig config = new SeriesConfig();
config.setName(name);
config.setType("line");
config.setData(data);
config.setAreaStyle(true);
config.setStack(stackName);
configs.add(config);
});
return buildChartData(title, categories, configs);
}
/**
* 构建雷达图数据
* @param title 图表标题
* @param indicators 雷达图指标
* @param seriesDataMap 系列数据映射
* @return ChartData
*/
public static ChartData buildRadarChart(String title, List<String> indicators,
Map<String, List<Object>> seriesDataMap) {
List<SeriesConfig> configs = new ArrayList<>();
seriesDataMap.forEach((name, data) -> {
SeriesConfig config = new SeriesConfig();
config.setName(name);
config.setType("radar");
config.setData(data);
configs.add(config);
});
return buildChartData(title, indicators, configs);
}
/**
* 构建仪表盘数据
* @param title 图表标题
* @param seriesName 系列名称
* @param value 数值
* @param max 最大值
* @return ChartData
*/
public static ChartData buildGaugeChart(String title, String seriesName, Object value, Object max) {
List<SeriesConfig> configs = new ArrayList<>();
SeriesConfig config = new SeriesConfig();
config.setName(seriesName);
config.setType("gauge");
config.setData(Arrays.asList(value));
configs.add(config);
return buildChartData(title, new ArrayList<>(), configs);
}
}
图表数据控制器
import lombok.extern.slf4j.Slf4j;
import org.example.flowable.common.Result;
import org.example.flowable.entity.ChartData;
import org.example.flowable.entity.SeriesConfig;
import org.example.flowable.utils.ChartUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
@RestController
@RequestMapping("/api/chart")
@Slf4j
public class ChartController {
@GetMapping("/trend")
public Result<ChartData> getTrendData() {
try {
String title = "流程处理效率趋势图";
List<String> categories = Arrays.asList(
"2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04",
"2024-01-05", "2024-01-06", "2024-01-07", "2024-01-08"
);
Map<String, List<Object>> seriesDataMap = new HashMap<>();
seriesDataMap.put("处理效率", Arrays.asList(65, 68, 72, 70, 75, 78, 80, 82));
seriesDataMap.put("处理数量", Arrays.asList(120, 135, 150, 140, 160, 170, 180, 185));
ChartData chartData = ChartUtil.buildTrendChart(title, categories, seriesDataMap);
return Result.success(chartData);
} catch (Exception e) {
return Result.error("获取趋势图数据失败");
}
}
@GetMapping("/stats")
public Result<ChartData> getStatsData() {
try {
String title = "流程实例统计";
List<String> categories = Arrays.asList("进行中", "已完成", "已暂停", "已取消");
Map<String, List<Object>> seriesDataMap = new HashMap<>();
seriesDataMap.put("流程数量", Arrays.asList(45, 120, 30, 15));
ChartData chartData = ChartUtil.buildBarChart(title, categories, seriesDataMap);
return Result.success(chartData);
} catch (Exception e) {
return Result.error("获取统计图数据失败");
}
}
@GetMapping("/user-activity")
public Result<ChartData> getUserActivityData() {
try {
String title = "用户活跃度统计";
List<String> categories = Arrays.asList("周一", "周二", "周三", "周四", "周五", "周六", "周日");
Map<String, List<Object>> seriesDataMap = new HashMap<>();
seriesDataMap.put("活跃用户", Arrays.asList(120, 132, 101, 134, 90, 230, 210));
seriesDataMap.put("新增用户", Arrays.asList(20, 25, 18, 30, 15, 45, 35));
ChartData chartData = ChartUtil.buildTrendChart(title, categories, seriesDataMap);
return Result.success(chartData);
} catch (Exception e) {
return Result.error("获取用户活跃度数据失败");
}
}
@GetMapping("/task-completion")
public Result<ChartData> getTaskCompletionData() {
try {
String title = "任务完成率";
List<String> categories = Arrays.asList("已完成", "进行中", "待处理", "已取消");
List<Object> data = Arrays.asList(65, 20, 10, 5);
ChartData chartData = ChartUtil.buildPieChart(title, categories, data);
return Result.success(chartData);
} catch (Exception e) {
return Result.error("获取任务完成率数据失败");
}
}
@GetMapping("/sales-trend")
public Result<ChartData> getSalesTrendData() {
try {
String title = "销售趋势分析";
List<String> categories = Arrays.asList("Q1", "Q2", "Q3", "Q4");
Map<String, List<Object>> seriesDataMap = new HashMap<>();
seriesDataMap.put("产品A", Arrays.asList(100, 120, 150, 180));
seriesDataMap.put("产品B", Arrays.asList(80, 90, 110, 130));
ChartData chartData = ChartUtil.buildBarChart(title, categories, seriesDataMap);
return Result.success(chartData);
} catch (Exception e) {
return Result.error("获取销售趋势数据失败");
}
}
@GetMapping("/area-chart")
public Result<ChartData> getAreaChartData() {
try {
String title = "销售面积图";
List<String> categories = Arrays.asList("1月", "2月", "3月", "4月", "5月", "6月");
Map<String, List<Object>> seriesDataMap = new HashMap<>();
seriesDataMap.put("销售额", Arrays.asList(120, 150, 180, 200, 220, 250));
seriesDataMap.put("利润", Arrays.asList(30, 40, 50, 60, 70, 80));
ChartData chartData = ChartUtil.buildAreaChart(title, categories, seriesDataMap);
return Result.success(chartData);
} catch (Exception e) {
return Result.error("获取面积图数据失败");
}
}
@GetMapping("/scatter")
public Result<ChartData> getScatterData() {
try {
String title = "用户行为分析";
Map<String, List<Object[]>> seriesDataMap = new HashMap<>();
// 用户A的行为数据 [x, y] 坐标
List<Object[]> userAData = Arrays.asList(
new Object[]{10, 20},
new Object[]{15, 25},
new Object[]{20, 30},
new Object[]{25, 35}
);
seriesDataMap.put("用户A", userAData);
// 用户B的行为数据
List<Object[]> userBData = Arrays.asList(
new Object[]{12, 18},
new Object[]{18, 28},
new Object[]{22, 32},
new Object[]{28, 38}
);
seriesDataMap.put("用户B", userBData);
ChartData chartData = ChartUtil.buildScatterChart(title, seriesDataMap);
return Result.success(chartData);
} catch (Exception e) {
return Result.error("获取散点图数据失败");
}
}
@GetMapping("/stacked-bar")
public Result<ChartData> getStackedBarData() {
try {
String title = "部门业绩统计";
List<String> categories = Arrays.asList("Q1", "Q2", "Q3", "Q4");
Map<String, List<Object>> seriesDataMap = new HashMap<>();
seriesDataMap.put("销售部", Arrays.asList(100, 120, 150, 180));
seriesDataMap.put("技术部", Arrays.asList(80, 90, 110, 130));
seriesDataMap.put("市场部", Arrays.asList(60, 70, 80, 90));
ChartData chartData = ChartUtil.buildStackedBarChart(title, categories, seriesDataMap, "部门业绩");
log.info("堆叠柱状图数据构建完成");
return Result.success(chartData);
} catch (Exception e) {
log.error("获取堆叠柱状图数据失败", e);
return Result.error("获取堆叠柱状图数据失败");
}
}
@GetMapping("/radar")
public Result<ChartData> getRadarData() {
try {
String title = "员工能力评估";
List<String> indicators = Arrays.asList("技术能力", "沟通能力", "学习能力", "创新能力", "团队协作", "执行力");
Map<String, List<Object>> seriesDataMap = new HashMap<>();
seriesDataMap.put("张三", Arrays.asList(85, 90, 80, 75, 88, 82));
seriesDataMap.put("李四", Arrays.asList(78, 85, 90, 88, 80, 85));
seriesDataMap.put("王五", Arrays.asList(92, 75, 85, 90, 85, 88));
ChartData chartData = ChartUtil.buildRadarChart(title, indicators, seriesDataMap);
return Result.success(chartData);
} catch (Exception e) {
return Result.error("获取雷达图数据失败");
}
}
@GetMapping("/gauge")
public Result<ChartData> getGaugeData() {
try {
String title = "系统性能监控";
String seriesName = "CPU使用率";
Object value = 75; // 当前值
Object max = 100; // 最大值
ChartData chartData = ChartUtil.buildGaugeChart(title, seriesName, value, max);
return Result.success(chartData);
} catch (Exception e) {
return Result.error("获取仪表盘数据失败");
}
}
@GetMapping("/multi-gauge")
public Result<ChartData> getMultiGaugeData() {
try {
String title = "系统监控面板";
// 构建多个仪表盘数据
List<SeriesConfig> configs = new ArrayList<>();
// CPU使用率
SeriesConfig cpuConfig = new SeriesConfig();
cpuConfig.setName("CPU使用率");
cpuConfig.setType("gauge");
cpuConfig.setData(Arrays.asList(75));
configs.add(cpuConfig);
// 内存使用率
SeriesConfig memoryConfig = new SeriesConfig();
memoryConfig.setName("内存使用率");
memoryConfig.setType("gauge");
memoryConfig.setData(Arrays.asList(60));
configs.add(memoryConfig);
// 磁盘使用率
SeriesConfig diskConfig = new SeriesConfig();
diskConfig.setName("磁盘使用率");
diskConfig.setType("gauge");
diskConfig.setData(Arrays.asList(45));
configs.add(diskConfig);
ChartData chartData = ChartUtil.buildChartData(title, new ArrayList<>(), configs);
return Result.success(chartData);
} catch (Exception e) {
return Result.error("获取多仪表盘数据失败");
}
}
}
前端图表组件
1.ChartComponent.vue
<template>
<div class="chart-container">
<div class="chart-header">
<h3>{{ chartTitle }}</h3>
<div class="chart-controls">
<button @click="refreshChart" class="btn-refresh">刷新数据</button>
<select v-model="selectedChartType" @change="switchChartType">
<option value="trend">折线图</option>
<option value="stats">单柱状图</option>
<!-- <option value="user-activity">用户活跃度</option>-->
<option value="task-completion">饼图</option>
<option value="sales-trend">双柱状图</option>
<option value="area-chart">面积图</option>
<option value="scatter">散点图</option>
<option value="stacked-bar">堆叠柱状图</option>
<option value="radar">雷达图</option>
<option value="gauge">仪表盘</option>
<option value="multi-gauge">多仪表盘</option>
</select>
</div>
</div>
<div ref="chartContainer" class="chart-content"></div>
<div class="data-explanation">
<h4>数据说明:</h4>
<ul>
<li><strong>数据来源:</strong>后端ChartController接口</li>
<li><strong>连接方式:</strong>前端通过axios调用/api/chart接口</li>
<li><strong>数据处理:</strong>获取JSON数据后转换为ECharts配置</li>
<li><strong>渲染方式:</strong>使用ECharts库渲染图表</li>
<li><strong>数据更新:</strong>支持实时刷新和图表类型切换</li>
<li><strong>交互功能:</strong>点击图例可切换线条显示/隐藏</li>
</ul>
</div>
</div>
</template>
<script>
import axios from 'axios'
import * as echarts from 'echarts'
export default {
name: 'ChartComponent',
data() {
return {
selectedChartType: 'trend',
chartTitle: '流程处理效率趋势图',
apiEndpoints: {
trend: '/api/chart/trend',
stats: '/api/chart/stats',
'user-activity': '/api/chart/user-activity',
'task-completion': '/api/chart/task-completion',
'sales-trend': '/api/chart/sales-trend',
'area-chart': '/api/chart/area-chart',
scatter: '/api/chart/scatter',
'stacked-bar': '/api/chart/stacked-bar',
radar: '/api/chart/radar',
gauge: '/api/chart/gauge',
'multi-gauge': '/api/chart/multi-gauge'
}
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
// 改为调用真实接口
this.loadChartData()
})
},
beforeUnmount() {
if (this.chartInstance) {
this.chartInstance.dispose()
}
},
methods: {
initChart() {
if (this.$refs.chartContainer) {
this.chartInstance = echarts.init(this.$refs.chartContainer)
// 添加窗口大小变化监听
window.addEventListener('resize', () => {
if (this.chartInstance) {
this.chartInstance.resize()
}
})
}
},
// 调用真实接口获取数据
async loadChartData() {
try {
console.log(`正在请求接口: ${this.apiEndpoints[this.selectedChartType]}`)
const response = await axios.get(this.apiEndpoints[this.selectedChartType])
// console.log('接口响应:', response.data)
if (response.data.success) {
const chartData = response.data.data
this.chartTitle = chartData.title || '图表'
console.log('接收到的图表数据:', chartData)
// 验证数据完整性
if (this.validateChartData(chartData)) {
// 构建ECharts配置
const option = this.buildEChartsOption(chartData)
console.log('ECharts配置:', option)
if (this.chartInstance) {
this.chartInstance.clear()
this.chartInstance.setOption(option, true)
console.log('图表渲染成功')
}
} else {
console.error('数据验证失败,使用默认数据')
this.loadDefaultData()
}
} else {
console.error('获取图表数据失败:', response.data.message)
this.loadDefaultData()
}
} catch (error) {
console.error('请求图表数据出错:', error)
console.log('使用默认数据作为备用')
this.loadDefaultData()
}
},
// 验证图表数据的完整性
validateChartData(chartData) {
if (!chartData) {
console.error('chartData为空')
return false
}
if (!chartData.series || !Array.isArray(chartData.series)) {
console.error('series数据无效')
return false
}
// 检查每个series是否有效
for (let i = 0; i < chartData.series.length; i++) {
const series = chartData.series[i]
if (!series || typeof series !== 'object') {
console.error(`series[${i}]为空或不是对象`)
return false
}
if (!series.name || !series.type) {
console.error(`series[${i}]缺少必需字段:`, series)
return false
}
}
return true
},
// 构建ECharts配置
buildEChartsOption(chartData) {
// console.log('原始数据字段:', Object.keys(chartData))
// console.log('series数据:', chartData.series)
const option = {
title: {
text: chartData.title || '图表',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: [],
top: 30
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: chartData.categories || []
},
yAxis: {
type: 'value'
},
series: []
}
// 处理series数据
if (chartData.series && Array.isArray(chartData.series)) {
option.series = chartData.series.map(series=> {
const seriesConfig = {
name: series.name,
type: series.type,
data: this.getSeriesData(series),
color: series.color || ''
}
// 面积图特殊处理
if (series.areaStyle) {
seriesConfig.areaStyle = {}
}
// 堆叠图特殊处理
if (series.stack) {
seriesConfig.stack = series.stack
}
return seriesConfig
})
// 设置图例数据
option.legend.data = option.series.map(s => s.name)
}
// 特殊处理饼图
if (chartData.series && chartData.series.length > 0 && chartData.series[0].type === 'pie') {
const pieData = chartData.categories.map((name, index) => ({
name: name,
value: this.getSeriesData(chartData.series[0])[index]
}))
option.series[0] = {
name: chartData.series[0].name,
type: 'pie',
radius: ['40%', '70%'], // 环形饼图
center: ['50%', '60%'], // 饼图位置
data: pieData,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
},
label: {
show: true,
fontSize: '16',
fontWeight: 'bold'
}
},
label: {
show: true,
formatter: '{b}: {c} ({d}%)', // 显示名称、数值和百分比
fontSize: 12
},
labelLine: {
show: true
},
itemStyle: {
borderRadius: 8, // 圆角
borderColor: '#fff',
borderWidth: 2
}
}
// 饼图专用tooltip配置
option.tooltip = {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)',
backgroundColor: 'rgba(0,0,0,0.8)',
borderColor: '#ccc',
borderWidth: 1,
textStyle: {
color: '#fff'
}
}
// 饼图专用legend配置
option.legend = {
orient: 'vertical',
left: 'left',
top: 'center',
data: chartData.categories,
textStyle: {
fontSize: 12
}
}
// 饼图不需要x轴和y轴
delete option.xAxis
delete option.yAxis
delete option.grid
}
// 特殊处理雷达图
if (chartData.series && chartData.series.length > 0 && chartData.series[0].type === 'radar') {
const indicators = (chartData.categories || []).map(name => ({ name, max: 100 }))
option.radar = {
indicator: indicators,
center: ['50%', '58%'],
radius: '56%',
splitNumber: 5,
shape: 'polygon',
axisName: { color: '#666', fontSize: 11, padding: [1, 3] },
axisLine: { lineStyle: { color: 'rgba(0,0,0,0.14)' } },
splitLine: { lineStyle: { color: 'rgba(0,0,0,0.10)' } },
splitArea: { areaStyle: { color: ['rgba(24,144,255,0.03)','rgba(24,144,255,0.05)'] } }
}
const colors = ['#5470C6','#91CC75','#FAC858','#EE6666','#73C0DE']
option.series = chartData.series.map((s, i) => {
const color = s.color || colors[i % colors.length]
return {
name: s.name,
type: 'radar',
symbol: 'circle',
symbolSize: 4,
lineStyle: { width: 2, color },
itemStyle: { color },
areaStyle: { color, opacity: 0.15 },
emphasis: { focus: 'series', lineStyle: { width: 3 } },
data: [{ value: this.getSeriesData(s), name: s.name }]
}
})
option.legend = {
top: 34,
left: 'center',
icon: 'circle',
itemWidth: 8,
itemHeight: 8,
itemGap: 14,
textStyle: { fontSize: 12 },
data: chartData.series.map(s => s.name)
}
option.tooltip = {
trigger: 'item',
confine: true,
formatter: (p) => {
const vals = p.value || []
return `${p.seriesName}<br/>` + (chartData.categories || [])
.map((n, i) => `${n}:${vals[i] ?? '-'}`).join('<br/>')
}
}
delete option.xAxis
delete option.yAxis
delete option.grid
}
// 特殊处理仪表盘
if (chartData.series && chartData.series.length > 0 && chartData.series[0].type === 'gauge') {
if (chartData.series.length === 1) {
// 单仪表盘
const value = this.getSeriesData(chartData.series[0])[0]
option.series = [{
name: chartData.series[0].name,
type: 'gauge',
center: ['50%', '60%'],
radius: '80%',
min: 0,
max: 100,
splitNumber: 10,
axisLine: {
lineStyle: {
width: 8,
color: [
[0.3, '#67e0e3'],
[0.7, '#37a2da'],
[1, '#fd666d']
]
}
},
pointer: {
itemStyle: {
color: 'auto'
}
},
axisTick: {
distance: -30,
splitNumber: 5,
lineStyle: {
width: 2,
color: '#999'
}
},
splitLine: {
distance: -30,
length: 30,
lineStyle: {
width: 4,
color: '#999'
}
},
axisLabel: {
color: 'auto',
distance: 40,
fontSize: 12
},
detail: {
valueAnimation: true,
formatter: '{value}%',
color: 'auto',
fontSize: 20,
offsetCenter: [0, '70%']
},
data: [{
value: value,
name: chartData.series[0].name
}]
}]
} else {
// 多仪表盘
option.series = chartData.series.map((series, index) => {
const value = this.getSeriesData(series)[0]
const centerX = 25 + (index % 2) * 50
const centerY = 30 + Math.floor(index / 2) * 60
return {
name: series.name,
type: 'gauge',
center: [centerX + '%', centerY + '%'],
radius: '30%',
min: 0,
max: 100,
splitNumber: 5,
axisLine: {
lineStyle: {
width: 4,
color: [
[0.3, '#67e0e3'],
[0.7, '#37a2da'],
[1, '#fd666d']
]
}
},
pointer: {
itemStyle: {
color: 'auto'
}
},
axisTick: {
distance: -15,
splitNumber: 5,
lineStyle: {
width: 1,
color: '#999'
}
},
splitLine: {
distance: -15,
length: 15,
lineStyle: {
width: 2,
color: '#999'
}
},
axisLabel: {
color: 'auto',
distance: 20,
fontSize: 10
},
detail: {
valueAnimation: true,
formatter: '{value}%',
color: 'auto',
fontSize: 14,
offsetCenter: [0, '60%']
},
data: [{
value: value,
name: series.name
}]
}
})
}
option.tooltip = {
trigger: 'item',
formatter: '{a} <br/>{b}: {c}%'
}
delete option.xAxis
delete option.yAxis
delete option.grid
delete option.legend
}
// 特殊处理散点图
if (chartData.series && chartData.series.length > 0 && chartData.series[0].type === 'scatter') {
// 散点图不需要x轴分类
option.xAxis.type = 'value'
delete option.xAxis.data
}
return option
},
// 获取系列数据
getSeriesData(series) {
if(series.type==='scatter' && series.scatterData){
// 散点图数据格式转化
return series.scatterData.map(point => [point[0], point[1]])
}
return series.data || []
},
// 保留默认数据作为备用
loadDefaultData() {
const option = {
title: {
text: '暂无数据',
left: 'center'
},
series: [
{
type: 'line',
data: []
}
]
}
if (this.chartInstance) {
this.chartInstance.clear()
this.chartInstance.setOption(option, true)
}
},
// 刷新图表数据
refreshChart() {
console.log('刷新图表数据')
this.loadChartData()
},
// 切换图表类型
switchChartType() {
console.log(`切换图表类型为: ${this.selectedChartType}`)
this.loadChartData()
}
}
}
</script>
<style scoped>
.chart-container {
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #eee;
}
.chart-header h3 {
margin: 0;
color: #333;
}
.chart-controls {
display: flex;
gap: 10px;
align-items: center;
}
.btn-refresh {
padding: 8px 16px;
background: #1890ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s;
}
.btn-refresh:hover {
background: #40a9ff;
}
select {
padding: 8px 12px;
border: 1px solid #d9d9d9;
border-radius: 4px;
background: white;
}
.chart-content {
width: 100%;
height: 400px;
margin-bottom: 20px;
}
.data-explanation {
background: #f5f5f5;
padding: 15px;
border-radius: 4px;
border-left: 4px solid #1890ff;
}
.data-explanation h4 {
margin: 0 0 10px 0;
color: #333;
}
.data-explanation ul {
margin: 0;
padding-left: 20px;
}
.data-explanation li {
margin-bottom: 5px;
color: #666;
line-height: 1.5;
}
</style>
2.页面引入组件
<!-- Dashboard.vue -->
<template>
<div class="dashboard">
<h1>流程管理仪表板</h1>
<!-- 图表组件 -->
<ChartComponent />
<!-- 其他仪表板内容 -->
<div class="dashboard-grid">
<div class="card">
<h3>流程概览</h3>
<p>当前运行中的流程实例数量</p>
</div>
<div class="card">
<h3>任务统计</h3>
<p>待处理任务数量</p>
</div>
</div>
</div>
</template>
<script>
import ChartComponent from '@/components/ChartComponent.vue'
export default {
name: 'Dashboard',
components: {
ChartComponent
}
}
</script>
<style scoped>
.dashboard {
padding: 20px;
}
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-top: 20px;
}
.card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
</style>
页面效果图
![image]()
![image]()
![image]()
![image]()
![image]()
![image]()
![image]()
![image]()
![image]()
![image]()