浏览代码

配置折线图

kagg886 1 月之前
父节点
当前提交
f51f7d91f5
共有 2 个文件被更改,包括 89 次插入137 次删除
  1. 7 2
      src/components/markdown/plugins/impl/VueCharts.vue
  2. 82 135
      src/components/markdown/plugins/impl/VueStructData.vue

+ 7 - 2
src/components/markdown/plugins/impl/VueCharts.vue

@@ -3,7 +3,7 @@ import { onMounted, onUnmounted, ref, watch } from 'vue'
 import * as echarts from 'echarts'
 
 type Props = {
-	data: string
+	data: string | echarts.EChartsOption
 }
 
 const prop = defineProps<Props>()
@@ -20,7 +20,12 @@ const showOrigin = ref(false)
 const setOption = () => {
 	let data: echarts.EChartsOption
 	try {
-		data = JSON.parse(decodeURIComponent(prop.data))
+		if (typeof prop.data === 'object') {
+			data = prop.data
+		} else {
+			data = JSON.parse(decodeURIComponent(prop.data))
+		}
+
 	} catch (e) {
 		console.error(e)
 		return

+ 82 - 135
src/components/markdown/plugins/impl/VueStructData.vue

@@ -1,11 +1,12 @@
 <script setup lang="ts">
-import { ref, onMounted, watch, computed, nextTick } from 'vue'
+import { nextTick, onMounted, ref, watch } from 'vue'
 import { ElMessage } from 'element-plus'
-import { Download, Grid, TrendCharts, DataLine } from '@element-plus/icons-vue'
+import { DataLine, Download, Grid, TrendCharts } from '@element-plus/icons-vue'
 import assist from '/@/api/assist'
 import { useLoading } from '/@/utils/loading-util'
 import download from 'downloadjs'
 import VueCharts from './VueCharts.vue'
+import { EChartsOption } from 'echarts'
 
 const props = defineProps<{
 	uuid: string
@@ -58,41 +59,43 @@ const display = ref<'table' | 'bar' | 'line'>('table')
 const chart = ref()
 
 // 图表配置选项
-const chartOptions = ref('')
+const chartOptions = ref<EChartsOption>()
 
 // 生成柱状图配置 - 统计每列数据的分布情况
-const generateBarChartOptions = () => {
-	if (!data.value || !data.value.fields || !data.value.data) return ''
+const generateBarChartOptions: () => EChartsOption | undefined = () => {
+	if (!data.value || !data.value.fields || !data.value.data) return undefined
 
 	// 统计每个字段的数据分布
 	const fieldStats: { [field: string]: { [value: string]: number } } = {}
 
 	// 初始化统计对象
-	data.value.fields.forEach(field => {
+	data.value.fields.forEach((field) => {
 		fieldStats[field] = {}
 	})
 
 	// 统计每个字段中每个值的出现次数
-	data.value.data.forEach(row => {
-		data.value!.fields.forEach(field => {
+	data.value.data.forEach((row) => {
+		data.value!.fields.forEach((field) => {
 			const value = row[field] || '空值'
 			fieldStats[field][value] = (fieldStats[field][value] || 0) + 1
 		})
 	})
 
 	// 准备图表数据
-	const xAxisData = data.value.fields // 横坐标是表头(字段名)
+	const xAxisData = data.value.fields.map((field) => {
+		return field.length > 6 ? field.slice(0, 3) + '...' : field
+	}) // 横坐标是表头(字段名)
 	const seriesData: { [value: string]: number[] } = {}
 
 	// 收集所有可能的值
 	const allValues = new Set<string>()
-	Object.values(fieldStats).forEach(fieldStat => {
-		Object.keys(fieldStat).forEach(value => allValues.add(value))
+	Object.values(fieldStats).forEach((fieldStat) => {
+		Object.keys(fieldStat).forEach((value) => allValues.add(value))
 	})
 
 	// 为每个值创建一个系列
-	Array.from(allValues).forEach(value => {
-		seriesData[value] = data.value!.fields.map(field => fieldStats[field][value] || 0)
+	Array.from(allValues).forEach((value) => {
+		seriesData[value] = data.value!.fields.map((field) => fieldStats[field][value] || 0)
 	})
 
 	// 生成系列配置
@@ -100,149 +103,97 @@ const generateBarChartOptions = () => {
 		name: value,
 		type: 'bar',
 		data: counts,
-		stack: 'total' // 使用堆叠柱状图更好地展示分布
+		stack: 'total', // 使用堆叠柱状图更好地展示分布,
 	}))
 
-	const option = {
-		title: {
-			text: '数据分布统计',
-			left: 'center'
-		},
-		tooltip: {
-			trigger: 'axis',
-			axisPointer: {
-				type: 'shadow'
-			},
-			backgroundColor: 'rgba(50, 50, 50, 0.9)',
-			borderColor: '#ccc',
-			borderWidth: 1,
-			textStyle: {
-				color: '#fff',
-				fontSize: 12
-			},
-			padding: [8, 12],
-			extraCssText: 'max-width: 300px; word-wrap: break-word;',
-			formatter: (params: any) => {
-				if (!params || params.length === 0) return ''
-				let result = `<div style="font-weight: bold; margin-bottom: 4px;">${params[0].axisValue}</div>`
-				params.forEach((param: any) => {
-					if (param.value > 0) { // 只显示有数据的项
-						result += `<div style="margin: 2px 0;">${param.marker}<span style="margin-left: 4px;">${param.seriesName}: ${param.value}</span></div>`
-					}
-				})
-				return result
-			}
-		},
-		legend: {
-			data: Object.keys(seriesData),
-			top: 30,
-			type: 'scroll' // 如果图例太多,使用滚动
-		},
-		grid: {
-			left: '3%',
-			right: '4%',
-			bottom: '3%',
-			containLabel: true
-		},
+	return {
 		xAxis: {
 			type: 'category',
 			data: xAxisData,
-			axisLabel: {
-				rotate: 45 // 如果字段名太长,旋转显示
-			}
+		},
+		legend: {
+			show: true,       // 显示图例
+			type: 'scroll',   // 如果图例太多,可以加滚动
+			top: 'top',       // 位置:上方
+		},
+		tooltip: {
+			show: true,
+			trigger: 'item',
 		},
 		yAxis: {
 			type: 'value',
-			name: '出现次数'
+			name: '出现次数',
 		},
-		series: series
-	}
-
-	return encodeURIComponent(JSON.stringify(option))
+		series: series,
+	} as EChartsOption
 }
 
-// 生成折线图配置 - 展示数据趋势(按行索引
-const generateLineChartOptions = () => {
-	if (!data.value || !data.value.fields || !data.value.data) return ''
+// 生成折线图配置 - 统计每列数据的分布情况(与柱状图算法同步)
+const generateLineChartOptions: () => EChartsOption | undefined = () => {
+	if (!data.value || !data.value.fields || !data.value.data) return undefined
 
-	// 尝试找到数值型字段
-	const numericFields = data.value.fields.filter(field => {
-		return data.value!.data.some(row => {
-			const value = row[field]
-			return !isNaN(parseFloat(value)) && isFinite(parseFloat(value))
-		})
+	// 统计每个字段的数据分布
+	const fieldStats: { [field: string]: { [value: string]: number } } = {}
+
+	// 初始化统计对象
+	data.value.fields.forEach((field) => {
+		fieldStats[field] = {}
 	})
 
-	// 如果没有数值型字段,则显示每个字段的数据长度趋势
-	const fieldsToShow = numericFields.length > 0 ? numericFields : data.value.fields
+	// 统计每个字段中每个值的出现次数
+	data.value.data.forEach((row) => {
+		data.value!.fields.forEach((field) => {
+			const value = row[field] || '空值'
+			fieldStats[field][value] = (fieldStats[field][value] || 0) + 1
+		})
+	})
 
-	// X轴数据:行索引或者第一列数据
-	const xAxisData = data.value.data.map((_, index) => `第${index + 1}行`)
+	// 准备图表数据
+	const xAxisData = data.value.fields.map((field) => {
+		return field.length > 6 ? field.slice(0, 3) + '...' : field
+	}) // 横坐标是表头(字段名)
+	const seriesData: { [value: string]: number[] } = {}
 
-	// 生成系列数据
-	const series = fieldsToShow.map(field => {
-		const seriesData = data.value!.data.map(row => {
-			const value = row[field]
-			// 如果是数值型字段,直接使用数值
-			if (numericFields.includes(field)) {
-				return parseFloat(value) || 0
-			}
-			// 否则使用字符串长度
-			return (value || '').toString().length
-		})
+	// 收集所有可能的值
+	const allValues = new Set<string>()
+	Object.values(fieldStats).forEach((fieldStat) => {
+		Object.keys(fieldStat).forEach((value) => allValues.add(value))
+	})
 
-		return {
-			name: field,
-			type: 'line',
-			smooth: true,
-			data: seriesData,
-			connectNulls: false
-		}
+	// 为每个值创建一个系列
+	Array.from(allValues).forEach((value) => {
+		seriesData[value] = data.value!.fields.map((field) => fieldStats[field][value] || 0)
 	})
 
-	const option = {
-		title: {
-			text: numericFields.length > 0 ? '数值趋势图' : '字段长度趋势图',
-			left: 'center'
-		},
-		tooltip: {
-			trigger: 'axis',
-			formatter: (params: any) => {
-				let result = `${params[0].axisValue}<br/>`
-				params.forEach((param: any) => {
-					const suffix = numericFields.includes(param.seriesName) ? '' : ' (字符长度)'
-					result += `${param.marker}${param.seriesName}: ${param.value}${suffix}<br/>`
-				})
-				return result
-			}
-		},
-		legend: {
-			data: fieldsToShow,
-			top: 30,
-			type: 'scroll'
-		},
-		grid: {
-			left: '3%',
-			right: '4%',
-			bottom: '3%',
-			containLabel: true
-		},
+	// 生成系列配置
+	const series = Object.entries(seriesData).map(([value, counts]) => ({
+		name: value,
+		type: 'line',
+		smooth: true,
+		data: counts,
+		connectNulls: false,
+	}))
+
+	return {
 		xAxis: {
 			type: 'category',
-			boundaryGap: false,
 			data: xAxisData,
-			axisLabel: {
-				rotate: 45
-			}
+		},
+		legend: {
+			show: true,       // 显示图例
+			type: 'scroll',   // 如果图例太多,可以加滚动
+			top: 'top',       // 位置:上方
+		},
+		tooltip: {
+			show: true,
+			trigger: 'axis',
 		},
 		yAxis: {
 			type: 'value',
-			name: numericFields.length > 0 ? '数值' : '字符长度'
+			name: '出现次数',
 		},
-		series: series
-	}
-
-	return encodeURIComponent(JSON.stringify(option))
+		series: series,
+	} as EChartsOption
 }
 
 // 监听显示模式变化,生成对应的图表配置
@@ -335,11 +286,7 @@ watch(display, async (newVal) => {
 
 				<!-- 图表视图 -->
 				<div v-else class="chart-wrapper">
-					<VueCharts
-						ref="chart"
-						:data="chartOptions"
-						v-if="chartOptions"
-					/>
+					<VueCharts ref="chart" :data="chartOptions" v-if="chartOptions" />
 				</div>
 			</div>