buliangMap/src/views/compositeIndex.vue
2025-06-24 18:58:59 +08:00

1340 lines
40 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="backgroundbox">
<!-- 关闭按钮 -->
<router-link to="/navigation" style="z-index: 100;" class="card1">
<div class="card-icon1"></div>
</router-link>
<header>
<h1>上海市食品安全风险预警</h1>
<div v-show="suducepingShow" class="color-white flex justify-center items-center" style="color: #81E7ED;">
数据加载耗时:{{ suducepingValue }}</div>
<!-- <div class="showTime"><span>2023-12-23 15:35:43</span></div> -->
</header>
<div class="left">
<div class='box1'>
<div class="boxall">
<div class="alltitle">{{ currentMonth }} 后各区各维度分构成比</div>
<div ref="echart2" class="navboxall" style=" height: 100%; width: 100%;"></div>
</div>
<div class="boxall-footer"></div>
</div>
<div class='box2'>
<div class="boxall">
<div class="alltitle">{{ currentMonth }} 后各区食品安全综合指数</div>
<div ref="echart1" class="navboxall" style=" height: 100%; width: 100%;"></div>
</div>
<div class="boxall-footer"></div>
</div>
</div>
<div class="right">
<div class='box3'>
<div class="boxall" style="padding: 0px;">
<!-- <div class="alltitle">标题样式333</div> -->
<div id='echart3box' ref="echart3" class="navboxall" style=" height: 100%;
width: 100%;
position: absolute;">
</div>
</div>
<div class="boxall-footer"></div>
</div>
<div class='box4'>
<div class="boxall">
<div class="alltitle">食品安全综合指数重点指标风险贡献构成比
<el-select class="selectBox" v-model="selectBoxValue" placeholder="Select" size="small"
@change="handleSelectChange">
<el-option class="optionBox" v-for="item in areaNameList" :key="item" :label="item" :value="item" />
</el-select>
</div>
<!-- <div class="selectBox"> -->
<!-- </div> -->
<div ref="echart4" class="navboxall" style=" height: 90%; width: 100%;"></div>
</div>
<div class="boxall-footer"></div>
</div>
</div>
<div class="shanghaimap">
<div ref="echartMap" class="w-full h-screen"></div>
</div>
</div>
</template>
<style scoped>
@import '../assets/css/compositelndex.css';
</style>
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount, reactive, computed } from "vue";
import shanghaishiJson from "../mapjson/shanghaishi.json";
import shanghaishiJieDaoJson from "../mapjson/shangHaiJieDao.json";
import echarts3json from "../mapjson/echarts3.json";
import type { GeoJSONSourceInput } from "echarts/types/src/coord/geo/geoTypes";
import * as echarts from "echarts";
import $ from 'jquery';
import { getcompositeIndexData, getAllData, getRelationshipNetwork, getPredictedScores, getTertiaryImportance } from "../api/compositeIndex";
import { index, link } from "d3";
import { da } from "element-plus/es/locale";
// import '../css/compositelndex.css';
const selectBoxValue = ref('浦东新区')
interface AllScoreVO {
primary_name: string;
now_score: number;
yuce_score: number;
month_percentage_change: number;
}
interface AreaYujingScore {
area: string;
all_score_vo: AllScoreVO;
xzcf_vo: AllScoreVO;
xxzs_vo: AllScoreVO;
tsjb_vo: AllScoreVO;
zfjc_vo: AllScoreVO;
cckh_vo: AllScoreVO;
cjjc_vo: AllScoreVO;
zhx_vo: AllScoreVO;
}
interface AreaYujingScoreList {
year: number;
month: number;
area_yujing_score_list: AreaYujingScore[];
}
// 定义 Field 类型
type Field = typeof weiduMapping[number]['field'];
interface echart2AreaYujingScore {
name: string;
xzcf: number;
xxzs: number;
tsjb: number;
zfjc: number;
cckh: number;
cjjc: number;
zhx: number;
}
interface DataTertiaryImportance {
month: number;
year: number;
area_tertiary_importance_list: AreaTertiaryImportance[];
}
interface AreaTertiaryImportance {
area_name: string;
primary_importance_groups_list: PrimaryImportanceGroup[];
}
interface PrimaryImportanceGroup {
primary_name: string;
tertiary_importance_list: TertiaryImportance[];
}
interface TertiaryImportance {
tertiary_name: string;
importance_percentage: number;
importance: number;
}
interface ZSYujingAreaMapVO {
area: string;
all_score_vo: YujingItemVo
xzcf_vo: YujingItemVo;
xxzs_vo: YujingItemVo;
tsjb_vo: YujingItemVo;
zfjc_vo: YujingItemVo;
cckh_vo: YujingItemVo;
cjjc_vo: YujingItemVo;
zhx_vo: YujingItemVo;
}
interface YujingItemVo {
primary_name: string;
now_score: number;
last_score: number;
yuce_score: number;
month_percentage_change: number;
}
const echartMap = ref(null);
let chartMap: echarts.ECharts | null = null;
var optionXyMap01: EChartsOption;
const echart1 = ref(null);
let chart1: echarts.ECharts | null = null;
var option1: EChartsOption;
const echart2 = ref(null);
let chart2: echarts.ECharts | null = null;
var option2: EChartsOption;
const echart3 = ref(null);
let chart3: echarts.ECharts | null = null;
var option3: EChartsOption;
const echart4 = ref(null);
let chart4: echarts.ECharts | null = null;
var option4: EChartsOption;
// const chart = ref();
const geoCoordMap: Record<string, [number, number]> = {
黄浦区: [121.480317, 31.213771],
徐汇区: [121.43752, 31.159973],
长宁区: [121.3822, 31.203123],
静安区: [121.457224, 31.255003],
普陀区: [121.399499, 31.241701],
虹口区: [121.484832, 31.26997],
杨浦区: [121.523797, 31.290755],
闵行区: [121.429972, 31.051658],
宝山区: [121.409934, 31.396896],
嘉定区: [121.250333, 31.350524],
浦东新区: [121.7395171, 31.11317039],
金山区: [121.220736, 30.814697],
松江区: [121.223543, 31.03047],
青浦区: [121.113021, 31.151209],
奉贤区: [121.568472, 30.902345],
崇明区: [121.597516, 31.596946],
};
const month = ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",];
// const dateStr: String[] = [];
let dateStr: string[] = ["2023-01-01", "2023-02-01", "2023-03-01"]; // 示例数据
// 定义一个响应式的字符串数组
const months = ref<string[]>(['一个月', '二个月', '三个月', '四个月', '五个月', '六个月',
'七个月', '八个月', '九个月', '十个月', '十一个月', '十二个月']);
// 假设currentIndex是由某个事件触发后设置的值
const currentIndex = ref<number>(0); // 初始值假设为0即当前月份
// const currentMonth = ref('六个月')
const currentMonth = ref('')
const mapData = reactive<any>([[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]);
const barData: number[][] = [];
const legend = [
"rgb(245, 166, 10)",
"rgb(197, 190, 36)",
"rgb(81, 184, 198)",
// "rgba(5, 27, 74)",
"rgba(30, 80, 179)",
];
const legend2 = [
["rgba(245, 166, 10, 1)", "rgba(245, 166, 10, 0.2)"],
["rgba(197, 190, 36, 1)", "rgba(197, 190, 36, 0.2)"],
["rgba(81, 184, 198, 1)", "rgba(81, 184, 198, 0.2)"],
["rgba(30, 80, 179, 1)", "rgba(30, 80, 179, 0.2)"],
// ["rgba(147, 235, 248, 0)", "rgba(147, 235, 248, 0.2)"],
];
//角标配置
let legendValue: number[][] = [[90, 92, 93]];
const scatterLegend = [
"rgb(254, 69, 0)",
"rgb(255, 200, 9)",
"rgb(143, 237, 143)",
"rgb(133, 204, 253)",
// "rgb(133, 204, 253)",
// "rgb(133, 204, 253)",
// "rgb(133, 204, 253)",
// "rgb(133, 204, 253)",
];
//接口速度测评
const suducepingShow = ref(true);
//接口速度测评单位:秒
const suducepingValue = ref(0);
const startValue = ref(0);
let scatterLegendValue: number[] = [-3, -1, 0];
for (let i = 0; i < mapData.length; i++) {
barData.push(mapData[i].map((data: any) => data.value));
// console.log(barData);
};
let yuceYearScore: { area_yujing_score_list: any; month: any; year: any; all_score_interval: any; month_percentage_change_interval: any }[] = [];
onMounted(() => {
chartMap = echarts.init(echartMap.value!);
chart1 = echarts.init(echart1.value!);
chart2 = echarts.init(echart2.value!);
chart3 = echarts.init(echart3.value!);
chart4 = echarts.init(echart4.value!);
areaNameList.forEach((area, index) => {
areaIndexMap.set(area, index);
});
startValue.value = performance.now(); // 记录开始时间
getPredictedScores().then((res) => {
const endCheckMonitoringValue = performance.now(); // 记录结束时间
const executionTime = endCheckMonitoringValue - startValue.value; // 计算执行时间
// 假设 executionTime 是毫秒数,将其转换为秒
const executionTimeInSeconds = parseFloat((executionTime / 1000).toFixed(2));
if (executionTimeInSeconds > suducepingValue.value) {
suducepingValue.value = executionTimeInSeconds;
}
dateStr = [];
yuceYearScore = res.data;
yuceYearScore.forEach((nowYuceYearScore: any, index: number) => {
// const nowYuceYearScore = yuceYearScore[index]
legendValue[index] = nowYuceYearScore.all_score_interval;
scatterLegendValue = nowYuceYearScore.month_percentage_change_interval;
const dataString = yuceYearScore[index].year + '年' + yuceYearScore[index].month + '月'
dateStr.push(dataString);
// debugger;
nowYuceYearScore.area_yujing_score_list.map((area_yujing_score_list: ZSYujingAreaMapVO) => {
return mapData[index].push({
month: nowYuceYearScore.month,
year: nowYuceYearScore.year,
name: area_yujing_score_list.area,
value: area_yujing_score_list.all_score_vo.now_score,
yuce_score: area_yujing_score_list.all_score_vo.yuce_score,
all_score_vo: area_yujing_score_list.all_score_vo,
xzcf_vo: area_yujing_score_list.xzcf_vo,
xxzs_vo: area_yujing_score_list.xxzs_vo,
tsjb_vo: area_yujing_score_list.tsjb_vo,
zfjc_vo: area_yujing_score_list.zfjc_vo,
cckh_vo: area_yujing_score_list.cckh_vo,
cjjc_vo: area_yujing_score_list.cjjc_vo,
zhx_vo: area_yujing_score_list.zhx_vo,
});
})
});
initChart();
initChart1();
initChart2();
})
initChart3();
initChart4();
// 监听窗口大小变化,以适应图表
// window.addEventListener('resize', resizeChart);
});
const initChart = () => {
optionXyMap01 = {
// 时间滚动轴
timeline: {
// data: month,
data: dateStr,
axisType: "category",
autoPlay: true,
currentIndex: 0,
playInterval: 3000,
left: "10%",
right: "10%",
bottom: "3%",
width: "80%",
// label: {
// textStyle: { color: "#ddd" }, // 修改这里
// emphasis: { textStyle: { color: "#fff" } },
// },
symbolSize: 10,
lineStyle: { color: "#555" },
checkpointStyle: { borderColor: "#777", borderWidth: 2 },
controlStyle: {
showNextBtn: true,
showPrevBtn: true,
color: '#666',
borderColor: '#666',
// normal: {
// color: '#666',
// borderColor: '#666'
// },
// emphasis: {
// color: '#aaa',
// borderColor: '#aaa'
// },
// showNextBtn: true,
// showPrevBtn: true,
// normal: { color: "#666", borderColor: "#666" },
// emphasis: { color: "#aaa", borderColor: "#aaa" },
},
},
baseOption: {
animation: true,
animationDuration: 1000,
animationEasing: "cubicInOut",
animationDurationUpdate: 1000,
animationEasingUpdate: "cubicInOut",
grid: {
right: "1%",
top: "15%",
bottom: "10%",
width: "20%",
},
// toolbox: {
// show: true,
// feature: {
// restore: { show: true, title: "重置地图大小" },
// saveAsImage: { show: true, title: "保存为图片" },
// },
// },
// 四个区间
visualMap: [
{
id: 'vm1',
type: "piecewise",
show: true,
splitNumber: 4,
pieces: [
{ gte: legendValue[currentIndex.value][2], color: legend[3] },
{ lt: legendValue[currentIndex.value][2], gte: legendValue[currentIndex.value][1], color: legend[2] },
{ lt: legendValue[currentIndex.value][1], gte: legendValue[currentIndex.value][0], color: legend[1] },
{ lt: legendValue[currentIndex.value][0], color: legend[0] },
],
selectedMode: false,
// inRange: ['red','blue'],
textStyle: {
color: "#fff",
},
//定位区间
left: '60%', // 距离容器左侧 10%
top: '13%', // 垂直居中
},
{
id: 'vm2',
type: "piecewise",
itemWidth: 15,
itemHeight: 19,
show: true, // 不显示视觉映射控件
splitNumber: 4,
// itemSymbol: 'circle', // 强制设置为圆形
itemSymbol: 'path://M760.516923 78.769231l-7.483077-5.12-3.544615-3.544616a89.009231 89.009231 0 0 0-10.24-6.301538l-5.513846-3.544615a435.593846 435.593846 0 0 0-86.252308-39.384616h-4.332308l-27.175384-7.876923h-4.332308l-29.932308-7.483077h-1.969231c-10.24 0-21.267692-2.756923-31.901538-3.544615h-53.169231L477.735385 0H473.796923l-24.418461 4.726154c-14.572308 1.969231-27.569231 4.726154-39.384616 7.483077h-5.513846A407.630769 407.630769 0 0 0 351.704615 31.113846a426.929231 426.929231 0 0 0-105.550769 60.652308l-4.332308 3.150769a433.230769 433.230769 0 0 0-58.28923 617.550769c2.756923 3.150769 27.569231 29.538462 42.535384 42.929231l255.212308 255.212308a41.747692 41.747692 0 0 0 59.076923 0l255.212308-255.212308c14.966154-13.390769 39.384615-39.384615 42.535384-42.929231a433.230769 433.230769 0 0 0-78.76923-634.092307z',
pieces: [
{ gte: scatterLegendValue[2], color: scatterLegend[3] },
{ lt: scatterLegendValue[2], gte: scatterLegendValue[1], color: scatterLegend[2] },
{ lt: scatterLegendValue[1], gte: scatterLegendValue[0], color: scatterLegend[1] },
{ lt: scatterLegendValue[0], color: scatterLegend[0] }
],
selectedMode: false,
// inRange: ['red','blue'],
textStyle: {
color: "#fff",
},
//定位区间
left: '55%', // 距离容器左侧 10%
top: '12.9%', // 垂直居中
itemGap: 5, // 新增图例项间距
inRange: {
// symbolSize: [30, 10]
},
}
],
geo: {
show: true,
map: "shanghaishi",
roam: false,
left: "32%",
zoom: 0.9,
label: {
//@ts-ignore
emphasis: { show: false }
},
// label: {
// show: false,
// },
// label: {
// normal: { show: false, },
// emphasis: { show: false },
// },
select: {
label: {
show: false,
},
itemStyle: {
color: "",
},
},
itemStyle: {
borderColor: "rgba(147, 235, 248, 1)",
borderWidth: 1,
shadowColor: "rgba(128, 217, 248, 1)",
shadowOffsetX: -2,
shadowOffsetY: 2,
shadowBlur: 10,
// normal: {
// borderColor: "rgba(147, 235, 248, 1)",
// borderWidth: 1,
// shadowColor: "rgba(128, 217, 248, 1)",
// shadowOffsetX: -2,
// shadowOffsetY: 2,
// shadowBlur: 10,
// },
emphasis: {
areaColor: "#389BB7",
borderWidth: 0,
},
},
},
},
// options: month.map((_, n) => ({
options: dateStr.map((_, n) => ({
backgroundColor: "#051b4a",
graphic: {
type: 'image',
id: 'backgroundImage',
style: {
image: 'src/image/1731056619440.jpg', // 替换为你的图片路径
width: '100%', // 或者设置为具体的宽度
height: '100%', // 或者设置为具体的高度
},
z: -1 // 确保背景图片在最底层
},
tooltip: {
show: true,
trigger: "item",
formatter: (params: any) => {
// console.log(params);
if (params.componentType === "series") {
return getTooltipContent(params);
} else if (params.componentType === "timeline") {
return params.name;
}
// return params.name + ": " + params.value; // 根据实际的数据格式调整formatter
},
},
series: [
{
type: "map",
map: "shanghaishi",
geoIndex: 0,
// aspectScale: 0.75, //设置地图长宽比
showLegendSymbol: false,
label: {
normal: { show: false },
emphasis: { show: false, textStyle: { color: "#fff" } },
},
roam: true,
itemStyle: {
normal: { areaColor: "#031525", borderColor: "#FFFFFF" },
emphasis: { areaColor: "#2B91B7" },
},
animation: false,
data: convertMapData(mapData[n], n),
},
// 定义一个 ECharts 系列类型为散点图scatter用于在地图上展示散点数据
{
// 指定图表类型为散点图
type: "scatter",
// 指定使用地理坐标系,意味着散点将基于地图进行定位
coordinateSystem: "geo",
// 新增图例配置
legendHoverLink: true,
// 配置散点的标签
label: {
// 配置标签的常规状态
normal: {
// 显示标签
show: true,
// 标签的格式化字符串,{b} 表示数据项的名称
formatter: "{b}",
// 标签的位置,设置在散点的底部
position: "bottom",
},
},
// 涟漪特效配置,此处被注释掉了,若启用可实现点击散点时的涟漪动画效果
// rippleEffect: { period: 15, scale: 4, brushType: "fill" },
// 散点的图标形状,设置为图钉形状
symbol: "pin",
// 散点图标的大小
symbolSize: 30,
// 启用鼠标悬停时的动画效果
hoverAnimation: true,
// 关联视觉映射
visualMapId: 'vm2', // 关联第二个visualMap组件
// 散点图的数据,通过 converScatterData 函数将 mapData 数组中的第 n 项数据转换为适合散点图展示的格式
data: converScatterData(mapData[n]),
},
],
})) as echarts.EChartsOption[],
};
chartMap = echarts.init(echartMap.value!);
echarts.registerMap("shanghaishi", shanghaishiJson as GeoJSONSourceInput);
chartMap.setOption(optionXyMap01);
// onTimelineChanged(chartMap)
// 绑定时间轴变更事件
chartMap.on('timelinechanged', handleTimelineChange);
};
const handleTimelineChange = (event: any) => {
currentIndex.value = event.currentIndex;
currentMonth.value = yuceYearScore[currentIndex.value].year + '年' + yuceYearScore[currentIndex.value].month + '月'
// currentMonth.value = months.value[currentIndex.value];
// 更新第二个图表的数据
chart1?.setOption(updateEchart1Option(currentIndex.value));
chart2?.setOption(updateEchart2Option(currentIndex.value));
chart4?.setOption(updateEchart4Option(currentIndex.value));
};
// 创建一个映射表,用于排序
const areaIndexMap = new Map<string, number>();
const areaNameList = [
"上海市",
"浦东新区",
"闵行区",
"宝山区",
"松江区",
"嘉定区",
"杨浦区",
"普陀区",
"青浦区",
"奉贤区",
"徐汇区",
"静安区",
"金山区",
"虹口区",
"崇明区",
"长宁区",
"黄浦区"]
// 初始化方格
const initChart1 = () => {
const option = updateEchart1Option(0);
if (echart1.value) {
var chart1 = echarts.init(echart1.value);
chart1.setOption(option);
}
}
const updateEchart1Option = (index: number) => {
// 按指定顺序对一月排序
const sortedData = yuceYearScore[index].area_yujing_score_list.sort((a: AreaYujingScore, b: AreaYujingScore) => {
return areaIndexMap.get(a.area)! - areaIndexMap.get(b.area)!;
});
let nowScores = null;
if (index < 5) {
// 提取 now_score 成绩
nowScores = sortedData.map((item: AreaYujingScore) => item.all_score_vo.now_score);
}
// 提取 一月 成绩
const yuceYiScores = sortedData.map((item: AreaYujingScore) => item.all_score_vo.yuce_score);
// 方格1配置
const option = {
tooltip: {
trigger: 'axis',
textStyle: {
fontSize: 10
},
axisPointer: {
lineStyle: {
color: '#57617B'
}
},
extraCssText: 'max-width: 200px; max-height: 150px; overflow-y: auto;'
},
legend: {
data: ['真实分', currentMonth.value],
top: '0',
textStyle: {
fontSize: 10,
color: '#fff'
},
itemGap: 20,
},
grid: {
left: '5',
right: '10',
top: '10',
bottom: '20',
containLabel: true
},
xAxis: [
{
type: 'category',
boundaryGap: false,
axisLabel: {
interval: 0, // 显示所有标签
rotate: 45, // 旋转标签以避免拥挤
show: true,
textStyle: {
fontSize: 10,
color: 'rgba(255,255,255,.6)'
}
},
axisLine: {
lineStyle: {
color: 'rgba(255,255,255,.1)'
}
},
data: areaNameList
}
],
yAxis: [
{
type: 'value',
max: 100, // 设定折线图变化区间
min: 70,
axisLabel: {
show: true,
textStyle: {
color: 'rgba(255,255,255,.6)'
}
},
axisLine: {
lineStyle: {
color: 'rgba(255,255,255,.1)'
}
},
splitLine: {
lineStyle: {
color: 'rgba(255,255,255,.1)'
}
}
}
],
series: [
{
name: '真实分',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
width: 2
},
itemStyle: {
color: '#cdba00',
borderColor: 'rgba(137,189,2,0.27)',
borderWidth: 12
},
data: nowScores
},
{
name: currentMonth.value,
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
width: 2
},
itemStyle: {
color: '#d87c7c',
borderColor: 'rgba(0,136,212,0.2)',
borderWidth: 12
},
data: yuceYiScores
}
]
};
return option
}
interface GraphNode {
symbolSize: number;
label?: {
show?: boolean;
};
}
type EChartsOption = echarts.EChartsOption;
const initChart2 = () => {
const option = updateEchart2Option(0);
chart2 = echarts.init(echart2.value!);
if (echart2.value) {
var chart2 = echarts.init(echart2.value);
chart2.setOption(option);
}
}
const updateEchart2Option = (index: number) => {
const allData: echart2AreaYujingScore[] = yuceYearScore[index].area_yujing_score_list.map((yujingCountyData: AreaYujingScore) => {
return {
name: yujingCountyData.area,
xzcf: yujingCountyData.xzcf_vo.yuce_score,
xxzs: yujingCountyData.xxzs_vo.yuce_score,
tsjb: yujingCountyData.tsjb_vo.yuce_score,
zfjc: yujingCountyData.zfjc_vo.yuce_score,
cckh: yujingCountyData.cckh_vo.yuce_score,
cjjc: yujingCountyData.cjjc_vo.yuce_score,
zhx: yujingCountyData.zhx_vo.yuce_score,
}
})
//计算各区总分
// const countyScore: number[] = [];
const countyScoreAdd = reactive<{ [key: string]: number }>({});
// const countyScoreAdd
allData.map((countyData: echart2AreaYujingScore) => {
countyScoreAdd[countyData.name] = 0;
weiduMapping.map(({ field }) => {
countyScoreAdd[countyData.name] = countyScoreAdd[countyData.name] + countyData[field as Field];
})
})
option2.series = weiduMapping.map(({ name, color, field }, sid) => {
return {
color: color,
name: name,
type: 'bar',
stack: 'total',
barWidth: '90%',
data: allData.map(countyData => {
const result = countyData[field as Field] / countyScoreAdd[countyData.name] * 100;
return parseFloat(result.toFixed(2));
}),
};
});
return option2
}
const rawData = [
[150, 212, 201, 154, 190, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 410],
[150, 212, 201, 154, 190, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 410],
[150, 212, 201, 154, 190, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 410],
[150, 212, 201, 154, 190, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 410],
[150, 212, 201, 154, 190, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 410],
[150, 212, 201, 154, 190, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 410],
[150, 212, 201, 154, 190, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 410]
];
const totalData: number[] = [];
for (let i = 0; i < rawData[0].length; ++i) {
let sum = 0;
for (let j = 0; j < rawData.length; ++j) {
sum += rawData[j][i];
}
totalData.push(sum);
}
const weiduMapping = [
{ name: '行政处罚', color: '#4b67e5', field: 'xzcf' as const },
{ name: '信息追溯', color: '#5dc7e3', field: 'xxzs' as const },
{ name: '投诉举报', color: '#59c78d', field: 'tsjb' as const },
{ name: '执法检查', color: '#98c998', field: 'zfjc' as const },
{ name: '抽查考核', color: '#d7b57f', field: 'cckh' as const },
{ name: '抽检监测', color: '#9264dd', field: 'cjjc' as const },
{ name: '综合性', color: '#c13b5d', field: 'zhx' as const },
// 可以继续添加其他类型
];
const series: echarts.BarSeriesOption[] = [];
option2 = {
legend: {
top: '0',
// orient: 'vertical', // 设置图例为垂直布局
align: 'left', // 对齐方式,可选值有 'left', 'right'
itemWidth: 10, // 图例项的宽度
itemGap: 5, // 图例项之间的间距
// 可以设置图例的最大高度,超过后滚动显示
height: 70, // 图例的最大高度
// 如果需要固定宽度,可以设置
// width: 200 // 图例的最大宽度
textStyle: {
color: '#fff',
fontSize: 10,
},
},
tooltip: {
trigger: 'axis',
textStyle: {
fontSize: 10
},
axisPointer: {
lineStyle: {
color: '#57617B'
}
},
extraCssText: 'max-width: 200px; max-height: 200px; overflow-y: auto;'
},
grid: {
left: 30,
right: 10,
top: 30,
bottom: 50
},
yAxis: {
max: 100,
type: 'value'
},
xAxis: {
type: 'category',
boundaryGap: true,
axisLabel: {
interval: 0, // 显示所有标签
rotate: 45, // 旋转标签以避免拥挤
show: true,
color: 'rgba(255,255,255,.6)', // 直接设置文本颜色
fontSize: 10, // 直接设置字体大小
// textStyle: {
// fontSize: 10,
// color: 'rgba(255,255,255,.6)'
// }
},
axisLine: {
lineStyle: {
color: 'rgba(255,255,255,.1)'
}
},
data: areaNameList
},
series
};
const chart3data = [
{
"name": "三国演义",
// "x": 0,
// y: 0,
"symbolSize": 150,
"draggable": "true",
"value": 27
}]
chart3data.map(data => {
data.symbolSize = 10
})
const initChart3 = () => {
getRelationshipNetwork().then((res) => {
const endCheckMonitoringValue = performance.now(); // 记录结束时间
const executionTime = endCheckMonitoringValue - startValue.value; // 计算执行时间
// 假设 executionTime 是毫秒数,将其转换为秒
const executionTimeInSeconds = parseFloat((executionTime / 1000).toFixed(2));
if (executionTimeInSeconds > suducepingValue.value) {
suducepingValue.value = executionTimeInSeconds;
}
const nodes = res.data.nodes;
const links = res.data.links;
const categories = res.data.categories;
// 步骤1: 提取所有 name 属性值
// const names = nodes.map((node: { category: any; }) => node.category);
// // 步骤2: 使用 Set 去重
// const uniqueNames = new Set(names);
// // 步骤3: 将去重后的 name 值重新构造成新的对象数组
// const categories = Array.from(uniqueNames).map(name => ({ name }));
// // 使用 Set 来存储唯一的名称
// const categories = Array.from(new Set(nodes.map((node: { category: any; }) => node.category)))
// .map((name: any) => { (name) });
nodes.map((data: { draggable: boolean; value: number; symbolSize: number; }) => {
data.symbolSize = 10
data.value = 15
data.draggable = false
})
if (echart3.value) {
chart3 = echarts.init(echart3.value);
chart3.hideLoading();
option3 = {
title: {
text: '食品安全综合指数重点指标关联网络',
// subtext: 'Default layout',
top: '8',
left: '11',
textStyle: {
color: '#81E7ED',
fontSize: 15, // 设置字体大小
fontWeight: 'bold', // 设置字体加粗
}
},
tooltip: {
show: false,
trigger: 'item',
textStyle: {
fontSize: 10
},
axisPointer: {
lineStyle: {
color: '#57617B'
}
},
extraCssText: 'max-width: 200px; max-height: 150px; overflow-y: auto;'
},
legend: [
{
// selectedMode: 'single',
data: echarts3json.categories.map(function (a: { name: string }) {
return a.name;
})
}
],
animationDuration: 1500,
animationEasingUpdate: 'quinticInOut',
series: [{
name: '食品安全综合指数重点指标关联网络',
type: 'graph',
layout: 'force',
top: '13%',
force: {
repulsion: 1000,
edgeLength: 1000, // 调整边长
gravity: 10, // 吸引力,可以增加图的整体形状
layoutAnimation: true, // 启用布局动画
},
data: nodes,
links: links,
categories: categories,
focusNodeAdjacency: true,
roam: true, label: {
show: true,
position: 'top',
textBorderColor: '#fff',
color: '#fff'
},
lineStyle: {
color: 'source',
curveness: 0,
type: "solid"
}
// label: {
// normal: {
// show: true,
// position: 'top',
// textBorderColor: '#fff',
// color: '#fff'
// }
// },
// lineStyle: {
// normal: {
// color: 'source',
// curveness: 0,
// type: "solid"
// }
// }
}]
};
console.log(option3.series);
chart3.setOption(option3);
}
})
};
const groupedItems = new Map<number, DataTertiaryImportance>();
const initChart4 = () => {
if (echart4.value) {
getTertiaryImportance().then((res) => {
const endCheckMonitoringValue = performance.now(); // 记录结束时间
const executionTime = endCheckMonitoringValue - startValue.value; // 计算执行时间
// 假设 executionTime 是毫秒数,将其转换为秒
const executionTimeInSeconds = parseFloat((executionTime / 1000).toFixed(2));
if (executionTimeInSeconds > suducepingValue.value) {
suducepingValue.value = executionTimeInSeconds;
}
// res.data.group
res.data.forEach((dataTertiaryImportance: DataTertiaryImportance, index: number) => {
groupedItems.set(index, dataTertiaryImportance);
});
const option4 = updateEchart4Option(0)
chart4 = echarts.init(echart4.value!);
chart4.setOption(option4)
})
}
}
const handleSelectChange = (value: string) => {
updateEchart4Option(currentIndex.value)
};
const updateEchart4Option = (index: number) => {
const findPrimaryImportance = (Primary: String, PrimaryImportanceGroup: PrimaryImportanceGroup[]) => {
const foundImportanceGroup = PrimaryImportanceGroup?.find(item => item.primary_name === Primary)?.tertiary_importance_list!;
return foundImportanceGroup.map((TertiaryImportance) => {
return {
value: TertiaryImportance.importance_percentage,
name: TertiaryImportance.tertiary_name
}
})
}
//当前月的全部数据
const dataTertiaryImportance = groupedItems.get(index)!;
//当前区的全部数据
const foundTertiaryImportance = dataTertiaryImportance?.area_tertiary_importance_list.find(item => item.area_name === selectBoxValue.value)!;
const PrimaryImportanceGroup = foundTertiaryImportance.primary_importance_groups_list;
return option4 = {
color: ['#4b67e5', '#5dc7e3', '#59c78d', '#98c998', '#d7b57f', '#9264dd', '#c13b5d',],
tooltip: {
trigger: 'item',
textStyle: {
fontSize: 10
},
axisPointer: {
lineStyle: {
color: '#57617B'
}
},
extraCssText: 'max-width: 200px; max-height: 150px; overflow-y: auto;'
},
graphic: zhongyaoxingMapping.map(({ name, field }, index) => {
return {
type: 'text',
left: (index * 25 + 7) + '%',
top: '5%',
style: {
text: name, // 标题文本
fontSize: 12,
fontWeight: 'bold',
fill: '#ffffff' // 文字颜色
}
}
}),
series: zhongyaoxingMapping.map(({ name, field }, index) => {
return {
name: name,
type: 'pie',
radius: ['40%', '70%'],
center: [12.5 + index * 25 + '%', '57%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 5,
borderColor: '#293441',
borderWidth: 1
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 10,
fontWeight: 'bold',
color: '#6581a1'
}
},
labelLine: {
show: false
},
data: findPrimaryImportance(name, PrimaryImportanceGroup)
}
})
};
}
const zhongyaoxingMapping = [
{ name: '抽检监测', field: 'cjjc' },
{ name: '信息追溯', field: 'xxzs' },
{ name: '投诉举报', field: 'tsjb' },
{ name: '执法检查', field: 'zfjc' },
// 可以继续添加其他类型
];
function resizeChart() {
if (chart1) {
chart1.resize();
}
if (chart2) {
chart2.resize();
}
if (chart3) {
chart3.resize();
}
if (chart4) {
chart4.resize();
}
}
const convertMapData = (data: any[], index1: number) => {
console.log(data, "2222222222222")
const legendValueMax = legendValue[index1][2]
return data.map((item) => {
const index =
legendValue[index1].findIndex((legend) => item.yuce_score <= legend) + (item.yuce_score > legendValueMax ? 4 : 0);
// debugger;
console.log(legendValue[index1]);
console.log(index1);
item.itemStyle = {
color: {
type: "radial",
x: 0.5,
y: 0.5,
r: 0.8,
colorStops: [
{ offset: 0, color: legend2[index][0] },
{ offset: 1, color: legend2[index][1] },
],
globalCoord: false,
},
};
// console.log(item);
return item;
});
};
/**
* 该函数用于将输入的数据转换为适合 ECharts 散点图使用的数据格式。
* @param {any[]} data - 输入的数据数组,每个元素包含地区相关的信息。
* @returns {Array<Object|null>} - 转换后的数组,每个元素是一个对象,包含散点图所需的信息,过滤掉无效的元素。
*/
function converScatterData(data: any[]) {
// 打印输入的数据,方便调试,"111" 可能是用于标识该日志的辅助信息
console.log(data, "111");
// 对输入的数据数组进行遍历,将每个元素转换为适合散点图的数据格式
return data.map((item) => {
// 从 geoCoordMap 中获取当前地区的地理坐标
const geoCoord = geoCoordMap[item.name];
// 根据地区的月度百分比变化计算对应的颜色索引
// console.log(scatterLegendValue, "-------------")
// console.log('当前月度百分比变化值:', item.all_score_vo.month_percentage_change);
const index =
scatterLegendValue.findIndex(
// 查找第一个满足月度百分比变化小于等于当前阈值的索引
(scatter) => {
// console.log(`比较: 当前阈值=${scatter}, 变化值=${item.all_score_vo.month_percentage_change}`);
return item.all_score_vo.month_percentage_change <= scatter
}
) + (item.all_score_vo.month_percentage_change > 0 ? 4 : 0);
// const index =
// scatterLegendValue.findIndex(
// // 查找第一个满足月度百分比变化小于等于当前阈值的索引
// (scatter) => item.all_score_vo.month_percentage_change <= scatter
// ) + (item.all_score_vo.month_percentage_change > 0 ? 4 : 0);
// 打印计算得到的颜色索引,方便调试
// console.log(index);
//设置气泡大小的值
const bubbleSize = Math.abs(item.yuce_score - item.value) * 10;
console.log(bubbleSize, "333333333333333");
// 如果存在地理坐标,则返回一个包含散点图所需信息的对象,否则返回 null
return geoCoord
? {
// 地区名称
name: item.name,
// 地理坐标和数值的组合
value: geoCoord.concat(item.value),
// 月份信息
month: item.month,
// 散点的样式,主要设置颜色
itemStyle: {
color: scatterLegend[index],
},
// 设置气泡大小(新增)
// symbolSize: bubbleSize, // 根据数值动态调整大小
// symbolSize: Math.max(10, Math.min(50, bubbleSize)), // 限制在20-50区间
symbolSize: Math.abs(item.all_score_vo.month_percentage_change) * 10,
}
: null;
})
// 过滤掉数组中的 null 元素,确保返回的数组只包含有效的数据对象
.filter(Boolean);
}
const getTooltipContent = (item: any) => {
// debugger;
const areaData = mapData[item.data.month - 1].find((d: any) => d.name === item.name);
if (!areaData) return "";
let content = `${item.name}<br/>`;
// 添加预测分
content += `${'预测分'}${areaData.yuce_score}<br/>`;
if (currentIndex.value < 5) {
// 添加真实分
content += formatScore(areaData.all_score_vo, "真实分");
}
//先留着,将来可能用
// 添加行政处罚
// content += formatScore(areaData.xzcf_vo, "行政处罚");
// // 添加信息追溯
// content += formatScore(areaData.xxzs_vo, "信息追溯");
// // 添加投诉举报
// content += formatScore(areaData.tsjb_vo, "投诉举报");
// // 添加执法检查
// content += formatScore(areaData.zfjc_vo, "执法检查");
// // 添加抽查考核
// content += formatScore(areaData.cckh_vo, "抽查考核");
// // 添加抽检监测
// content += formatScore(areaData.cjjc_vo, "抽检监测");
// // 添加综合性
// content += formatScore(areaData.zhx_vo, "综合性");
return content;
};
const formatScore = (scoreVo: any, primaryName: any) => {
const change = scoreVo.month_percentage_change;
const arrow = change > 0 ? "↑" : change < 0 ? "↓" : "";
const color = change > 0 ? "green" : change < 0 ? "red" : "";
let changeValue = Math.abs(change).toFixed(2);
changeValue = Number(changeValue) == 0 ? "" : changeValue;
return `${primaryName}${scoreVo.now_score.toFixed(
1
)} <span style="color:${color}">${arrow}${changeValue}</span><br/>`;
};
// onBeforeUnmount(() => {
// const myChart: any = echarts.init(chart.value as unknown as HTMLElement);
// window.removeEventListener("resize", myChart.resize);
// });
</script>
<style>
.card1 {
position: absolute;
top: 10px;
right: 10px;
width: 30px;
height: 30px;
background: transparent;
border: none;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.card-icon1 {
font-size: 16px;
color: white;
}
</style>