buliangMap/src/views/shanghaiStreetMapDasai.vue
yindongqi f0548cc2bc feat(视图): 为关联关系图添加弹出和收回动画效果
添加动画状态管理变量 isAnimating,并实现平滑的弹出和收回动画效果
修改关联关系图的显示逻辑,在动画播放期间保持元素可见
添加 CSS 动画关键帧和样式类来处理动画效果
2025-08-08 14:39:42 +08:00

4448 lines
129 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 ref="centerBorder"
class="absolute translate-x-1/2 translate-y-1/2 border-[2px] border-dashed rounded-[0.25rem] border-gray-600">
</div> -->
<!-- 右边的弹出窗体 -->
<nut-popup position="right" v-model:visible="state.show1" :style="{ width: state.width, height: state.height }">
<button @click="RefreshData" class="w-full bg-red-300">确定</button>
</nut-popup>
<!-- <div class="w-full h-screen bg-gradient-to-tr from-green-300 via-cyan-500 to-blue-400"
:style="{ backgroundColor: basicOptions.backgroundColor }" id="mainContainer"> -->
<div class="w-full h-screen bg-gradient-to-b from-blue-800 to-gray-800 relative overflow-hidden" id="mainContainer">
<!-- 科技感背景元素 -->
<div class="absolute inset-0 w-full h-full overflow-hidden">
<div
class="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-cyan-500 to-transparent opacity-70">
</div>
<div
class="absolute bottom-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-cyan-500 to-transparent opacity-70">
</div>
<div
class="absolute top-0 left-0 h-full w-[2px] bg-gradient-to-b from-transparent via-cyan-500 to-transparent opacity-70">
</div>
<div
class="absolute top-0 right-0 h-full w-[2px] bg-gradient-to-b from-transparent via-cyan-500 to-transparent opacity-70">
</div>
<!-- 科技感图案 -->
<div class="absolute top-10 left-10 w-[200px] h-[200px] border-l-2 border-t-2 border-cyan-500 opacity-20"></div>
<div class="absolute bottom-10 right-10 w-[200px] h-[200px] border-r-2 border-b-2 border-cyan-500 opacity-20">
</div>
<div
class="absolute top-0 left-0 w-full h-full bg-[url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48ZyBmaWxsPSJub25lIiBzdHJva2U9IiMwYmI3ZmYiIHN0cm9rZS13aWR0aD0iMC4yIiBvcGFjaXR5PSIwLjIiPjxwYXRoIGQ9Ik0yMCAyMCBMMCAyMCBMMCA0MCIvPjxwYXRoIGQ9Ik0yMCAyMCBMNDAgMjAgTDQwIDQwIi8+PHBhdGggZD0iTTIwIDIwIEwwIDIwIEwwIDAiLz48cGF0aCBkPSJNMjAgMjAgTDQwIDIwIEw0MCAwIi8+PC9nPjwvc3ZnPg==')] bg-repeat opacity-5">
</div>
</div>
<!-- 关闭按钮 -->
<router-link to="/navigation" style="z-index: 100" class="card1">
<div class="card-icon1"></div>
</router-link>
<!-- 标题 -->
<div class="w-full h-[52px]" id="containerTitle">
<div
class="flex w-full absolute justify-center flex-col items-center text-4xl py-2 z-10 pt-10 text-white font-bold">
<div class="relative">
<span class="text-cyan-500">上海市</span>
<span class="text-white">食品安全综合指数</span>
<div
class="absolute -bottom-2 left-0 w-full h-[3px] bg-gradient-to-r from-transparent via-cyan-500 to-transparent">
</div>
</div>
<!-- <div class="text-xl pt-4">总分数{{ AllCourse }}</div> -->
</div>
</div>
<!-- 仪表盘 -->
<vue-draggable-resizable ref="yibiao1" style="border: none; left: 49%; top: 16.5%; z-index: 999"
:draggable="draggableBoolean" :resizable="resizableBoolean">
<div class="flex items-center justify-center w-96 h-48 rounded-md bg-transparent" ref="yibiao"></div>
</vue-draggable-resizable>
<!-- 选项 -->
<div class="w-full absolute flex justify-center pt-12 gap-4 z-20">
<div
class="relative bg-transparent backdrop-blur-sm border border-blue-300/50 rounded px-3 py-2 shadow-[0_0_8px_rgba(59,130,246,0.3)]">
<!-- 新增年份下拉框 -->
<el-select v-model="yearValue" class="m-1 text-white font-medium bg-transparent" placeholder="年份"
@change="SelectBlur" style="width: 100px">
<el-option v-for="item in yearOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<!-- <div
class="absolute -top-3 left-2 bg-transparent backdrop-blur-sm px-2 text-xs text-blue-300 font-medium border-x border-t border-blue-300/50">
筛选条件</div> -->
<el-select v-model="monthValue" class="m-1 text-white font-medium bg-transparent" placeholder="月份"
@change="SelectBlur" style="width: 100px">
<el-option v-for="item in monthOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-select v-model="districtValue" class="m-1 text-white font-medium bg-transparent" placeholder="辖区"
@change="SelectBlur" style="width: 100px">
<el-option v-for="item in districtOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-select v-model="stageValue" class="m-1 text-white font-medium bg-transparent" placeholder="环节"
@change="SelectBlur" style="width: 100px">
<el-option v-for="item in stageOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</div>
<!-- 左1 -->
<vue-draggable-resizable ref="refWoTe1" style="border: none; z-index: 999" :draggable="draggableBoolean"
:resizable="resizableBoolean">
<div class="flex items-center justify-center w-96 h-48 border-2 border-dashed rounded border-gray-500"
ref="main1"></div>
</vue-draggable-resizable>
<!-- 左2 -->
<vue-draggable-resizable ref="refWoTe2" style="border: none; z-index: 999" :draggable="draggableBoolean"
:resizable="resizableBoolean">
<div class="flex items-center justify-center w-96 h-48 border-2 border-dashed rounded border-gray-500"
ref="main2"></div>
</vue-draggable-resizable>
<!-- 左3 -->
<vue-draggable-resizable ref="refWoTe3" style="border: none; z-index: 999" :draggable="draggableBoolean"
:resizable="resizableBoolean">
<div class="flex items-center justify-center w-96 h-48 border-2 border-dashed rounded border-gray-500"
ref="main3"></div>
</vue-draggable-resizable>
<!-- 右1 -->
<vue-draggable-resizable ref="refWoTe4" style="border: none; z-index: 999" :draggable="draggableBoolean"
:resizable="resizableBoolean">
<div class="flex items-center justify-center w-96 h-48 border-2 border-dashed rounded border-gray-500"
ref="main4">
<div class="flex justify-around text-white">
<div class="p-2 flex flex-col font-semibold items-center">
<div class="flex flex-row">
<img src="../../public/image/shiwuzhongdu.png" class="w-8 h-8 rounded-full" />食物中毒控制率
</div>
<div>{{ SecondLevelMetricValues.foodPoisoningIncidence }}分</div>
</div>
<div class="p-2 flex flex-col font-semibold items-center">
<div class="flex flex-row">
<img src="../../public/image/manyidu.png" class="w-8 h-8 rounded-full" />市民满意度
</div>
<div>{{ SecondLevelMetricValues.citizenSatisfaction }}分</div>
</div>
<div class="p-2 flex flex-col font-semibold items-center">
<div class="flex flex-row">
<img src="../../public/image/zhixiaodu.png" class="w-8 h-8 rounded-full" />市民知晓度
</div>
<div>{{ SecondLevelMetricValues.citizenAwareness }}分</div>
</div>
<!-- <div class="text-xl p-2 flex flex-col font-semibold items-center">
<div class="flex flex-row">
<img
src="../../public/image/qiyerenzhenglv.jpg"
class="w-8 h-8 rounded-full"
/>企业管理体系认证率
</div>
<div>{{ SecondLevelMetricValues.certificationRate }}分</div>
</div> -->
</div>
</div>
</vue-draggable-resizable>
<!-- 右2 -->
<vue-draggable-resizable ref="refWoTe5" style="border: none; z-index: 999;" :draggable="draggableBoolean"
:resizable="resizableBoolean">
<div class="flex items-center justify-center w-96 h-48 border-2 border-dashed rounded border-gray-500"
ref="main5">
<div class="scroll-wrapper" v-if="defaultShow">
<div class="scroll-header">
<span class="scroll-header-text">检测项目</span>
<span class="scroll-header-text">食品小类</span>
<span class="scroll-header-text">被抽样单位名称</span>
<span class="scroll-header-text">抽样时间</span>
<span class="">风险等级</span>
</div>
<div class="scroll-container">
<vue3ScrollSeamless :dataList="scrollData" :classOptions="classOptions">
<div v-for="(item, index) in scrollData" :key="index">
<div class="scroll-item">
<span class="scroll-item-text">{{ item.xiangmu }}</span>
<span class="scroll-item-text">{{ item.xiaolei }}</span>
<span class="scroll-item-text">{{ item.danwei }}</span>
<span class="scroll-item-text">{{ item.time }}</span>
<span class="" style="
width: 20px;
height: 10px;
color: black;
text-align: center;
margin: 2px 10px 0px 10px;
" :style="{ backgroundColor: item.dengji }">-</span>
</div>
</div>
</vue3ScrollSeamless>
</div>
</div>
<swiper :modules="modules" :loop="true" :slides-per-view="1" :space-between="50"
:autoplay="{ delay: 3000, disableOnInteraction: true }" :navigation="navigation"
:pagination="{ clickable: true }" :scrollbar="{ draggable: false }" class="swiperBox"
@slideChange="onSlideChange" v-if="!defaultShow" ref="swiperRef" id="swiperBox">
<swiper-slide v-if="choujianShow">
<div class="scroll-wrapper">
<div class="scroll-header-title">首发预警</div>
<div class="scroll-header">
<span class="scroll-header-text">检测项目</span>
<span class="scroll-header-text">食品小类</span>
<span class="scroll-header-text">被抽样单位名称</span>
<span class="scroll-header-text">抽样时间</span>
<span class="">风险等级</span>
</div>
<div class="scroll-container">
<vue3ScrollSeamless :dataList="choujianShoufaData" :classOptions="classOptions">
<div v-for="(item, index) in choujianShoufaData" :key="index">
<div class="scroll-item">
<span class="scroll-item-text">{{ item.xiangmu }}</span>
<span class="scroll-item-text">{{ item.xiaolei }}</span>
<span class="scroll-item-text">{{ item.danwei }}</span>
<span class="scroll-item-text">{{ item.time }}</span>
<span class="" style="
width: 20px;
height: 10px;
color: black;
text-align: center;
margin: 2px 10px 0px 10px;
" :style="{ backgroundColor: item.dengji }">-</span>
</div>
</div>
</vue3ScrollSeamless>
</div>
</div>
</swiper-slide>
<swiper-slide v-if="choujianShow">
<div class="scroll-wrapper">
<div class="scroll-header-title">高发预警</div>
<div class="scroll-header">
<span class="scroll-header-text">检测项目</span>
<span class="scroll-header-text">食品小类</span>
<span class="scroll-header-text">抽检件数</span>
<span class="scroll-header-text">不合格率(%</span>
<span class="">风险等级</span>
</div>
<div class="scroll-container">
<vue3ScrollSeamless :dataList="choujianGaofaData" :classOptions="classOptions">
<div v-for="(item, index) in choujianGaofaData" :key="index">
<div class="scroll-item">
<span class="scroll-item-text">{{ item.xiangmu }}</span>
<span class="scroll-item-text">{{ item.xiaolei }}</span>
<span class="scroll-item-text">{{ item.choujian }}</span>
<span class="scroll-item-text">{{ item.buhegelv }}</span>
<span class="" style="
width: 20px;
height: 10px;
color: black;
text-align: center;
margin: 2px 10px 0px 10px;
" :style="{ backgroundColor: item.dengji }">-</span>
</div>
</div>
</vue3ScrollSeamless>
</div>
</div>
</swiper-slide>
<swiper-slide v-if="choujianShow">
<div class="w-full h-full">
<div ref="main7" style="width: 100%; height: 100%"></div>
</div>
</swiper-slide>
<swiper-slide v-if="tousuShow">
<div class="scroll-wrapper">
<div class="scroll-header-title">高发预警</div>
<div class="scroll-header">
<span class="scroll-header-text">投诉环节</span>
<span class="scroll-header-text">投诉问题种类</span>
<span class="scroll-header-text">投诉率</span>
<span class="">风险等级</span>
</div>
<div class="scroll-container">
<vue3ScrollSeamless :dataList="jubaoGaofaData" :classOptions="classOptions">
<div v-for="(item, index) in jubaoGaofaData" :key="index" @click="handleRowClick(item, index)">
<div class="scroll-item">
<span class="scroll-item-text">{{ item.huanjie }}</span>
<span class="scroll-item-text">{{ item.zhonglei }}</span>
<span class="scroll-item-text">{{ item.tousulv }}</span>
<span class="" style="
width: 20px;
height: 10px;
color: black;
text-align: center;
margin: 2px 10px 0px 10px;
" :style="{ backgroundColor: item.dengji }">-</span>
</div>
</div>
</vue3ScrollSeamless>
</div>
</div>
</swiper-slide>
<swiper-slide v-if="tousuShow">
<div class="scroll-wrapper">
<div class="scroll-header-title">频发预警</div>
<div class="scroll-header">
<span class="scroll-header-text">被投诉主体名称</span>
<span class="scroll-header-text">被投诉产品类别</span>
<span class="scroll-header-text">抽检合格率</span>
<span class="scroll-header-text">被投诉问题种类</span>
<span class="scroll-header-text">被投诉问题数</span>
<span class="">风险等级</span>
</div>
<div class="scroll-container">
<vue3ScrollSeamless :dataList="jubaoPinfaData" :classOptions="classOptions">
<div v-for="(item, index) in jubaoPinfaData" :key="index">
<div class="scroll-item">
<span class="scroll-item-text">{{ item.zhuti }}</span>
<span class="scroll-item-text">{{ item.leibie }}</span>
<span class="scroll-item-text">{{ item.hegelv }}</span>
<span class="scroll-item-text">{{ item.zhonglei }}</span>
<span class="scroll-item-text">{{ item.wentishu }}</span>
<span class="" style="
width: 20px;
height: 10px;
color: black;
text-align: center;
margin: 2px 10px 0px 10px;
" :style="{ backgroundColor: item.dengji }">-</span>
</div>
</div>
</vue3ScrollSeamless>
</div>
</div>
</swiper-slide>
<div class="swiper-button-prev" @click.stop="prevEl" />
<!--左箭头如果放置在swiper外面需要自定义样式-->
<div class="swiper-button-next" @click.stop="nextEl" />
<!--右箭头如果放置在swiper外面需要自定义样式-->
<!-- 如果需要滚动条 -->
<!-- <div class="swiper-scrollbar"></div> -->
</swiper>
<div class="scroll-wrapper" v-if="zhifaShow">
<div class="w-full h-full">
<div ref="main8" style="width: 100%; height: 100%"></div>
</div>
</div>
</div>
</vue-draggable-resizable>
<!-- 右3 -->
<vue-draggable-resizable ref="refWoTe6" style="border: none; z-index: 999" :draggable="draggableBoolean"
:resizable="resizableBoolean" v-show="!relationShow && !isAnimating">
<div
class="flex items-center justify-center w-96 h-48 border-2 border-dashed rounded border-gray-500 overflow-hidden"
ref="main6">
</div>
</vue-draggable-resizable>
<!-- 右下角按钮 -->
<div ref="fancyBtn"
class=" fixed z-[9999] bottom-10 right-7 px-3 py-1 rounded-full bg-gradient-to-r from-blue-500 via-cyan-400 to-purple-500 shadow-lg text-white font-bold text-lg cursor-pointer transition-all duration-300 ease-in-out hover:scale-110 hover:shadow-2xl active:scale-95 active:brightness-90 ring-2 ring-white/30 ring-offset-2 ring-offset-black backdrop-blur-md overflow-hidden"
@click="handleFancyClick" style="user-select: none;">
关联关系图
<span ref="ripple" class="ripple"></span>
</div>
<div
class="flex items-center justify-center w-96 h-48 border-2 border-dashed rounded border-gray-500 overflow-hidden z-[999] fixed bottom-10 right-7"
ref="main9"
:class="{
'animate-popup': isAnimating && relationShow,
'animate-shrink': isAnimating && !relationShow,
'opacity-0 scale-0': !relationShow && !isAnimating
}"
v-show="relationShow || isAnimating"
style="transform-origin: bottom right;">
</div>
<!-- 底部 -->
<div class="w-full absolute bottom-2 flex justify-center text-white">
<div class="flex items-center justify-center w-96 h-48" ref="timeline"></div>
</div>
<div class="shanghaimap">
<!-- <EChartsMap /> -->
<!-- <div id="streetMapEcharts" style="width: 320%; height: 50%;"></div> -->
<div id="streetMapEcharts" style="width: 100%; height: 100%"></div>
</div>
<div id="tooltip" style="
position: absolute;
background-color: #fff;
border: 1px solid #ddd;
padding: 10px;
border-radius: 5px;
pointer-events: none;
z-index: 9999;
" v-show="isTooltipVisible"></div>
</div>
</template>
<script setup lang="ts">
import VueDraggableResizable from "vue-draggable-resizable/src/components/vue-draggable-resizable.vue";
import "vue-draggable-resizable/dist/VueDraggableResizable.css";
import { onMounted, reactive, ref, watch, onBeforeUnmount, nextTick } from "vue";
import BarShow from "../components/barshow.vue";
import shangHaiJieDao from "../mapjson/shangHaiJieDao.json";
import shangHaiXiaQu from "../mapjson/shanghaiXiaQu.json";
import CnFxJiedao2 from "../mapjson/CnFxJiedao2.json";
import type { GeoJSONSourceInput } from "echarts/types/src/coord/geo/geoTypes";
import { vue3ScrollSeamless } from "vue3-scroll-seamless";
import EChartsMap from "./eChartsMap.vue";
import { Sketch } from "@ans1998/vue3-color";
import {
getMapLayoutList,
loadMapOptions,
removeMapLayout,
updateMapLayout,
getDistinguishValue,
getCheckMonitoringValueStreet,
getCheckAssessmentValueStreet,
getIstrativeSanctionValueStreet,
getComplaintReportValueStreet,
getInformationTracingValueStreet,
getLawEnforcementInspectionValueStreet,
getAllCourse,
getSecondLevelMetricValues,
getAllData,
getStreetAllData,
} from "../api/SettingAPIDaSai";
import Enumerable from "linq";
import { first, reduce } from "lodash";
import { ElMessage } from "element-plus";
import { router } from "../router";
import headMenu from "../components/headMenu.vue";
import * as echarts from "echarts";
import "echarts-wordcloud";
import * as d3 from "d3";
import { Swiper, SwiperSlide } from "swiper/vue";
import "swiper/css";
import "swiper/less/navigation";
import "swiper/less/pagination";
import {
Autoplay,
Navigation,
Pagination,
Scrollbar,
A11y,
} from "swiper/modules";
// 新增 API 引入
import { getRelationshipNetwork } from "../api/compositeIndex";
//各个区的值
interface DistinguishValueInterface {
[key: string]: number;
}
// 新增类型
interface TimelineData {
timeLabel: string;
realScores: number[];
predictScores: number[];
}
// 类型定义
type TimePeriod =
| "上三个月"
| "上二个月"
| "上一个月"
| "当前月"
| "下一个月"
| "下二个月"
| "下三个月";
type ScoreData = {
real: number[];
predict: number[];
};
// 响应式数据
const timelineData = ref<TimelineData[]>([]);
const currentTimeIndex = ref(0);
//改变地图大小
const changeSizeValue = ref(1.15);
//设置地图左右移动
const changePositionX = ref(1.02);
//设置地图上下移动
const changePositionY = ref(30);
//设置16个区是否可以移动
const draggableBoolean = ref(false);
//设置16个区是否可以放大缩小
const resizableBoolean = ref(false);
//总分数
const AllCourse = ref(100);
//tooltip框显示
const isTooltipVisible = ref(false);
//右2图表默认展示
const defaultShow = ref(true);
const classOptions = {
// 滚动步长
step: 0.1,
limitMoveNum: 9,
};
const navigation = ref({
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
});
// 在modules加入要使用的模块
const modules = [Autoplay, Pagination, Navigation, Scrollbar];
const prevEl = () => {
console.log("上一张");
};
const nextEl = () => {
console.log("下一张");
};
// 更改当前活动swiper
const onSlideChange = (swiper: any) => {
// swiper是当前轮播的对象里面可以获取到当前swiper的所有信息当前索引是activeIndex
console.log(swiper.activeIndex, "活动swiper更改");
};
const swiperBoxHeight = ref("300px");
const choujianShow = ref(false);
const tousuShow = ref(false);
const zhifaShow = ref(false);
//
const littlemonth = ref(-1);
// 下拉框"年份"值
const yearValue = ref("2023");
// 下拉框"年份"option
const yearOptions = [
{
value: "2021",
label: "2021",
},
{
value: "2022",
label: "2022",
},
{
value: "2023",
label: "2023",
},
// 可以根据需要添加更多年份
];
//下拉框"月份"值
const monthValue = ref("4");
// 下拉框"月份"option
const monthOptions = [
{
value: "",
label: "全部",
},
{
value: "1",
label: "一月",
},
{
value: "2",
label: "二月",
},
{
value: "3",
label: "三月",
},
{
value: "4",
label: "四月",
},
{
value: "5",
label: "五月",
},
// {
// value: "6",
// label: "六月",
// }
// ,
// {
// value: "7",
// label: "七月",
// },
// {
// value: "8",
// label: "八月",
// },
// {
// value: "9",
// label: "九月",
// },
// {
// value: "10",
// label: "十月",
// },
// {
// value: "11",
// label: "十一月",
// },
// {
// value: "12",
// label: "十二月",
// },
];
//下拉框"市区"值
const districtValue = ref("上海市");
//下拉框"市区"option
const districtOptions = [
{
value: "上海市",
label: "上海市",
},
{
value: "奉贤区",
label: "奉贤区",
},
{
value: "长宁区",
label: "长宁区",
},
{
value: "黄浦区",
label: "黄浦区",
},
{
value: "徐汇区",
label: "徐汇区",
},
{
value: "静安区",
label: "静安区",
},
{
value: "普陀区",
label: "普陀区",
},
{
value: "虹口区",
label: "虹口区",
},
// {
// value: "杨浦区",
// label: "杨浦区",
// },
// {
// value: "闵行区",
// label: "闵行区",
// },
// {
// value: "宝山区",
// label: "宝山区",
// },
// {
// value: "嘉定区",
// label: "嘉定区",
// },
// {
// value: "浦东新区",
// label: "浦东新区",
// },
// {
// value: "金山区",
// label: "金山区",
// },
// {
// value: "松江区",
// label: "松江区",
// },
// {
// value: "青浦区",
// label: "青浦区",
// },
// {
// value: "崇明区",
// label: "崇明区",
// },
];
// 背景页面颜色:
const webPageBackgroundColor = "#F0F0F0";
//图表颜色
const echartsColorArray = [
"#5470c6",
"#91cc75",
"#fac858",
"#ee6666",
"#73c0de",
"#3ba272",
"#fc8452",
"#9a60b4",
"#ea7ccc",
];
// const echartsColorArray = ['#FF00FF', '#00FF00'];
// 六个表的分数
const checkMonitoringScore = ref(100); //安全抽检1表
const checkAssessmentScore = ref(100); //抽查考核2表
const istrativeSanctionScore = ref(100); //行政处罚3表
const complaintReportScore = ref(100); //投诉举报4表
const informationTracingScore = ref(100); //信息追溯5表
const lawEnforcementInspectionScore = ref(100); //执法检查6表
//六个表的值
const checkMonitoringData = ref(); //安全抽检1表
const checkAssessmentPost = ref(); //抽查考核2表岗位
const checkAssessmentPassRate = ref(); //抽查考核2表合格率
const checkAssessmentNumber = ref(); //抽查考核2表考核人次
const istrativeSanctionData = ref(); //行政处罚3表
const complaintReportData = ref(); //投诉举报4表
const informationTracingCompanyType = ref(); //信息追溯5表企业分类名
const informationTracingCompanyTypeValue = ref(); //信息追溯5表企业分类值
const lawEnforcementInspectionData = ref(); //执法检查6表
// 二级指标的值
const SecondLevelMetricValues = ref({
foodPoisoningIncidence: 100,
citizenSatisfaction: 100,
citizenAwareness: 100,
certificationRate: 100,
});
// 下拉框"环节"值
const stageValue = ref("");
// 下拉框"环节"option
const stageOptions = [
{
value: "",
label: "全部",
},
{
value: "生产环节",
label: "生产环节",
},
{
value: "销售环节",
label: "销售环节",
},
{
value: "餐饮环节",
label: "餐饮环节",
},
];
///新增的展示框图界面
const arrStepShow = ref(["", "", "", ""]);
//新增展示框图的颜色数据
const curArrColor = ref();
const arrColorDef = {
red: [
"#FEF2F2",
"#FEE2E2",
"#FECACA",
"#FCA5A5",
"#F87171",
"#EF4444",
"#DC2626",
"#B91C1C",
"#991B1B",
"#7F1D1D",
],
green: [
"#ECFDF5",
"#D1FAE5",
"#A7F3D0",
"#6EE7B7",
"#34D399",
"#10B981",
"#059669",
"#047857",
"#065F46",
"#064E3B",
],
yellow: [
"#FFFBEB",
"#FEF3C7",
"#FDE68A",
"#FCD34D",
"#FBBF24",
"#F59E0B",
"#D97706",
"#B45309",
"#92400E",
"#78350F",
],
blue: [
// "#EFF6FF",
// "#DBEAFE",
// "#BFDBFE",
// "#93C5FD",
"#60A5FA",
"#3B82F6",
// "#2563EB",
"#1D4ED8",
// "#1E40AF",
"#1E3A8A",
],
qingxin: [
"#FFFF99", // 分数段0-25
"#98FB98", // 分数段25-50
"#ADD8E6", // 分数段50-75
"#A52A2A", // 分数段75-100
],
};
//赋值测试
curArrColor.value = arrColorDef.blue;
//设置展示框图界面数据
const setStepShow = (min: number, max: number, jiange: number) => {
arrStepShow.value = [];
for (let index = 0; index < 4; index++) {
var strShow =
(min + jiange * index).toFixed(0) +
"~" +
(min + jiange * (index + 1)).toFixed(0);
arrStepShow.value.push(strShow);
}
arrStepShow.value.push((min + jiange * 4).toFixed(0) + "~" + max.toFixed(0));
//设置显示的颜色,改造成动态的方式
switch (basicOptions.radioColorType) {
case "red":
curArrColor.value = arrColorDef.red;
break;
case "yellow":
curArrColor.value = arrColorDef.yellow;
break;
case "blue":
curArrColor.value = arrColorDef.blue;
break;
case "green":
curArrColor.value = arrColorDef.green;
break;
case "qingxin":
curArrColor.value = arrColorDef.qingxin;
break;
}
// // console.log("间隔数据", arrStepShow);
};
//路由切换部分
// const radio1 = ref('上海地图')
// const getRouter=(e:any)=>{
// // console.log('路由',e);
// if (e==='全国地图') {
// router.push('/chinamap')
// }
// }
//定义每个页面的后台编号
const curId = "1";
//这个页面除了地图之外的统一配置
const basicOptions = reactive({
backgroundColor: webPageBackgroundColor, //整个界面的背景颜色
isPresetColorRange: false, //是否是使用自己的设置区间值
colorRange: ["0", "0", "0", "0", "0", "0", "0", "0", "0"], //自己设计的区间值
radioColorType: "blue", //地图的色系选择
barTitle: "各区分数", //柱状图的标题内容
barTextColor: "", //柱状图的文字颜色
isSqrt: false, //是否进行开平方的操作
});
//改变背景颜色的功能部分///////////////////////////////////////////////
const showSketch_Background = ref(false);
//const sketchBgColor_Background = ref(0);
//改变背景颜色的设置
const changSketch_Background = () => {
showSketch_Background.value = !showSketch_Background.value;
};
const changSketchButton_Background = (item: any) => {
if (item.isOk) {
// console.log("确定");
basicOptions.backgroundColor = item.activeColor;
showSketch_Background.value = false;
} else {
// // console.log("取消");
showSketch_Background.value = false;
}
};
/////////////////////////////////////////////////////////////////////////////
//改变文字颜色的功能部分///////////////////////////////////////////////
const showSketch_barTextColor = ref(false);
//const sketchBgColor_barTextColor = ref(0);
//改变文字颜色的设置
const changSketch_barTextColor = () => {
showSketch_barTextColor.value = !showSketch_barTextColor.value;
};
const changSketchButton_barTextColor = (item: any) => {
if (item.isOk) {
// console.log("确定");
basicOptions.barTextColor = item.activeColor;
showSketch_barTextColor.value = false;
} else {
// console.log("取消");
showSketch_barTextColor.value = false;
}
};
/////////////////////////////////////////////////////////////////////////////
const state = reactive({
show1: false,
width: "30%",
height: "100%",
});
const openDrawer = () => {
state.show1 = true;
};
//const internalInstance = getCurrentInstance()
//颜色色系设置
//const radioColorType = ref("red");
const curMax = ref(0);
//所有的拖拉框
// 崇明区
const refChongMing = ref(null);
// 浦东新区
const refPuDong = ref(null);
// 宝山区
const refBaoShan = ref(null);
// 嘉定区
const refJiaDing = ref(null);
// 青浦区
const refQingPu = ref(null);
// 金山区
const refJinShan = ref(null);
// 奉贤区
const refFengXian = ref(null);
// 松江区
const refSongJiang = ref(null);
// 闵行区
const refMinHang = ref(null);
// 徐汇区
const refXuHui = ref(null);
// 长宁区
const refChangNing = ref(null);
// 普陀区
const refPuTuo = ref(null);
// 静安区
const refJingAn = ref(null);
// 虹口区
const refHongKou = ref(null);
// 黄浦区
const refHuangPu = ref(null);
// 杨浦区
const refYangPu = ref(null);
//柱状图
const refBar = ref(null);
// 颜色图例
const refColorLegend = ref(null);
//食品大类表格
const refWoTe1 = ref(null);
const refWoTe2 = ref(null);
const refWoTe3 = ref(null);
const refWoTe4 = ref(null);
const refWoTe5 = ref(null);
const refWoTe6 = ref(null);
const yibiao1 = ref(null);
//设置动态样式颜色的值
// 崇明区
const colorChongMing = ref(null);
// 浦东新区
const colorPuDong = ref(null);
// 宝山区
const colorBaoShan = ref(null);
// 嘉定区
const colorJiaDing = ref(null);
// 青浦区
const colorQingPu = ref(null);
// 金山区
const colorJinShan = ref(null);
// 奉贤区
const colorFengXian = ref(null);
// 松江区
const colorSongJiang = ref(null);
// 闵行区
const colorMinHang = ref(null);
// 徐汇区
const colorXuHui = ref(null);
// 长宁区
const colorChangNing = ref(null);
// 普陀区
const colorPuTuo = ref(null);
// 静安区
const colorJingAn = ref(null);
// 虹口区
const colorHongKou = ref(null);
// 黄浦区
const colorHuangPu = ref(null);
// 杨浦区
const colorYangPu = ref(null);
// 柱状图
const colorBar = ref(null);
let chartRelationship: echarts.ECharts | null = null; // 需要保持图表实例引用
//const curColorType=ref("red")
//所有的拖拉框的位置参数
const curLayout = reactive({
ChongMing: { value: "0", color: "fill-red-100" },
PuDong: { value: "0", color: "fill-red-100" },
BaoShan: { value: "0", color: "fill-red-100" },
JiaDing: { value: "0", color: "fill-red-100" },
QingPu: { value: "0", color: "fill-red-100" },
JinShan: { value: "0", color: "fill-red-100" },
FengXian: { value: "0", color: "fill-red-100" },
SongJiang: { value: "0", color: "fill-red-100" },
MinHang: { value: "0", color: "fill-red-100" },
XuHui: { value: "0", color: "fill-red-100" },
ChangNing: { value: "0", color: "fill-red-100" },
PuTuo: { value: "0", color: "fill-red-100" },
JingAn: { value: "0", color: "fill-red-100" },
HongKou: { value: "0", color: "fill-red-100" },
HuangPu: { value: "0", color: "fill-red-100" },
YangPu: { value: "0", color: "fill-red-100" },
Bar: { value: "0", color: "fill-red-100" },
Options: {},
ColorLegend: {},
WoTe1: {},
WoTe2: {},
WoTe3: {},
WoTe4: {},
WoTe5: {},
WoTe6: {},
});
//当前的颜色赋值的数组
const curArrayColor: any = ref([]);
//浏览器可视页面宽度
const windowWidth = ref(1920);
//浏览器可视页面高度
const windowHeight = ref(960);
// 保存配置的结果
const btnLoad = () => {
setLayout(refWoTe1.value! as any, curLayout["WoTe1"]);
setLayout(refWoTe2.value! as any, curLayout["WoTe2"]);
setLayout(refWoTe3.value! as any, curLayout["WoTe3"]);
setLayout(refWoTe4.value! as any, curLayout["WoTe4"]);
setLayout(refWoTe5.value! as any, curLayout["WoTe5"]);
setLayout(refWoTe6.value! as any, curLayout["WoTe6"]);
};
//位子重置
const btnLoadJson = (
distinguishValue:
| DistinguishValueInterface
| { name: string; value: number }[]
) => {
console.log("位置重置");
console.log("windowWidth.value", windowWidth.value);
console.log("windowHeight.value", windowHeight.value);
//把保存的信息放在这里
//1920 960
const dJson = {
Bar: {
left: windowWidth.value / 3.5, //432,
top: windowHeight.value / 8, //80,
width: 90, //81,
// // height: windowHeight.value/6.62,//145,
value: "0",
color: "fill-red-100",
},
};
curLayout.Bar = dJson.Bar;
console.log("dJson", dJson);
btnLoad();
};
//获取界面位置的方法
const getLayout = (refObj: any) => {
return {
left: refObj.left,
top: refObj.top,
width: refObj.width,
height: refObj.height,
value: "0",
color: "fill-red-100",
};
};
//设置界面位置
const setLayout = (refObj: any, Pos: any) => {
// console.log("d显示布局", refObj, "-------", Pos);
refObj.left = Pos.left;
refObj.top = Pos.top;
refObj.width = Pos.width;
refObj.height = Pos.height;
};
//设置文字通用颜色
const textStyle = ref("font-xs");
//设置当前的颜色分隔值
const GetColorValue = (curvalue: number) => {
// console.log(curvalue);
//先从整体里面获取总数值,然后进行开发
let arrValue: Array<{ id: string; value: any }> = new Array<{
id: string;
value: any;
}>();
arrValue.push({ id: "ChongMing", value: curLayout.ChongMing.value });
arrValue.push({ id: "PuDong", value: curLayout.PuDong.value });
arrValue.push({ id: "BaoShan", value: curLayout.BaoShan.value });
arrValue.push({ id: "JiaDing", value: curLayout.JiaDing.value });
arrValue.push({ id: "QingPu", value: curLayout.QingPu.value });
arrValue.push({ id: "JinShan", value: curLayout.JinShan.value });
arrValue.push({ id: "FengXian", value: curLayout.FengXian.value });
arrValue.push({ id: "SongJiang", value: curLayout.SongJiang.value });
arrValue.push({ id: "MinHang", value: curLayout.MinHang.value });
arrValue.push({ id: "XuHui", value: curLayout.XuHui.value });
arrValue.push({ id: "ChangNing", value: curLayout.ChangNing.value });
arrValue.push({ id: "PuTuo", value: curLayout.PuTuo.value });
arrValue.push({ id: "JingAn", value: curLayout.JingAn.value });
arrValue.push({ id: "HongKou", value: curLayout.HongKou.value });
arrValue.push({ id: "HuangPu", value: curLayout.HuangPu.value });
arrValue.push({ id: "YangPu", value: curLayout.YangPu.value });
// console.log("新生成数据值", arrValue);
//定义一个目前使用的中间值进行整体的判断操作
//下面进行改造,考虑是使用覆盖模式还是使用当前的平均值模式:
//首先要判断是不是使用均方差,是均方差,需要使用
if (basicOptions.isSqrt == true) {
// console.log("开方之前的值", curvalue);
curvalue = Math.sqrt(curvalue);
// console.log("开方之后的值", curvalue);
}
let max = 0;
let min = 60;
let intJianGe = 0;
if (basicOptions.isPresetColorRange == true) {
//就从新生成的数组里面找到位置
for (let index = 0; index <= 10; index++) {
// const element = array[index];
// console.log(
// "色彩区间",
// index,
// curvalue,
// basicOptions.colorRange[index],
// basicOptions.colorRange[index + 1]
// );
if (
curvalue > parseFloat(basicOptions.colorRange[index]) &&
curvalue <= parseFloat(basicOptions.colorRange[index + 1])
) {
return index + 1;
}
}
return 0;
} else {
// arrValue.map()
max = arrValue.reduce(function (prev, cur) {
return Math.max(prev, cur.value!);
}, 0);
// min = arrValue.reduce(function (prev, cur) {
// return Math.min(prev, cur.value!);
// }, 0);
min = arrValue.reduce((prev, cur) => Math.min(prev, cur.value), Infinity);
//最大和最小的平均分割
intJianGe = (max - min) / 4;
// console.log("计算结果", max, min, intJianGe);
//增加的间隔计算结果方法
setStepShow(min, max, intJianGe);
curMax.value = max;
for (let index = 1; index <= 9; index++) {
//const element = array[index];
if (intJianGe * index + min >= curvalue) {
return index;
}
}
return 1;
}
};
const changeClass = (refObj: any, index: number) => {
// console.log(index);
switch (index) {
case 1:
if (basicOptions.radioColorType == "red") refObj.value = "fill-red-400";
if (basicOptions.radioColorType == "yellow")
refObj.value = "fill-yellow-400";
if (basicOptions.radioColorType == "blue") refObj.value = "fill-blue-400";
if (basicOptions.radioColorType == "green")
refObj.value = "fill-green-400";
if (basicOptions.radioColorType == "qingxin")
refObj.value = "fill-yellow-200";
break;
case 2:
if (basicOptions.radioColorType == "red") refObj.value = "fill-red-500";
if (basicOptions.radioColorType == "yellow")
refObj.value = "fill-yellow-500";
if (basicOptions.radioColorType == "blue") refObj.value = "fill-blue-500";
if (basicOptions.radioColorType == "green")
refObj.value = "fill-green-500";
if (basicOptions.radioColorType == "qingxin")
refObj.value = "fill-green-200";
break;
case 3:
if (basicOptions.radioColorType == "red") refObj.value = "fill-red-700";
if (basicOptions.radioColorType == "yellow")
refObj.value = "fill-yellow-700";
if (basicOptions.radioColorType == "blue") refObj.value = "fill-blue-700";
if (basicOptions.radioColorType == "green")
refObj.value = "fill-green-700";
if (basicOptions.radioColorType == "qingxin")
refObj.value = "fill-sky-200";
break;
case 4:
if (basicOptions.radioColorType == "red") refObj.value = "fill-red-900";
if (basicOptions.radioColorType == "yellow")
refObj.value = "fill-yellow-900";
if (basicOptions.radioColorType == "blue") refObj.value = "fill-blue-900";
if (basicOptions.radioColorType == "green")
refObj.value = "fill-green-900";
if (basicOptions.radioColorType == "qingxin")
refObj.value = "fill-red-800";
break;
default:
break;
}
};
//检测覆盖设置的数据
const checkArray = () => {
var cArr = basicOptions.colorRange;
// console.log("cArr", cArr);
for (let index = 1; index < cArr.length; index++) {
// console.log("arr1,arr2", cArr[index - 1], cArr[index]);
if (parseFloat(cArr[index]) < parseFloat(cArr[index - 1])) {
return false;
}
}
return true;
};
//刷新页面数据
const RefreshData = () => {
///GetColor("杨浦")
// var aa=GetColorValue(curLayout.YangPu.value)
// // console.log('杨浦数据',aa);
//colorYangPu.value=aa.toString()
//colorYangPu.value="fill-red-100"
//// console.log('崇明信息',GetColorValue(curLayout.ChongMing.value));
//如果发现需要覆盖现有的参数逻辑,需要进行整个数组的叠加的增加趋势判断才行,所以需要一个函数方法;
var aa = checkArray();
if (basicOptions.isPresetColorRange == true) {
// console.log("开始识别");
if (checkArray() === false) {
ElMessage({
type: "success",
message: "覆盖的数据结构配置不正确,需要按照从小到大的状态进行设置",
});
return false;
}
}
// console.log(curLayout.ChongMing.value, "-------------------------");
changeClass(
colorChongMing,
GetColorValue(parseFloat(curLayout.ChongMing.value))
);
changeClass(colorPuDong, GetColorValue(parseFloat(curLayout.PuDong.value)));
changeClass(colorBaoShan, GetColorValue(parseFloat(curLayout.BaoShan.value)));
changeClass(colorJiaDing, GetColorValue(parseFloat(curLayout.JiaDing.value)));
changeClass(colorQingPu, GetColorValue(parseFloat(curLayout.QingPu.value)));
changeClass(colorJinShan, GetColorValue(parseFloat(curLayout.JinShan.value)));
changeClass(
colorFengXian,
GetColorValue(parseFloat(curLayout.FengXian.value))
);
changeClass(
colorSongJiang,
GetColorValue(parseFloat(curLayout.SongJiang.value))
);
changeClass(colorMinHang, GetColorValue(parseFloat(curLayout.MinHang.value)));
changeClass(colorXuHui, GetColorValue(parseFloat(curLayout.XuHui.value)));
changeClass(
colorChangNing,
GetColorValue(parseFloat(curLayout.ChangNing.value))
);
changeClass(colorPuTuo, GetColorValue(parseFloat(curLayout.PuTuo.value)));
changeClass(colorJingAn, GetColorValue(parseFloat(curLayout.JingAn.value)));
changeClass(colorHongKou, GetColorValue(parseFloat(curLayout.HongKou.value)));
changeClass(colorHuangPu, GetColorValue(parseFloat(curLayout.HuangPu.value)));
changeClass(colorYangPu, GetColorValue(parseFloat(curLayout.YangPu.value)));
const curArrayColorTemp = [];
//生成新的颜色数组
curArrayColorTemp.push({
name: "崇明",
value: curLayout.ChongMing.value,
color: colorChongMing.value!,
});
curArrayColorTemp.push({
name: "浦东",
value: curLayout.PuDong.value,
color: colorPuDong.value!,
});
curArrayColorTemp.push({
name: "宝山",
value: curLayout.BaoShan.value,
color: colorBaoShan.value!,
});
curArrayColorTemp.push({
name: "嘉定",
value: curLayout.JiaDing.value,
color: colorJiaDing.value!,
});
curArrayColorTemp.push({
name: "青浦",
value: curLayout.QingPu.value,
color: colorQingPu.value!,
});
curArrayColorTemp.push({
name: "金山",
value: curLayout.JinShan.value,
color: colorJinShan.value!,
});
curArrayColorTemp.push({
name: "奉贤",
value: curLayout.FengXian.value,
color: colorFengXian.value!,
});
curArrayColorTemp.push({
name: "松江",
value: curLayout.SongJiang.value,
color: colorSongJiang.value!,
});
curArrayColorTemp.push({
name: "闵行",
value: curLayout.MinHang.value,
color: colorMinHang.value!,
});
curArrayColorTemp.push({
name: "徐汇",
value: curLayout.XuHui.value,
color: colorXuHui.value!,
});
curArrayColorTemp.push({
name: "长宁",
value: curLayout.ChangNing.value,
color: colorChangNing.value!,
});
curArrayColorTemp.push({
name: "普陀",
value: curLayout.PuTuo.value,
color: colorPuTuo.value!,
});
curArrayColorTemp.push({
name: "静安",
value: curLayout.JingAn.value,
color: colorJingAn.value!,
});
curArrayColorTemp.push({
name: "虹口",
value: curLayout.HongKou.value,
color: colorHongKou.value!,
});
curArrayColorTemp.push({
name: "黄浦",
value: curLayout.HuangPu.value,
color: colorHuangPu.value!,
});
curArrayColorTemp.push({
name: "杨浦",
value: curLayout.YangPu.value,
color: colorYangPu.value!,
});
// streetScore
//由大到小排序
// curArrayColor.value = curArrayColorTemp.sort((a, b) => {
// if (parseFloat(a.value!) > parseFloat(b.value!)) {
// return -1;
// }
// if (parseFloat(a.value!) < parseFloat(b.value!)) {
// return 1;
// }
// return 0;
// });
curArrayColor.value = streetScore.sort((a, b) => {
if (a.value! > b.value!) {
return -1;
}
if (a.value! < b.value!) {
return 1;
}
return 0;
});
// console.log("curArrayColor", curArrayColor.value);
};
//RefreshData();
let main1 = ref();
let main2 = ref();
let main3 = ref();
let main4 = ref();
let main5 = ref();
let main6 = ref();
let main7 = ref();
let main8 = ref();
let main9 = ref();
const relationShow = ref(false);
const isAnimating = ref(false);
let swiperRef = ref();
let timeline = ref();
let yibiao = ref();
let centerBorder = ref();
let bottomBorder = ref();
let zhifaEchartsTootipIndex = 0;
type EChartsOption = echarts.EChartsOption;
const echarts1 = () => {
var myChart = echarts.init(main1.value);
var option: EChartsOption;
option = {
title: [
{
text: "抽检监测 {score|" + checkMonitoringScore.value + "} 分",
left: "center",
right: "center",
top: "5%",
textStyle: {
color: "#00eaff", // 科技感的荧光蓝
fontSize: 18,
rich: {
score: {
fontSize: 28,
color: "#1890ff",
fontWeight: "bold",
textShadowColor: "gray", // 阴影颜色
textShadowBlur: 5, // 阴影模糊程度
textShadowOffsetX: 1, // 阴影水平偏移
textShadowOffsetY: 2, // 阴影垂直偏移
},
},
},
},
{
text: "主\n要\n问\n题",
textStyle: {
fontSize: 14,
fontWeight: "bold",
color: "#ffffff",
},
textAlign: "left",
textBaseline: "middle",
top: "50%",
},
],
tooltip: {
trigger: "item",
textStyle: {
color: "#00eaff",
},
formatter: function (params: any) {
const buhege = params.value.toFixed(1);
// 自定义 tooltip 内容
return `
<div>
<strong>${params.name}</strong><br/>
总数: ${params.data.count}<br/>
合格数: ${params.data.hege}<br/>
不合格率: ${buhege}%
</div>
`;
},
},
series: <echarts.SeriesOption>[
{
type: "wordCloud",
shape: "circle",
keepAspect: false,
// maskImage: maskImage,
left: "8%",
top: "20%",
width: "90%",
height: "75%",
right: null,
bottom: null,
sizeRange: [12, 20],
rotationRange: [0, 0],
rotationStep: 45,
gridSize: 6,
drawOutOfBound: false,
shrinkToFit: true,
layoutAnimation: true,
textStyle: {
fontFamily: "sans-serif",
fontWeight: "bold",
color: function () {
return echartsColorArray[
Math.floor(Math.random() * echartsColorArray.length)
];
},
},
emphasis: {
// focus: 'self',
textStyle: {
textShadowBlur: 3,
textShadowColor: "#333",
},
},
//data属性中的value值却大权重就却大展示字体就却大
data: checkMonitoringData.value,
},
],
};
option && myChart.setOption(option);
myChart.setOption(option);
// 添加点击事件
myChart.on("click", function (params: any) {
console.log("点击的词云:", params.name);
defaultShow.value = true;
choujianShow.value = false;
//调用接口
setTimeout(() => {
defaultShow.value = false;
tousuShow.value = false;
zhifaShow.value = false;
choujianShow.value = true;
setTimeout(() => {
choujianEchartsPinfa();
}, 500);
}, 100);
// console.log(swiperRef.value, "swiperRef.value");
// console.log(document.getElementById("swiperBox"), "document.getElementById('swiperBox')");
setTimeout(() => {
const swiperBox = document.getElementById("swiperBox");
if (swiperBox) {
swiperBox.style.height = swiperBoxHeight.value;
}
}, 100);
});
};
const nohegelv = (hegelv: number) => {
return 100 - hegelv;
};
const echarts2 = () => {
d3.select(main2.value).html("");
// 定义一组预设的颜色
var colors = [
"RGB(0, 255, 255)",
"RGB(0, 0, 255)",
"RGB(255, 255, 0)",
"RGB(255, 192, 203)",
"RGB(255, 255, 153)",
"RGB(230, 230, 250)",
"RGB(211, 211, 211)",
];
// 创建 SVG 元素
var svg = d3
.select(main2.value)
.append("svg")
.attr("width", windowWidth.value * 0.23)
.attr("height", (windowHeight.value - 100 - 3 * 4) / 3);
// 创建布局
var layout = d3
.pack()
.size([windowWidth.value * 0.23, (windowHeight.value - 100 - 3 * 4) / 2.2])
// .size([300,300])
.padding(40);
// 数据处理
var root: any = d3
.hierarchy({ children: complaintReportData.value })
.sum(function (d: any) {
return d.value;
});
var nodes = layout(root).descendants();
// 选择Tooltip的元素
var tooltip = d3.select("#tooltip");
// 设置文字大小的比例尺
var fontSizeScale = d3
.scaleLinear()
.domain([
0,
d3.max(nodes, function (d: any) {
return d.value;
}),
])
.range([6, 28]); // 根据需要调整文字大小的范围
// 假设在y方向上我们希望缩小为原来的一半
const scaleYFactor = 0.73;
// // 遍历所有节点并应用变换
nodes.forEach((d) => {
// 更新节点的位置以模拟椭圆布局
d.y = d.y * scaleYFactor; // 缩小y坐标
// 可能还需要平移以保持在视窗内居中
// 这里的平移量取决于您的具体需求和初始SVG尺寸
// 下面只是一个例子,可能需要调整
d.y += 20;
});
// 绘制气泡图
svg
.selectAll(".node")
.data(
// nodes
nodes.filter(function (d) {
return !d.children;
})
)
.enter()
.append("circle")
.attr("class", "node")
.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
})
.attr("r", function (d) {
return d.r;
})
.style("fill", function () {
var randomIndex = Math.floor(Math.random() * echartsColorArray.length);
return echartsColorArray[randomIndex];
})
.style("opacity", 0.7)
// 鼠标悬停事件
.on("mousemove", function (event, d: any) {
isTooltipVisible.value = true;
var name = d.data.name.length * 18;
var value = String(d.data.value.length).length * 16;
var x = event.clientX - (Number(name) + Number(value) - 50); // 获取鼠标的 x 坐标并减去tooltip的宽度以将其定位到左侧
var y = event.clientY - 28; // 获取鼠标的 y 坐标并减去一定的值以调整tooltip的垂直位置
tooltip.transition().duration(200).style("opacity", 0.9);
tooltip
.html(d.data.name + "" + d.data.value)
.style("left", x + "px") // 设置 tooltip 的位置为左侧
.style("top", y + "px"); // 设置 tooltip 的垂直位置
})
// 鼠标移出事件
.on("mouseout", function () {
isTooltipVisible.value = false;
})
// 添加点击事件
.on("click", function (event, d: any) {
//右1高亮显示
console.log("点击的气泡图节点:", d.data.name);
// highlightGraphNodeByName(d.data.name);
highlightGraphNodeByName("投诉举报-销售-" + d.data.name);
// 右2显示首发高发频发预警
defaultShow.value = true;
choujianShow.value = true;
setTimeout(() => {
defaultShow.value = false;
choujianShow.value = false;
tousuShow.value = true;
zhifaShow.value = false;
}, 100);
setTimeout(() => {
const swiperBox = document.getElementById("swiperBox");
if (swiperBox) {
swiperBox.style.height = swiperBoxHeight.value;
}
}, 100);
});
// 添加文字标签
svg
.selectAll(".label")
.data(nodes)
.enter()
.append("text")
.attr("class", "label")
.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y;
})
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.style("font-size", function (d: any) {
return fontSizeScale(d.data.value) + "px";
})
.style("fill", "white")
.style("pointer-events", "none")
.text(function (d: any) {
return d.data.name;
});
// 创建标题元素
svg
.append("text")
.attr("class", "title")
.attr("x", 120 + (windowWidth.value * 0.23 - 246) / 2)
.attr("y", 40) // 根据需要调整标题的垂直位置
.attr("text-anchor", "middle")
.style("font-size", "18px")
.style("font-weight", "bold")
.style("fill", "#00eaff")
.text("投诉举报 ")
.append("tspan")
.attr("class", "score")
.style("font-size", "28px")
.style("font-weight", "bold")
.style("fill", "#1890ff")
.style("text-shadow", "1px 1px 2px gray") // 添加阴影效果
.text(complaintReportScore.value)
.append("tspan")
.attr("class", "unit")
.style("font-size", "22px")
.style("font-weight", "bold")
.style("fill", "#00eaff")
.text(" 分");
// 创建副标题元素
var text = ["主", "要", "问", "题"];
for (let index = 0; index < text.length; index++) {
var element = text[index];
svg
.append("text")
.attr("class", "subtitle")
.attr("x", 5) // 根据需要调整副标题的水平位置
.attr(
"y",
((windowHeight.value - 100 - 3 * 4) / 3 - 198) / 2 + 78 + 14 * index
)
// .attr("text-anchor", "middle")
.style("font-size", "14px")
.style("font-weight", "bold")
.style("fill", "#ffffff")
// .attr("transform", "rotate(90, 20, " + 300 / 2 + ")") // 将副标题旋转90度
.text(element);
}
};
const echarts3 = () => {
var myChart = echarts.init(main3.value);
var option: EChartsOption;
option = {
title: [
{
text:
"执法检查 {score|" + lawEnforcementInspectionScore.value + "} 分",
left: "center",
right: "center",
textStyle: {
color: "#00eaff",
fontSize: 18,
rich: {
score: {
fontSize: 28,
color: "#1890ff",
fontWeight: "bold",
textShadowColor: "gray", // 阴影颜色
textShadowBlur: 2, // 阴影模糊程度
textShadowOffsetX: 1, // 阴影水平偏移
textShadowOffsetY: 1, // 阴影垂直偏移
},
},
},
},
{
text: "主\n要\n问\n题",
textStyle: {
color: "#ffffff",
fontSize: 14,
fontWeight: "bold",
},
textAlign: "left",
textBaseline: "middle",
top: "50%",
},
],
label: {
show: true,
formatter: "{b} : {c}",
},
tooltip: {
show: true,
formatter: "{b} : {c}",
},
series: [
{
type: "treemap",
data: lawEnforcementInspectionData.value,
height: "70%",
breadcrumb: { show: false },
nodeClick: false
// width: "100%"
},
],
// visualMap: {
// show: false,
// inRange: {
// color: echartsColorArray,
// },
// },
};
option && myChart.setOption(option);
myChart.setOption(option);
// 添加点击事件
myChart.on("click", function (params: any) {
console.log("点击的树图节点:", params.name);
highlightGraphNodeByName("执法检查-" + params.name);
// 你可以在这里根据需要进行更多操作,比如弹窗、跳转等
defaultShow.value = false;
choujianShow.value = false;
tousuShow.value = false;
zhifaShow.value = true;
setTimeout(() => {
zhifaEchartsPinfa();
}, 500)
});
};
// 初始化时间轴图表
const echarts6_old = async () => {
var myChart = echarts.init(main6.value);
var option: EChartsOption;
const timeValueMap = {
上三个月: -3,
上二个月: -2,
上一个月: -1,
当前月: 0,
下一个月: 1,
下二个月: 2,
下三个月: 3,
};
// 固化虚拟数据
// 完整数据结构
const mockDataSource = [
{
year: 2023,
jurisdiction: "奉贤区",
month: "5", // 基准月
selectedStreet: "",
process: "",
littlemonth: 0,
timeScores: [
{
period: "上三个月" as TimePeriod,
scores: {
real: [87.3, 88.6, 88.7, 92.6, null, null, null],
predict: [null, null, null, 92.6, 93.1, 91.6, 90.7],
},
},
{
period: "上二个月" as TimePeriod,
scores: {
real: [88.6, 88.7, 92.6, 87.3, null, null, null],
predict: [null, null, null, 87.3, 89.6, 91.1, 91.8],
},
},
{
period: "上一个月" as TimePeriod,
scores: {
real: [88.7, 92.6, 87.3, 93.5, null, null, null],
predict: [null, null, null, 93.5, 91.7, 92.2, 93.1],
},
},
{
period: "当前月" as TimePeriod,
scores: {
real: [93.5, 91.3, 92, 94.8, null, null, null],
predict: [null, null, null, 94.8, 95, 96.3, 97.5],
},
},
{
period: "下一个月" as TimePeriod,
scores: {
real: [87.3, 93.5, 91.3, null, null, null, null],
predict: [null, null, 91.3, 92.1, 92.7, 93.1, 94.2],
},
},
{
period: "下二个月" as TimePeriod,
scores: {
real: [93.5, 91.3, null, null, null, null, null],
predict: [null, 91.3, 92.1, 92.7, 93.1, 94.2, 94],
},
},
{
period: "下三个月" as TimePeriod,
scores: {
real: [91.3, null, null, null, null, null, null],
predict: [91.3, 92.1, 92.7, 93.1, 94.2, 94, 96],
},
},
],
},
{
year: 2023,
jurisdiction: "奉贤区",
month: "3", // 基准月
selectedStreet: "",
process: "食品安全检查",
littlemonth: 0,
timeScores: [
{
period: "上三个月" as TimePeriod,
scores: {
real: [87.3, 88.6, 88.7, 92.6, null, null, null],
predict: [null, null, null, 92.6, 93.1, 91.6, 90.7],
},
},
{
period: "上二个月" as TimePeriod,
scores: {
real: [88.6, 88.7, 92.6, 87.3, null, null, null],
predict: [null, null, null, 87.3, 89.6, 91.1, 91.8],
},
},
{
period: "上一个月" as TimePeriod,
scores: {
real: [88.7, 92.6, 87.3, 93.5, null, null, null],
predict: [null, null, null, 93.5, 91.7, 92.2, 93.1],
},
},
{
period: "当前月" as TimePeriod,
scores: {
real: [92.6, 87.3, 93.5, 91.3, null, null, null],
predict: [null, null, null, 91.3, 92.1, 93.1, 93.3],
},
},
{
period: "下一个月" as TimePeriod,
scores: {
real: [87.3, 93.5, 91.3, null, null, null, null],
predict: [null, null, 91.3, 92.1, 92.7, 93.1, 94.2],
},
},
{
period: "下二个月" as TimePeriod,
scores: {
real: [93.5, 91.3, null, null, null, null, null],
predict: [null, 91.3, 92.1, 92.7, 93.1, 94.2, 94],
},
},
{
period: "下三个月" as TimePeriod,
scores: {
real: [91.3, null, null, null, null, null, null],
predict: [91.3, 92.1, 92.7, 93.1, 94.2, 94, 96],
},
},
],
},
{
year: 2023,
jurisdiction: "奉贤区",
month: "5", // 基准月
selectedStreet: "金汇镇",
process: "食品安全检查",
littlemonth: 0,
timeScores: [
{
period: "上三个月" as TimePeriod,
scores: {
real: [87.3, 88.6, 88.7, 92.6, null, null, null],
predict: [null, null, null, 92.6, 93.1, 91.6, 90.7],
},
},
{
period: "上二个月" as TimePeriod,
scores: {
real: [88.6, 88.7, 92.6, 87.3, null, null, null],
predict: [null, null, null, 87.3, 89.6, 91.1, 91.8],
},
},
{
period: "上一个月" as TimePeriod,
scores: {
real: [88.7, 92.6, 87.3, 93.5, null, null, null],
predict: [null, null, null, 93.5, 91.7, 92.2, 93.1],
},
},
{
period: "当前月" as TimePeriod,
scores: {
real: [89.2, 89.2, 92.3, 92.5, null, null, null],
predict: [null, null, null, 92.5, 92.7, 93.1, 93.3],
},
},
{
period: "下一个月" as TimePeriod,
scores: {
real: [87.3, 93.5, 91.3, null, null, null, null],
predict: [null, null, 91.3, 92.1, 92.7, 93.1, 94.2],
},
},
{
period: "下二个月" as TimePeriod,
scores: {
real: [93.5, 91.3, null, null, null, null, null],
predict: [null, 91.3, 92.1, 92.7, 93.1, 94.2, 94],
},
},
{
period: "下三个月" as TimePeriod,
scores: {
real: [91.3, null, null, null, null, null, null],
predict: [91.3, 92.1, 92.7, 93.1, 94.2, 94, 96],
},
},
],
},
{
year: 2023,
jurisdiction: "奉贤区",
month: "5", // 基准月
selectedStreet: "金汇镇",
process: "食品安全检查",
littlemonth: -1,
timeScores: [
{
period: "上三个月" as TimePeriod,
scores: {
real: [87.3, 88.6, 88.7, 92.6, null, null, null],
predict: [null, null, null, 92.6, 93.1, 91.6, 90.7],
},
},
{
period: "上二个月" as TimePeriod,
scores: {
real: [88.6, 88.7, 92.6, 87.3, null, null, null],
predict: [null, null, null, 87.3, 89.6, 91.1, 91.8],
},
},
{
period: "上一个月" as TimePeriod,
scores: {
real: [88.7, 92.6, 87.3, 93.5, null, null, null],
predict: [null, null, null, 93.5, 91.7, 92.2, 93.1],
},
},
{
period: "当前月" as TimePeriod,
scores: {
real: [88.7, 89.2, 89.2, 92.3, null, null, null],
predict: [null, null, null, 92.3, 92.5, 93.1, 93.5],
},
},
{
period: "下一个月" as TimePeriod,
scores: {
real: [87.3, 93.5, 91.3, null, null, null, null],
predict: [null, null, 91.3, 92.1, 92.7, 93.1, 94.2],
},
},
{
period: "下二个月" as TimePeriod,
scores: {
real: [93.5, 91.3, null, null, null, null, null],
predict: [null, 91.3, 92.1, 92.7, 93.1, 94.2, 94],
},
},
{
period: "下三个月" as TimePeriod,
scores: {
real: [91.3, null, null, null, null, null, null],
predict: [91.3, 92.1, 92.7, 93.1, 94.2, 94, 96],
},
},
],
},
{
year: 2023,
jurisdiction: "奉贤区",
month: "3", // 基准月
selectedStreet: "金汇镇",
process: "食品安全检查",
littlemonth: -1,
timeScores: [
{
period: "上三个月" as TimePeriod,
scores: {
real: [87.3, 88.6, 88.7, 92.6, null, null, null],
predict: [null, null, null, 92.6, 93.1, 91.6, 90.7],
},
},
{
period: "上二个月" as TimePeriod,
scores: {
real: [88.6, 88.7, 92.6, 87.3, null, null, null],
predict: [null, null, null, 87.3, 89.6, 91.1, 91.8],
},
},
{
period: "上一个月" as TimePeriod,
scores: {
real: [88.7, 92.6, 87.3, 93.5, null, null, null],
predict: [null, null, null, 93.5, 91.7, 92.2, 93.1],
},
},
{
period: "当前月" as TimePeriod,
scores: {
real: [87.5, 87.9, 88.7, 89.2, null, null, null],
predict: [null, null, null, 89.2, 90.5, 90.9, 91.7],
},
},
{
period: "下一个月" as TimePeriod,
scores: {
real: [87.3, 93.5, 91.3, null, null, null, null],
predict: [null, null, 91.3, 92.1, 92.7, 93.1, 94.2],
},
},
{
period: "下二个月" as TimePeriod,
scores: {
real: [93.5, 91.3, null, null, null, null, null],
predict: [null, 91.3, 92.1, 92.7, 93.1, 94.2, 94],
},
},
{
period: "下三个月" as TimePeriod,
scores: {
real: [91.3, null, null, null, null, null, null],
predict: [91.3, 92.1, 92.7, 93.1, 94.2, 94, 96],
},
},
],
},
{
year: 2023,
jurisdiction: "上海市",
month: "3", // 基准月
selectedStreet: "",
process: "食品安全检查",
littlemonth: -2,
timeScores: [
{
period: "上三个月" as TimePeriod,
scores: {
real: [87.3, 88.6, 88.7, 92.6, null, null, null],
predict: [null, null, null, 92.6, 93.1, 91.6, 90.7],
},
},
{
period: "上二个月" as TimePeriod,
scores: {
real: [88.6, 88.7, 92.6, 87.3, null, null, null],
predict: [null, null, null, 87.3, 89.6, 91.1, 91.8],
},
},
{
period: "上一个月" as TimePeriod,
scores: {
real: [88.7, 92.6, 87.3, 93.5, null, null, null],
predict: [null, null, null, 93.5, 91.7, 92.2, 93.1],
},
},
{
period: "当前月" as TimePeriod,
scores: {
real: [90.8, 91.5, 91.5, 91.7, null, null, null],
predict: [null, null, null, 91.7, 92, 92.1, 92.3],
},
},
{
period: "下一个月" as TimePeriod,
scores: {
real: [87.3, 93.5, 91.3, null, null, null, null],
predict: [null, null, 91.3, 92.1, 92.7, 93.1, 94.2],
},
},
{
period: "下二个月" as TimePeriod,
scores: {
real: [93.5, 91.3, null, null, null, null, null],
predict: [null, 91.3, 92.1, 92.7, 93.1, 94.2, 94],
},
},
{
period: "下三个月" as TimePeriod,
scores: {
real: [91.3, null, null, null, null, null, null],
predict: [91.3, 92.1, 92.7, 93.1, 94.2, 94, 96],
},
},
],
},
{
year: 2023,
jurisdiction: "上海市",
month: "5", // 基准月
selectedStreet: "",
process: "食品安全检查",
littlemonth: -2,
timeScores: [
{
period: "上三个月" as TimePeriod,
scores: {
real: [87.3, 88.6, 88.7, 92.6, null, null, null],
predict: [null, null, null, 92.6, 93.1, 91.6, 90.7],
},
},
{
period: "上二个月" as TimePeriod,
scores: {
real: [88.6, 88.7, 92.6, 87.3, null, null, null],
predict: [null, null, null, 87.3, 89.6, 91.1, 91.8],
},
},
{
period: "上一个月" as TimePeriod,
scores: {
real: [88.7, 92.6, 87.3, 93.5, null, null, null],
predict: [null, null, null, 93.5, 91.7, 92.2, 93.1],
},
},
{
period: "当前月" as TimePeriod,
scores: {
real: [90.8, 91.7, 91.9, 92.2, null, null, null],
predict: [null, null, null, 92.2, 92.5, 92.9, 93.1],
},
},
{
period: "下一个月" as TimePeriod,
scores: {
real: [87.3, 93.5, 91.3, null, null, null, null],
predict: [null, null, 91.3, 92.1, 92.7, 93.1, 94.2],
},
},
{
period: "下二个月" as TimePeriod,
scores: {
real: [93.5, 91.3, null, null, null, null, null],
predict: [null, 91.3, 92.1, 92.7, 93.1, 94.2, 94],
},
},
{
period: "下三个月" as TimePeriod,
scores: {
real: [91.3, null, null, null, null, null, null],
predict: [91.3, 92.1, 92.7, 93.1, 94.2, 94, 96],
},
},
],
},
];
const mockDataSou = mockDataSource.find(
(item) =>
item.month === monthValue.value &&
item.jurisdiction === districtValue.value &&
item.selectedStreet === selectedStreet.value &&
item.littlemonth === littlemonth.value
);
// console.log('monthValue.value', monthValue.value);
// console.log('districtValue.value', districtValue.value);
// console.log('selectedStreet.value', selectedStreet.value);
// console.log('littlemonth.value', littlemonth.value);
// console.log('mockDataSou11', mockDataSou,"222",mockDataSource);
console.log("mockDataSou", mockDataSou);
const mockData = mockDataSou.timeScores.map((item) => item.scores);
console.log("mockData", mockData);
option = {
baseOption: {
timeline: {
axisType: "category",
autoPlay: true,
playInterval: 2000,
data: [
"上三个月",
"上二个月",
"上一个月",
"当前月",
"下一个月",
"下二个月",
"下三个月",
],
currentIndex: 3,
label: { formatter: (s: string) => s },
},
tooltip: {
trigger: "axis",
textStyle: {
fontSize: 10,
},
axisPointer: {
lineStyle: {
color: "#57617B",
},
},
extraCssText: "max-width: 200px; max-height: 150px; overflow-y: auto;",
},
legend: {
data: ["真实分", "预测分"],
selected: { 预测分: true, 真实分: true },
top: "30",
textStyle: {
fontSize: 10,
color: "#fff",
},
itemGap: 20,
},
calculable: true,
grid: {
top: 80,
bottom: 100,
left: "10%",
right: "5%",
},
xAxis: {
show: false, // 关键设置隐藏X轴
type: "category",
boundaryGap: false,
data: [], // 保留空数据(或保持原有月份数据但不影响显示)
},
yAxis: {
type: "value",
min: 70,
max: 100,
interval: 5,
axisLabel: { formatter: "{value} 分" },
splitLine: {
lineStyle: {
color: "rgba(255,255,255,.5)",
},
},
},
series: [
{
name: "真实分",
smooth: true,
type: "line",
symbol: "circle",
symbolSize: 8,
lineStyle: { width: 2 },
color: "#5470C6",
},
{
name: "预测分",
smooth: true,
type: "line",
symbol: "circle",
symbolSize: 8,
lineStyle: {
width: 2,
type: "dashed",
},
color: "#EE6666",
},
],
},
options: mockData.map((data) => ({
series: [{ data: data.real }, { data: data.predict }],
})),
};
myChart.setOption(option);
// 保留时间轴变化监听
myChart.on("timelinechanged", (params) => {
const currentName = option.baseOption.timeline.data[params.currentIndex];
const clickValue = timeValueMap[currentName];
littlemonth.value = clickValue;
});
};
const echarts6 = () => {
var myChart = echarts.init(main6.value);
var option: EChartsOption;
option = {
title: [
{
text: "行政处罚 {score|" + istrativeSanctionScore.value + "} 分",
top: "3%",
left: "center",
right: "center",
textStyle: {
fontSize: 18,
color: "#00eaff",
rich: {
score: {
fontSize: 28,
color: "#1890ff",
fontWeight: "bold",
textShadowColor: "gray", // 阴影颜色
textShadowBlur: 2, // 阴影模糊程度
textShadowOffsetX: 1, // 阴影水平偏移
textShadowOffsetY: 1, // 阴影垂直偏移
},
},
},
},
{
text: "主\n要\n问\n题",
textStyle: {
color: "#ffffff",
fontSize: 14,
fontWeight: "bold",
},
textAlign: "left",
textBaseline: "middle",
top: "50%",
},
],
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)",
},
legend: {
// top: "5%",
left: "center",
bottom: "1%",
textStyle: {
color: "#00eaff",
},
},
series: [
{
name: "行政处罚种类占比",
type: "pie",
radius: ["28%", "50%"],
avoidLabelOverlap: true,
top: "-5%",
label: {
color: "#00eaff",
show: true,
formatter: "{d}%",
position: "inner",
},
minShowLabelAngle: 1,
emphasis: {
label: {
color: "#00eaff",
show: true,
fontSize: 15,
fontWeight: "bold",
},
},
labelLine: {
show: false,
},
data: istrativeSanctionData.value,
// color: echartsColorArray,
},
],
};
option && myChart.setOption(option);
myChart.setOption(option);
};
const echartsYibiao = () => {
var myChart = echarts.init(yibiao.value);
var option: EChartsOption;
const szqxName = ref("上海市");
if (districtValue.value) {
szqxName.value = districtValue.value;
}
option = {
// 标题
title: [
{
left: "32%",
top: "75%",
bottom: 100,
text: szqxName.value + "综合指标",
textStyle: {
fontWeight: "bold",
fontSize: 18,
color: "#00eaff",
},
},
],
tooltip: {
show: true,
},
series: [
{
// 蓝色刻度
type: "gauge", // 图表类型为仪表盘
center: ["50%", "55%"], // 仪表盘的中心位置,默认全局居中
radius: "80%", // 仪表盘的半径大小
splitNumber: 10, // 刻度数量
min: 0, // 最小值
max: 100, // 最大值
startAngle: 200, // 起始角度
endAngle: -20, // 结束角度
clockwise: true, // 是否顺时针
axisLine: {
show: false, // 是否显示仪表盘轴线
lineStyle: {
width: 1, // 轴线宽度
shadowBlur: 0, // 轴线阴影模糊大小
color: [
[1, "#1e3a8a"], // 轴线颜色
],
},
},
axisTick: {
show: true, // 是否显示刻度
lineStyle: {
color: "#00eaff", // 刻度颜色
width: 0.5, // 刻度宽度
},
length: -5, // 刻度长度
splitNumber: 10, // 刻度分割段数
},
splitLine: {
show: true, // 是否显示分隔线
length: -8, // 分隔线长度
lineStyle: {
width: 0.8, // 刻度宽度
color: "#00eaff", // 分隔线颜色
},
},
axisLabel: {
distance: -10, // 标签与轴线的距离
color: "#00eaff", // 标签颜色
fontSize: 8, // 标签字体大小
fontWeight: "bold", // 标签字体粗细
},
pointer: {
// 仪表盘指针
show: false, // 是否显示指针
},
detail: {
show: false, // 是否显示详情
},
data: [
{
name: "", // 数据名称
value: 100, // 数据值
},
],
},
{
// 白色线条
startAngle: 200, // 仪表盘的起始角度,单位为度
endAngle: -20, // 仪表盘的结束角度,单位为度
type: "gauge", // 图表类型为仪表盘
center: ["50%", "55%"], // 仪表盘的中心位置,相对于容器的百分比
radius: "65%", // 仪表盘的半径大小,相对于容器的百分比
min: 0, // 仪表盘的最小值
max: 100, // 仪表盘的最大值
splitNumber: 0, // 刻度数量设置为0表示不显示刻度
axisLine: {
// 坐标轴线
lineStyle: {
color: [
[0.66, "#dddddd"], // 轴线颜色的渐变0.66处颜色为#dddddd
[1, "#dddddd"], // 轴线颜色的渐变1处颜色为#dddddd
], // 属性lineStyle控制线条样式
width: 2, // 轴线宽度
},
},
axisLabel: {
// 坐标轴小标记
fontWeight: "bolder", // 字体粗细
fontSize: 20, // 字体大小
color: "rgba(30,144,255,0)", // 字体颜色,设置为透明
},
splitLine: {
// 分隔线
length: 10, // 分隔线长度
lineStyle: {
// 属性lineStyle详见lineStyle控制线条样式
width: 0, // 分隔线宽度
color: "#444", // 分隔线颜色
},
},
},
{
// 蓝色线条
startAngle: 200, // 仪表盘的起始角度,单位为度
endAngle: -20, // 仪表盘的结束角度,单位为度
type: "gauge", // 图表类型为仪表盘
center: ["50%", "55%"], // 仪表盘的中心位置,相对于容器的百分比
radius: "71%", // 仪表盘的半径大小,相对于容器的百分比
min: 0, // 仪表盘的最小值
max: 100, // 仪表盘的最大值
splitNumber: 0, // 刻度数量设置为0表示不显示刻度
axisLine: {
// 坐标轴线
lineStyle: {
color: [
[0.66, "#1e3a8a"], // 轴线颜色的渐变0.66处颜色为#dddddd
[1, "#1e3a8a"], // 轴线颜色的渐变1处颜色为#dddddd
], // 属性lineStyle控制线条样式
width: 1, // 轴线宽度
},
},
axisLabel: {
// 坐标轴小标记
fontWeight: "bolder", // 字体粗细
fontSize: 16, // 字体大小
color: "rgba(30,144,255,0)", // 字体颜色,设置为透明
},
splitLine: {
// 分隔线
length: 10, // 分隔线长度
lineStyle: {
// 属性lineStyle详见lineStyle控制线条样式
width: 0, // 分隔线宽度
color: "#444", // 分隔线颜色
},
},
detail: {
// 详情显示
show: false, // 是否显示详情
},
},
{
// 指针和分数
name: "总分", // 图表名称
type: "gauge", // 图表类型:仪表盘
startAngle: 200, // 仪表盘的起始角度,单位为度
endAngle: -20, // 仪表盘的结束角度,单位为度
radius: "40%", // 仪表盘的半径大小,相对于容器宽度的百分比
center: ["50%", "55%"], // 仪表盘的中心位置,相对于容器的百分比,默认全局居中
min: 0, // 仪表盘的最小值
max: 100, // 仪表盘的最大值
axisLine: {
// 坐标轴线配置
show: false, // 是否显示坐标轴线
lineStyle: {
width: 25, // 坐标轴线的宽度
shadowBlur: 0, // 坐标轴线的阴影模糊程度
color: [
[0.2, "#00FAFC"], // 轴线颜色的渐变0.2处颜色为#00FAFC浅蓝色
[0.4, "#3BD542"], // 轴线颜色的渐变0.4处颜色为#3BD542绿色
[0.6, "#E3F424"], // 轴线颜色的渐变0.6处颜色为#E3F424黄色
[0.8, "#7E48DA"], // 轴线颜色的渐变0.8处颜色为#7E48DA紫色
[1, "#E531A8"], // 轴线颜色的渐变1处颜色为#E531A8粉红色
],
},
},
axisTick: {
// 刻度配置
show: false, // 是否显示刻度
},
splitLine: {
// 分隔线配置
show: false, // 是否显示分隔线
},
axisLabel: {
// 坐标轴标签配置
show: false, // 是否显示坐标轴标签
},
pointer: {
// 指针配置
show: true, // 是否显示指针
width: 4, // 指针宽度
length: 50, // 指针长度
// color: '#00eaff', // 指针颜色
},
detail: {
// 详情配置
show: true, // 是否显示详情
offsetCenter: [0, "80%"], // 详情文本相对于中心点的位置偏移x方向无偏移y方向向下偏移80%
fontSize: 20, // 详情文本字体大小(加倍)
color: "#00eaff", // 改为白色
fontWeight: "bolder", // 加粗显示
// textShadow: '0 0 5px rgba(0, 149, 255, 0.3)', // 添加阴影使文字在透明背景上更清晰
},
itemStyle: {
// 项目样式配置
color: "#00eaff", // 项目的默认颜色
},
data: [
{
// 数据配置
value: AllCourse.value, // 当前数据值
},
],
},
],
};
option && myChart.setOption(option);
myChart.setOption(option);
};
// 时间轴配置
const initTimelineChart = () => {
var myChart = echarts.init(timeline.value);
var option: EChartsOption;
const timeValueMap = {
上三个月: -3,
上二个月: -2,
上一个月: -1,
当前月: 0,
下一个月: 1,
下二个月: 2,
下三个月: 3,
// 若添加新的时间点,在此处添加对应映射
};
option = {
baseOption: {
timeline: {
axisType: "category",
autoPlay: false,
playInterval: 2000,
data: [
"上三个月",
"上二个月",
"上一个月",
"当前月",
"下一个月",
"下二个月",
"下三个月",
],
currentIndex: 3, // 默认选中第四个时间点
label: {
formatter: function (s) {
return s;
},
},
},
tooltip: {},
legend: {
left: "right",
data: [],
selected: {},
},
calculable: true,
grid: {
top: 80,
bottom: 100,
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
label: {
show: true,
formatter: function (params) {
return params.value.replace("\n", "");
},
},
},
},
},
series: [
// 这里可以根据实际需求添加系列
],
},
options: [
// 每个时间点对应的配置,可以根据实际需求添加
],
};
myChart.setOption(option);
// 监听时间轴的时间点变化事件
myChart.on("timelinechanged", (params) => {
const currentName = option.baseOption.timeline.data[params.currentIndex];
const clickValue = timeValueMap[currentName];
littlemonth.value = clickValue;
SelectBlur();
});
// 点击事件处理
myChart.on("click", (params: any) => { });
};
onMounted(() => {
console.log("onMounted开始执行");
getWindowWH();
getSixTableValue();
initTimelineChart();
echarts6();
initRelationshipChart();
// 获取总分数
// getAllData(
// yearValue.value,
// monthValue.value,
// stageValue.value,
// littlemonth.value
// )
getStreetAllData(
monthValue.value,
stageValue.value,
districtValue.value,
littlemonth.value
).then((res) => {
if (res.data.msg == "success") {
// AllCourse.value = res.data.data
// .filter((item: { name: string }) => item.name === districtValue.value)[0].value;
// echartsYibiao();
if (districtValue.value == "上海市") {
// 计算所有区域的平均值
const allDistricts = res.data.data.filter((item: { name: string }) => item.name !== "上海市");
const totalValue = allDistricts.reduce((sum: number, item: { value: number }) => sum + item.value, 0);
AllCourse.value = Number((totalValue / allDistricts.length).toFixed(1));
echartsYibiao();
} else {
AllCourse.value = res.data.data.filter(
(item: { name: string }) => item.name === districtValue.value
)[0].value;
echartsYibiao();
}
console.log("AllCourse", AllCourse.value, "======================");
}
});
LoadStreetAllData();
});
const LoadStreetAllData = () => {
// 获取每个街道的分数
getStreetAllData(
monthValue.value,
stageValue.value,
districtValue.value,
littlemonth.value
).then((res) => {
if (res.data.msg == "success") {
const courseValue = assignValuesToZonesStreet(res.data.data);
btnLoadJson(courseValue);
//刷新数据
RefreshData();
}
});
};
//加载地图列表信息,第一个网络访问方法
const lstMapLayout = ref([]);
const LoadMapLayoutList = () => {
getMapLayoutList(curId).then((res) => {
lstMapLayout.value = res.data;
// console.log("lstMapLayout", lstMapLayout);
});
};
const optMaps = ref([]);
const LoadOptMaps = () => {
loadMapOptions(curId).then((res) => {
optMaps.value = res.data.data;
// console.log("optMaps", optMaps.value);
});
};
const curSel = reactive({
name: "",
value: "",
});
//当前新增的名字
const curAddName = ref("");
//删除历史数据
const btnDelData = () => {
ElMessage({
type: "success",
message: `删除编号` + curSel.value + "的设计内容",
});
var dRet = removeMapLayout(curSel.value);
// console.log("删除返回", dRet);
//重新加载所有的信息
LoadAllDatas();
};
const LoadAllDatas = () => {
//获取当前所有的下拉菜单数据
getMapLayoutList(curId).then((res) => {
lstMapLayout.value = res.data;
// console.log("lstMapLayout", lstMapLayout);
});
//lstMapLayout.value=getMapLayoutList.data;
//获取当前所有的地图配置信息
loadMapOptions(curId).then((res) => {
optMaps.value = res.data.data;
// console.log("optMaps", optMaps.value);
});
//optMaps.value=loadMapOptions.data.data;
};
//加载当前的数据
const btnLoadData = (sel: any) => {
var dJson = JSON.parse(
(
Enumerable.from((lstMapLayout.value as any).data)
.where(function (x: any) {
return x.id == curSel.value;
})
.first() as any
).layoutdata
);
basicOptions.backgroundColor = dJson.Options.backgroundColor;
basicOptions.barTextColor = dJson.Options.barTextColor;
basicOptions.barTitle = dJson.Options.barTitle;
basicOptions.colorRange = dJson.Options.colorRange;
basicOptions.isPresetColorRange = dJson.Options.isPresetColorRange;
basicOptions.isSqrt = dJson.Options.isSqrt;
basicOptions.radioColorType = dJson.Options.radioColorType;
curLayout.ChongMing = dJson.ChongMing;
curLayout.PuDong = dJson.PuDong;
curLayout.BaoShan = dJson.BaoShan;
curLayout.JiaDing = dJson.JiaDing;
curLayout.QingPu = dJson.QingPu;
curLayout.JinShan = dJson.JinShan;
curLayout.FengXian = dJson.FengXian;
curLayout.SongJiang = dJson.SongJiang;
curLayout.MinHang = dJson.MinHang;
curLayout.XuHui = dJson.XuHui;
curLayout.ChangNing = dJson.ChangNing;
curLayout.PuTuo = dJson.PuTuo;
curLayout.JingAn = dJson.JingAn;
curLayout.HongKou = dJson.HongKou;
curLayout.HuangPu = dJson.HuangPu;
curLayout.YangPu = dJson.YangPu;
curLayout.Bar = dJson.Bar;
// console.log("柱状图", dJson.Bar);
btnLoad();
};
//获取浏览器可视化页面的宽高
const getWindowWH = () => {
//本地浏览器可视长宽1920*960
// console.log("浏览器宽度", window.innerWidth);
// console.log("浏览器高度", window.innerHeight);
windowHeight.value = window.innerHeight;
windowWidth.value = window.innerWidth;
// // console.log((windowHeight.value - 62 - 3 * 4) / 3 + 2 * 4, "-------------------------");
// 设置上下两个盒子间隙
const topBottomGap = 10;
//顶部空隙
const topGap = 52;
// 底部空隙
const bottomGap = 15;
// 左右空隙
const leftRightGap = 100;
// 最外层左右空隙
const leftRightGap1 = 20;
//单个盒子的高度
const mainHeight =
(windowHeight.value - 2 * topBottomGap - topGap - bottomGap) / 3;
// 单个盒子的宽度
const mainWidth = windowWidth.value * 0.25;
// 中间盒子的宽度
const centerWidth =
windowWidth.value - 2 * (leftRightGap1 + mainWidth + leftRightGap);
main1.value.style.left = leftRightGap1 + "px";
main1.value.style.width = mainWidth + "px";
main1.value.style.height = mainHeight + "px";
main1.value.style.top = 0 + "px";
main2.value.style.left = leftRightGap1 + "px";
main2.value.style.position = "absolute";
main2.value.style.width = mainWidth + "px";
main2.value.style.height = mainHeight + "px";
main2.value.style.top = mainHeight + topBottomGap + "px";
main3.value.style.left = leftRightGap1 + "px";
main3.value.style.width = mainWidth + "px";
main3.value.style.height = mainHeight + "px";
main3.value.style.top = 2 * mainHeight + 2 * topBottomGap + "px";
main4.value.style.left = windowWidth.value - mainWidth - leftRightGap1 + "px";
main4.value.style.position = 'absolute';
main4.value.style.width = mainWidth + "px";
main4.value.style.height = 0.4 * mainHeight + "px";
main4.value.style.top = 0 + "px";
main5.value.style.left = windowWidth.value - mainWidth - leftRightGap1 + "px";
main5.value.style.position = "absolute";
main5.value.style.width = mainWidth + "px";
main5.value.style.height = 1.6 * mainHeight + "px";
main5.value.style.top = 0.4 * mainHeight + topBottomGap + "px";
swiperBoxHeight.value = main5.value.style.height;
main6.value.style.left = windowWidth.value - mainWidth - leftRightGap1 + "px";
main6.value.style.width = mainWidth + "px";
main6.value.style.height = mainHeight + "px";
main6.value.style.top = main3.value.style.top;
main9.value.style.left = main6.value.style.left;
main9.value.style.width = main6.value.style.width;
main9.value.style.height = main6.value.style.height;
main9.value.style.top = main6.value.style.top;
// centerBorder.value.style.width = centerWidth + "px";
// centerBorder.value.style.height = windowHeight.value * 0.7 + "px";
// centerBorder.value.style.left = windowWidth.value * 0.5 + "px";
// centerBorder.value.style.top = windowHeight.value * 0.5 + "px";
// centerBorder.value.style.transform = "translate(-50%, -50%)";
timeline.value.style.width = windowWidth.value * 0.47 + "px";
};
//获取六个表的值+获取二级指标的值
const getSixTableValue = () => {
//抽检监测表格1表
getCheckMonitoringValueStreet(
monthValue.value,
stageValue.value,
districtValue.value,
selectedStreet.value,
littlemonth.value
).then((res) => {
if (res.data.msg == "success") {
checkMonitoringScore.value = res.data.score;
checkMonitoringData.value = res.data.data.map(
(item: {
zongjianshu: any;
hegejianshu: any;
sp_dl: string;
hegelv: number;
}) => ({
name: item.sp_dl,
value: nohegelv(item.hegelv),
hege: item.hegejianshu,
count: item.zongjianshu,
})
);
echarts1();
}
});
// //投诉举报表格2表
getComplaintReportValueStreet(
monthValue.value,
stageValue.value,
districtValue.value,
selectedStreet.value,
littlemonth.value
).then((res) => {
if (res.data.msg == "success") {
complaintReportScore.value = res.data.score;
complaintReportData.value = res.data.data.map(
(item: { tousuqingxin: string; tousushu: number }) => ({
name: item.tousuqingxin,
value: item.tousushu,
})
);
echarts2();
}
});
//执法检查表格3表
getLawEnforcementInspectionValueStreet(
monthValue.value,
stageValue.value,
districtValue.value,
selectedStreet.value,
littlemonth.value
).then((res) => {
if (res.data.msg == "success") {
lawEnforcementInspectionScore.value = res.data.score;
lawEnforcementInspectionData.value = res.data.data.map(
(item: { wentiqingxin: string; wentishu: number }) => ({
name: item.wentiqingxin,
value: item.wentishu,
})
);
echarts3();
}
});
//行政处罚表格6表
getIstrativeSanctionValueStreet(monthValue.value, stageValue.value, districtValue.value, selectedStreet.value, littlemonth.value).then(
(res) => {
if (res.data.msg == "success") {
istrativeSanctionScore.value = res.data.score;
istrativeSanctionData.value = res.data.data.map(
(item: { chufaqinxing: string; chufaqiye: number }) => ({
name: item.chufaqinxing,
value: item.chufaqiye,
})
);
echarts6();
}
}
);
// 获取二级指标的值
getSecondLevelMetricValues(districtValue.value).then((res) => {
if (res.data.msg == "success") {
for (let index = 0; index < res.data.data.length; index++) {
const element = res.data.data[index];
switch (element.zb) {
case "市民满意度":
SecondLevelMetricValues.value.citizenSatisfaction = element.score;
break;
case "市民知晓度":
SecondLevelMetricValues.value.citizenAwareness = element.score;
break;
case "食物中毒发生率":
SecondLevelMetricValues.value.foodPoisoningIncidence = element.score;
break;
case "企业管理体系认证率":
SecondLevelMetricValues.value.certificationRate = element.score;
break;
default:
break;
}
}
}
});
};
//将各个区的分数赋到对应的变量上
const assignValuesToZones = (courseValue: any[]) => {
const a: DistinguishValueInterface = {};
for (let index = 0; index < courseValue.length; index++) {
const name = courseValue[index].name;
const course: any = courseValue[index].score;
switch (name) {
case "崇明区":
a.ChongMing = course;
break;
case "浦东新区":
a.PuDong = course;
break;
case "宝山区":
a.BaoShan = course;
break;
case "嘉定区":
a.JiaDing = course;
break;
case "青浦区":
a.QingPu = course;
break;
case "金山区":
a.JinShan = course;
break;
case "奉贤区":
a.FengXian = course;
break;
case "松江区":
a.SongJiang = course;
break;
case "闵行区":
a.MinHang = course;
break;
case "徐汇区":
a.XuHui = course;
break;
case "长宁区":
a.ChangNing = course;
break;
case "普陀区":
a.PuTuo = course;
break;
case "静安区":
a.JingAn = course;
break;
case "虹口区":
a.HongKou = course;
break;
case "黄浦区":
a.HuangPu = course;
break;
case "杨浦区":
a.YangPu = course;
break;
}
}
return a;
};
// 离开焦点触发
const SelectBlur = () => {
getSixTableValue();
echarts6();
// 获取总分数
// getAllData(
// yearValue.value,
// monthValue.value,
// stageValue.value,
// littlemonth.value
// )
getStreetAllData(
monthValue.value,
stageValue.value,
districtValue.value,
littlemonth.value
).then((res) => {
if (res.data.msg == "success") {
if (districtValue.value == "上海市") {
// 计算所有区域的平均值
const allDistricts = res.data.data.filter((item: { name: string }) => item.name !== "上海市");
const totalValue = allDistricts.reduce((sum: number, item: { value: number }) => sum + item.value, 0);
AllCourse.value = Number((totalValue / allDistricts.length).toFixed(1));
echartsYibiao();
} else {
AllCourse.value = res.data.data.filter(
(item: { name: string }) => item.name === districtValue.value
)[0].value;
echartsYibiao();
}
console.log("AllCourse", AllCourse.value, "======================");
}
});
LoadStreetAllData();
};
//获取cookie
const getCookie = () => {
let reg = RegExp("Admin-Token" + "=([^;]+)");
let arr = document.cookie.match(reg);
if (arr) {
return arr[1];
} else {
return "没有获取到该cookie";
}
};
const getRandomNumber = (min: number, max: number) => {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
// 设置白鸽位置
const setDovePositon = () => {
debugger;
const containerTitle = document.getElementById("containerTitle");
const doveNumbers = getRandomNumber(5, 8);
for (let index = 0; index < doveNumbers; index++) {
const dove = document.createElement("img");
dove.src = "/public/image/dove.png";
dove.classList.add("dove");
dove.style.left =
getRandomNumber(window.innerWidth * 0.15, window.innerWidth * 0.85) +
"px";
dove.style.top = getRandomNumber(0, 52) + "px";
dove.style.width = getRandomNumber(64, 128) + "px";
containerTitle?.appendChild(dove);
}
};
interface ScoreInterval {
min: number;
max: number;
color: string;
}
// 定义区间
let scoreIntervals: ScoreInterval[] = [
{ min: 0, max: 24, color: "#60a5fa" },
{ min: 25, max: 50, color: "#3b82f6" },
{ min: 51, max: 75, color: "#1d4ed8" },
{ min: 75, max: 100, color: "#1e3a8a" },
];
interface AllScoreVO {
name: string;
street: string;
value: number;
xzcf: number;
xxzs: number;
tsjb: number;
zfjc: number;
cckh: number;
cjjc: number;
zhx: number;
score: number;
weight_add: number;
}
//街道成绩
// let streetScore = [
// { name: '海湾镇', value: 38 },
// { name: '金汇镇', value: 90 },
// { name: '庄行镇', value: 90 },
// { name: '上海市奉贤区海湾旅游区', value: 90 },
// { name: '青村镇', value: 90 },
// { name: '西渡街道', value: 90 },
// { name: '四团镇', value: 90 },
// { name: '金海街道', value: 90 },
// { name: '奉浦街道', value: 90 },
// { name: '奉城镇', value: 90 },
// { name: '柘林镇', value: 90 },
// { name: '上海海港综合经济开发区', value: 90 },
// { name: '南桥镇', value: 90 }
// // 省略其他数据
// ];
//街道成绩
let streetScore = [
{ name: "华阳路街道", value: 38 },
{ name: "金汇镇", value: 90 },
{ name: "程家桥街道", value: 90 },
{ name: "新泾镇", value: 90 },
{ name: "新华路街道", value: 90 },
{ name: "北新泾街道", value: 90 },
{ name: "江苏路街道", value: 90 },
{ name: "周家桥街道", value: 90 },
{ name: "天山路街道", value: 90 },
{ name: "仙霞新村街道", value: 90 },
// 省略其他数据
];
//街道成绩赋值
const assignValuesToZonesStreet = (streetScoreVO: AllScoreVO[]) => {
const result: DistinguishValueInterface = {};
//将结果保存到街道成绩中
console.log("streetScoreVO00000000000000", streetScoreVO);
if (districtValue.value == "上海市") {
streetScore = streetScoreVO.map((item) => {
result[item.name] = item.value;
return {
name: item.name,
value: item.value,
};
});
} else {
streetScore = streetScoreVO.map((item) => {
result[item.street] = item.value;
return {
name: item.street,
value: item.value,
};
});
}
// 对 streetScore 进行从大到小排序
streetScore = streetScore.sort((a, b) => a.value - b.value);
//设定四个区间
// // 计算最大值和最小值
const maxScore = Math.max(...streetScore.map((item) => item.value));
const minScore = Math.min(...streetScore.map((item) => item.value));
// 计算区间
const interval = (maxScore - minScore) / 4;
// 定义区间
scoreIntervals = [
{ min: minScore, max: minScore + interval, color: "#60a5fa" },
{
min: minScore + interval + 0.01,
max: minScore + 2 * interval,
color: "#3b82f6",
},
{
min: minScore + 2 * interval + 0.01,
max: minScore + 3 * interval,
color: "#1d4ed8",
},
{ min: minScore + 3 * interval + 0.01, max: maxScore, color: "#1e3a8a" },
];
initStreetChart();
return streetScore;
};
const selectedStreet = ref<string>("");
// 初始化街道地图
// aaaaaaaaaaaaaaaaaa
const initStreetChart = () => {
// 先获取之前的 ECharts 实例并销毁
const streetMapElement = document.getElementById(
"streetMapEcharts"
) as HTMLElement;
const existingChart = echarts.getInstanceByDom(streetMapElement);
if (existingChart) {
existingChart.dispose();
}
shangHaiJieDao as GeoJSONSourceInput;
shangHaiXiaQu as GeoJSONSourceInput;
// console.log('shangHaiJieDao', shangHaiJieDao);
// 过滤 geoJson 数组,只保留当前请求的街道
//根据请求结果包含的街道决定显示的街道
//拿出请求结果包含全部街道的名字
const streetNames = streetScore.map((item) => item.name);
//根据请求结果包含的街道决定显示的街道
let filteredFeatures;
if (districtValue.value == "上海市") {
// console.log('streetNames111111111', streetNames);
filteredFeatures = shangHaiXiaQu.features
.filter((feature: any) => streetNames.includes(feature.properties.name))
.map((feature: any) => ({ ...feature, type: "Feature" }));
// console.log('filteredFeatures1111111111', filteredFeatures);
} else {
// console.log('streetNames22222222222222', streetNames);
filteredFeatures = shangHaiJieDao.features
.filter((feature: any) => streetNames.includes(feature.properties.name))
.map((feature: any) => ({ ...feature, type: "Feature" }));
// console.log('filteredFeatures2222222222222', filteredFeatures);
}
// 创建新的 GeoJSONSourceInput 对象
const nowGeoJson: GeoJSONSourceInput = {
type: "FeatureCollection",
features: filteredFeatures,
};
// 注册地图
echarts.registerMap("JiedaoMap", nowGeoJson);
// 初始化地图
const streetChart = echarts.init(
document.getElementById("streetMapEcharts") as HTMLElement
);
console.log("streetScore", streetScore);
const streetOption = {
//网格布局
grid: {
right: "100%",
top: "10%",
// bottom: "10%",
width: "20%",
left: "12%", // 调整此属性以向右移动柱状图
},
//区间分数
visualMap: {
pieces: scoreIntervals.map((interval) => ({
gte: parseFloat(interval.min.toFixed(1)),
lte: parseFloat(interval.max.toFixed(1)),
color: interval.color,
})),
show: true, // 视觉映射控件
//定位区间
left: "83%", // 距离容器左侧 10%
top: "5%", // 垂直居中
orient: "vertical", // 垂直方向
textStyle: {
color: "#dbeafe",
fontSize: 12, // 设置字体大小
},
},
series: [
{
type: "map", // 图表类型为地图
map: "JiedaoMap", // 使用注册的地图名称
geoIndex: 10, // 地理坐标系组件的索引
zoom: districtValue.value === "上海市" ? 1.2 : 0.9, // 地图缩放等级
// left: '18%', // 距离左侧10%
// top: '15%', //
left: "center",
top: districtValue.value === "上海市" ? "12%" : "15%",
right: "auto",
bottom: "auto",
// center: [121.52, 30.94],
showLegendSymbol: false, // 不显示图例标记
label: {
normal: {
show: true, // 正常状态下显示标签
formatter: function (params: { name: any; data: { value: any } }) {
const name = params.name;
const value = params.data ? params.data.value : "";
return `${name}\n${value}`;
},
textStyle: {
color: "#fff", // 标签文字颜色
fontSize: 11, // 标签文字大小
},
},
emphasis: {
show: true,
textStyle: { color: "#fff" },
}, // 高亮状态下不显示标签,文本颜色为白色
},
roam: false, // 关闭鼠标缩放和平移漫游
itemStyle: {
normal: { areaColor: "#031525", borderColor: "#FFFFFF" }, // 正常状态下区域颜色和边界颜色
emphasis: { areaColor: "#2B91B7" }, // 高亮状态下区域颜色
},
animation: false, // 关闭动画
data: streetScore, // 地图数据
},
{
type: "bar",
data: streetScore.map((item) => 0),
barWidth: 10, // 设置柱子最大宽度为10px
barMaxWidth: 0, // 设置柱子最大宽度为0
barMinWidth: 0, // 设置柱子最小宽度为0
// 隐藏柱子核心配置
itemStyle: {
show: false,
color: "#dbeafe", //
borderColor: "transparent", // 同步隐藏边框
},
label: {
show: false,
position: "right",
color: "#dbeafe",
fontSize: 15,
distance: 10,
formatter: (params: { dataIndex: number }) =>
streetScore[params.dataIndex].value,
// 关键修复:强制标签显示
overflow: "truncate", // 文本溢出处理
// height: 50, // 设置标签最大高度
width: 50, // 设置标签最大宽度
align: "left", // 左对齐保证位置
},
},
],
xAxis: {
type: "value",
position: "top",
show: false,
// splitLine: { show: false },
// axisLabel: { show: false },
// axisLine: { show: false },
// axisTick: { show: false }
},
yAxis: {
show: false,
type: "category",
data: streetScore.map((item) => item.name),
axisLabel: {
color: "#dbeafe", // 设置统一的颜色
formatter: function (value: string) {
if (value === "上海市奉贤区海湾旅游区") {
return "上海...旅游区";
} else if (value === "上海海港综合经济开发区") {
return "上海...开发区";
}
return value;
},
},
boundaryGap: true,
axisLine: { show: true },
axisTick: { show: true },
},
};
streetChart.setOption(streetOption);
// 监听点击事件
streetChart.on("click", function (params) {
if (
params.componentType === "series" &&
params.seriesType === "map" &&
selectedStreet.value !== params.name
) {
selectedStreet.value = params.name; // 设置选中的区域名称
getSixTableValue();
console.log("点击的区域:", params.name);
console.log("点击的区域数据:", params.data);
// 在这里可以添加更多的逻辑来处理高亮区域
} else if (
params.componentType === "series" &&
params.seriesType === "map" &&
selectedStreet.value == params.name
) {
//重复点击某个区块(街道)
streetChart.dispatchAction({
type: "unselect",
name: params.name,
});
//对街道条件置空
selectedStreet.value = "";
getSixTableValue();
console.log("点击的区域:", params.name);
console.log("点击的区域数据:", params.data);
// 在这里可以添加更多的逻辑来处理高亮区域
}
echarts6();
});
};
// 新增初始化方法
const initRelationshipChart = () => {
// return;
const chartDom = main9.value;
if (!chartDom) return;
chartRelationship = echarts.init(chartDom);
getRelationshipNetwork().then((res) => {
// 处理节点数据,为每个节点设置初始样式
const nodes = res.data.nodes.map((node: any) => ({
...node,
symbolSize: 10, // 节点大小
value: 15, // 节点权重(可用于布局)
draggable: false, // 节点不可拖动
itemStyle: {
opacity: 0.8, // 节点初始透明度
},
}));
// 处理连线数据,为每条连线设置初始样式
const links = res.data.links.map((link: any) => ({
...link,
lineStyle: {
opacity: 0.3, // 连线初始透明度
},
}));
// ECharts 配置项
const option: echarts.EChartsOption = {
title: {
text: "食品安全综合指数重点指标关联网络", // 图表标题
textStyle: {
color: "#81E7ED", // 标题颜色
fontSize: 15, // 标题字体大小
fontWeight: "bold", // 标题加粗
},
},
tooltip: {
trigger: "item", // 悬停显示节点或连线信息
textStyle: { fontSize: 10 },
},
animationDuration: 0, // 初始动画时长
// animationEasingUpdate: 'quinticInOut', // 更新动画缓动效果
animationEasingUpdate: "linear", // 更新动画缓动效果
series: [
{
type: "graph", // 图类型为关系图
layout: "force", // 使用力引导布局
zoom: 0.8,
force: {
repulsion: 1000, // 节点之间的斥力
edgeLength: 1000, // 边的长度
gravity: 10, // 重力系数
},
emphasis: {
// 鼠标悬停或高亮时的样式
focus: "adjacency", // 高亮与当前节点相邻的节点和连线
label: {
show: true,
color: "#fff",
fontSize: 12,
},
lineStyle: {
width: 2,
color: "#ffeb7b",
},
},
select: {
// 选中节点时的样式
itemStyle: {
borderColor: "#fff",
borderWidth: 2,
},
},
data: nodes, // 节点数据
links: links, // 连线数据
categories: res.data.categories, // 节点分类(用于分组着色等)
roam: true, // 支持鼠标缩放和平移
label: {
show: true, // 显示节点标签
position: "top", // 标签显示在节点上方
textBorderColor: "#fff", // 标签描边色
color: "#fff", // 标签字体颜色
},
lineStyle: {
color: "source", // 连线颜色跟随源节点
curveness: 0, // 连线弯曲度
type: "solid", // 连线为实线
},
},
],
};
// 渲染图表
chartRelationship.setOption(option);
});
// 窗口变化时自适应
// window.addEventListener('resize', () => chartRelationship.resize());
};
// 在组件卸载时清理
onBeforeUnmount(() => {
if (chartRelationship) {
chartRelationship.dispose();
window.removeEventListener("resize", () => chartRelationship?.resize());
}
});
// 添加滚动数据
const scrollData = ref([
{
xiangmu: "霉菌",
xiaolei: "代用茶",
danwei: "上海新欧尚超市有限公司嘉定店",
time: "2023-04-11",
dengji: "red",
},
{
xiangmu: "恩诺沙星(以恩诺沙星与环丙沙星之和计)",
xiaolei: "鳜鱼",
danwei: "上海盒马网络科技有限公司长宁第三分公司",
time: "2023-04-25",
dengji: "red",
},
{
xiangmu: "氯氟氰菊酯和高效氯氟氰菊酯",
xiaolei: "芹菜",
danwei: "上海市奉贤区孙超农产品经营部",
time: "2023-04-17",
dengji: "red",
},
{
xiangmu: "亚硝酸盐(以亚硝酸钠计)",
xiaolei: "熏煮香肠火腿制品",
danwei: "上海盒马网络科技有限公司宝山第二分公司",
time: "2023-04-07",
dengji: "red",
},
{
xiangmu: "呋喃唑酮代谢物",
xiaolei: "鸡肉",
danwei: "上海市松江区新桥镇东虎饮食店",
time: "2023-04-28",
dengji: "red",
},
{
xiangmu: "氯氟氰菊酯和高效氯氟氰菊酯",
xiaolei: "姜",
danwei: "上海申捷餐饮有限公司",
time: "2023-04-20",
dengji: "red",
},
{
xiangmu: "啶虫脒",
xiaolei: "豇豆",
danwei: "上海市闵行区美佳要超市",
time: "2023-04-24",
dengji: "red",
},
{
xiangmu: "防腐剂混合使用时各自用量占其最大使用量的比例之和",
xiaolei: "酱卤肉制品",
danwei: "上海普陀盒马互联网网络科技有限公司",
time: "2023-04-13",
dengji: "red",
},
{
xiangmu: "环丙氨嗪",
xiaolei: "鸡肉",
danwei: "上海优加超市有限公司",
time: "2023-04-25",
dengji: "red",
},
]);
const choujianShoufaData = ref([
{
xiangmu: "氯霉素",
xiaolei: "蛏",
danwei: "上海农产品中心批发市场经营管理有限公司潘光剑",
time: "2023-04-28",
dengji: "red",
},
{
xiangmu: "恩诺沙星(以恩诺沙星与环丙沙星之和计)",
xiaolei: "鳜鱼",
danwei: "上海盒马网络科技有限公司长宁第三分公司",
time: "2023-04-25",
dengji: "red",
},
{
xiangmu: "呋喃唑酮代谢物",
xiaolei: "基围虾(海水虾)",
danwei: "沪陵农贸市场 褚衍涛",
time: "2023-04-17",
dengji: "orange",
},
{
xiangmu: "呋喃唑酮代谢物",
xiaolei: "南美白对虾",
danwei: "上海普陀盒马互联网络科技有限公司",
time: "2023-04-13",
dengji: "orange",
},
]);
const choujianGaofaData = ref([
// 第一张图片中的水产品数据
{
xiangmu: "恩诺沙星(以恩诺沙星与环丙沙星之和计)",
xiaolei: "鳊鱼",
choujian: 63,
buhegelv: 30.2,
dengji: "red",
},
{
xiangmu: "恩诺沙星(以恩诺沙星与环丙沙星之和计)",
xiaolei: "泥鳅",
choujian: 9,
buhegelv: 77.8,
dengji: "red",
},
{
xiangmu: "恩诺沙星(以恩诺沙星与环丙沙星之和计)",
xiaolei: "草虾(淡水虾)",
choujian: 2,
buhegelv: 50.0,
dengji: "red",
},
{
xiangmu: "呋喃唑酮代谢物",
xiaolei: "南美白对虾",
choujian: 3,
buhegelv: 33.3,
dengji: "red",
},
{
xiangmu: "恩诺沙星(以恩诺沙星与环丙沙星之和计)",
xiaolei: "牛蛙",
choujian: 116,
buhegelv: 10.3,
dengji: "orange",
},
{
xiangmu: "恩诺沙星(以恩诺沙星与环丙沙星之和计)",
xiaolei: "鳜鱼",
choujian: 10,
buhegelv: 10.0,
dengji: "orange",
},
{
xiangmu: "恩诺沙星(以恩诺沙星与环丙沙星之和计)",
xiaolei: "鳝鱼",
choujian: 8,
buhegelv: 12.5,
dengji: "orange",
},
{
xiangmu: "氯霉素",
xiaolei: "蛏",
choujian: 16,
buhegelv: 6.3,
dengji: "yellow",
},
{
xiangmu: "孔雀石绿",
xiaolei: "黄颡鱼",
choujian: 31,
buhegelv: 6.5,
dengji: "yellow",
},
]);
const choujianPinfaData = ref([
// {
// xiangmu: "噻虫胺",
// xiaolei: "姜",
// data: [9.5, 3.077, 10.5, 14.6], // [1月, 2月, 3月, 4月]
// prediction: 13.2, // 特殊示例值
// },
{
xiangmu: "恩诺沙星(以恩诺沙星与环丙沙星之和计)",
xiaolei: "鳊鱼",
data: [30.0, 7.143, 28.0, 30.2],
prediction: 42.6, // 取4月值
},
// {
// xiangmu: "恩诺沙星(以恩诺沙星与环丙沙星之和计)",
// xiaolei: "牛蛙",
// data: [0.0, 1.471, 2.5, 10.3],
// prediction: 11.7, // 取4月值
// },
// {
// xiangmu: "恩诺沙星(以恩诺沙星与环丙沙星之和计)",
// xiaolei: "泥鳅",
// data: [33.3, 0, 7.7, 77.8], // "无"转为 null
// prediction: 50, // 取4月值
// },
// {
// xiangmu: "呋喃唑酮代谢物",
// xiaolei: "牛蛙",
// data: [0.0, 0.0, 1.3, 3.5],
// prediction: 3.9, // 取4月值
// },
// {
// xiangmu: "呋喃西林代谢物",
// xiaolei: "牛蛙",
// data: [0.0, 2.941, 2.5, 3.5],
// prediction: 3.5, // 取4月值
// },
{
xiangmu: "恩诺沙星(以恩诺沙星与环丙沙星之和计)",
xiaolei: "黄鱼",
data: [0.0, 0.0, 2.7, 3.2],
prediction: 12.5, // 取4月值
},
]);
const zhifaPinfaData = ref([
{
xiangmu: "场所和设备设施清洁维护",
xiaolei: "场所和设备设施清洁维护",
data: [50.7, 49.7, 27.3, 22.3],
prediction: 22.6, // 取4月值
},
]);
// 新增:品发折线图渲染函数
const choujianEchartsPinfa = () => {
// 销毁已有实例,防止重复渲染
if (main7.value && echarts.getInstanceByDom(main7.value)) {
echarts.getInstanceByDom(main7.value).dispose();
}
var myChart = echarts.init(main7.value);
const months = ["1月", "2月", "3月", "4月", "5月"];
const colorList = [
"#5470C6",
"#91CC75",
"#FAC858",
"#EE6666",
"#73C0DE",
"#3BA272",
"#FC8452",
];
// 生成两条series实线+虚线
const series = choujianPinfaData.value
.map((item, idx) => [
// 实线部分
{
name: item.xiaolei,
type: "line",
data: [...item.data, null], // 1-4月有值第5月null
symbol: "circle",
symbolSize: 8,
lineStyle: { type: "solid", width: 2 },
itemStyle: { color: colorList[idx % colorList.length] },
connectNulls: true,
showSymbol: true,
emphasis: { focus: "series" },
z: 2,
},
// 虚线部分
{
name: item.xiaolei + "预测",
type: "line",
data: [null, null, null, item.data[3], item.prediction], // 只连4-5月
symbol: "emptyCircle",
symbolSize: 10,
lineStyle: { type: "dashed", width: 2 },
itemStyle: { color: colorList[idx % colorList.length] },
connectNulls: true,
showSymbol: true,
emphasis: { focus: "series" },
z: 1,
tooltip: { show: false }, // 关键隐藏虚线的tooltip
},
])
.flat();
// legend只显示品种名
const legendData = choujianPinfaData.value.map((item) => item.xiaolei);
const option = {
title: {
text: "频发预警",
left: "center",
textStyle: { color: "#00ffff", fontSize: 20, fontWeight: "bold" },
top: 5,
},
tooltip: {
trigger: "axis",
// valueFormatter: (v) => (v == null ? "无" : v + "%"),
valueFormatter: (v) => (v == null ? choujianPinfaData.value[0].prediction + "%" : v + "%"),
},
legend: {
data: legendData,
top: 30,
textStyle: { color: "#fff" },
// 只显示主线legend
selected: legendData.reduce((acc, cur) => {
acc[cur] = true;
return acc;
}, {}),
},
grid: { left: 45, right: 30, top: 63, bottom: 45 },
xAxis: {
type: "category",
data: months,
axisLabel: { color: "#fff" },
},
yAxis: {
show: true,
type: "value",
name: "",
axisLabel: { show: true }, // 隐藏y轴的具体数值
splitLine: { show: true, lineStyle: { color: "#fff", type: "dashed" } }, // 只显示横线
},
series: series,
};
myChart.setOption(option);
};
const zhifaEchartsPinfa = () => {
// 销毁已有实例,防止重复渲染
if (main8.value && echarts.getInstanceByDom(main8.value)) {
echarts.getInstanceByDom(main8.value).dispose();
}
var myChart = echarts.init(main8.value);
const months = ["1月", "2月", "3月", "4月", "5月"];
const colorList = [
"#5470C6",
"#91CC75",
"#FAC858",
"#EE6666",
"#73C0DE",
"#3BA272",
"#FC8452",
];
// 生成两条series实线+虚线
const series = zhifaPinfaData.value
.map((item, idx) => [
// 实线部分
{
name: item.xiaolei,
type: "line",
data: [...item.data, null], // 1-4月有值第5月null
symbol: "circle",
symbolSize: 8,
lineStyle: { type: "solid", width: 2 },
itemStyle: { color: colorList[idx % colorList.length] },
connectNulls: true,
showSymbol: true,
emphasis: { focus: "series" },
z: 2,
},
// 虚线部分
{
name: item.xiaolei + "预测",
type: "line",
data: [null, null, null, item.data[3], item.prediction], // 只连4-5月
symbol: "emptyCircle",
symbolSize: 10,
lineStyle: { type: "dashed", width: 2 },
itemStyle: { color: colorList[idx % colorList.length] },
connectNulls: true,
showSymbol: true,
emphasis: { focus: "series" },
z: 1,
tooltip: { show: false }, // 关键隐藏虚线的tooltip
},
])
.flat();
// legend只显示品种名
const legendData = zhifaPinfaData.value.map((item) => item.xiaolei);
const option = {
title: {
text: "频发预警",
left: "center",
textStyle: { color: "#00ffff", fontSize: 20, fontWeight: "bold" },
top: 5,
},
tooltip: {
trigger: "axis",
valueFormatter: (v) => (v == null ? zhifaPinfaData.value[0].prediction + "%" : v + "%"),
// formatter: '{b0}: {c0}<br />{b1}: {c1}'
},
legend: {
data: legendData,
top: 30,
textStyle: { color: "#fff" },
// 只显示主线legend
selected: legendData.reduce((acc, cur) => {
acc[cur] = true;
return acc;
}, {}),
},
grid: { left: 45, right: 30, top: 63, bottom: 45 },
xAxis: {
type: "category",
data: months,
axisLabel: { color: "#fff" },
},
yAxis: {
show: true,
type: "value",
name: "",
axisLabel: { show: true }, // 隐藏y轴的具体数值
splitLine: { show: true, lineStyle: { color: "#fff", type: "dashed" } }, // 只显示横线
},
series: series,
};
myChart.setOption(option);
};
const jubaoGaofaData = ref([
{
huanjie: "销售环节",
zhonglei: "虚假宣传",
tousulv: "7.05%",
dengji: "red",
},
{
huanjie: "销售环节",
zhonglei: "腐败变质",
tousulv: "3.21%",
dengji: "red",
}, {
huanjie: "销售环节",
zhonglei: "存在异物",
tousulv: "3.18%",
dengji: "red",
}, {
huanjie: "销售环节",
zhonglei: "原料处理不当",
tousulv: "2.04%",
dengji: "yellow",
}, {
huanjie: "销售环节",
zhonglei: "虚假宣传",
tousulv: "1.97%",
dengji: "yellow",
},
]);
const jubaoPinfaData = ref([
{
zhuti: "上海盒马网络科技有限公司",
leibie: "水果类",
hegelv: "100.0%(65/65)",
zhonglei: "腐败变质",
wentishu: 3,
dengji: "yellow",
},
{
zhuti: "上海盒马网络科技有限公司",
leibie: "畜禽肉及副产品",
hegelv: "100.0%(123/123)",
zhonglei: "腐败变质",
wentishu: 3,
dengji: "yellow",
},
{
zhuti: "上海盒马网络科技有限公司",
leibie: "水产品",
hegelv: "99.2%(251/253)",
zhonglei: "腐败变质",
wentishu: 3,
dengji: "yellow",
},
]);
const highlightGraphNodeByName = (nodeName: string) => {
// 先取消所有高亮
chartRelationship.dispatchAction({
type: "downplay",
seriesIndex: 0,
});
// 高亮目标节点
chartRelationship.dispatchAction({
type: "highlight",
seriesIndex: 0,
name: nodeName,
});
};
const handleRowClick = (item: any, index: number) => {
console.log(item, index);
// 将当前行数据移到首行
if (index !== 0) {
jubaoGaofaData.value.splice(index, 1); // 删除当前行
jubaoGaofaData.value.unshift(item); // 插入到首行
}
};
const fancyBtn = ref(null)
const ripple = ref(null)
const handleFancyClick = (e: MouseEvent) => {
const btn = fancyBtn.value as HTMLElement
const span = ripple.value as HTMLElement
if (!btn || !span) return
const rect = btn.getBoundingClientRect()
const size = Math.max(rect.width, rect.height)
span.style.width = span.style.height = size + 'px'
span.style.left = (e.clientX - rect.left - size / 2) + 'px'
span.style.top = (e.clientY - rect.top - size / 2) + 'px'
span.classList.add('show')
setTimeout(() => span.classList.remove('show'), 500)
//接下来展示或隐藏关联关系图
if (relationShow.value) {
// 隐藏动画:先播放收回动画,动画完成后再隐藏
isAnimating.value = true
relationShow.value = false
setTimeout(() => {
isAnimating.value = false
}, 300) // 动画持续时间
} else {
// 显示动画:先显示元素(但不可见),然后播放弹出动画
relationShow.value = true
// 延迟一帧再开始动画,确保元素已经渲染
requestAnimationFrame(() => {
isAnimating.value = true
setTimeout(() => {
isAnimating.value = false
}, 300) // 动画持续时间
})
}
}
</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;
}
.example-showcase .el-dropdown-link {
cursor: pointer;
/* color: var(--el-color-primary); */
color: white;
display: flex;
align-items: center;
}
#tooltip {
transition: all 0.3s ease-out;
}
.shanghaimap {
position: absolute;
/* 绝对定位 */
top: 18%;
/* 从容器的顶部开始 */
left: 26.5%;
/* 从容器的左侧开始 */
width: 47%;
/* 宽度占满整个容器 */
height: 75%;
/* 高度占满整个容器 */
z-index: 5;
/* 确保大盒子在小盒子后面 */
display: flex;
/* 使用 Flexbox 布局 */
justify-content: center;
/* 水平居中对齐 */
align-items: center;
/* 垂直居中对齐 */
}
.bar-show {
max-width: 100px;
/* 设置最大宽度 */
white-space: nowrap;
/* 禁止换行 */
overflow: hidden;
/* 超出部分隐藏 */
text-overflow: ellipsis;
/* 使用省略号表示超出部分 */
}
@keyframes fadeInOut {
0% {
opacity: 0;
transform: translateY(-10px);
}
50% {
opacity: 1;
}
100% {
opacity: 0;
transform: translateY(10px);
}
}
.scroll-wrapper {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.scroll-header {
display: flex;
justify-content: space-between;
padding: 8px 16px;
color: #00ffff;
font-weight: bold;
border-bottom: 2px solid rgba(0, 255, 255, 0.3);
background: rgba(0, 0, 0, 0.2);
font-size: 10px;
.scroll-header-text {
flex: 1;
text-align: center;
}
}
.scroll-container {
flex: 1;
overflow: hidden;
background: rgba(0, 0, 0, 0.2);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.scroll-item {
display: flex;
justify-content: space-between;
padding: 8px 16px;
color: rgb(204, 203, 203);
font-size: 8px;
}
.scroll-item-text {
flex: 1;
text-align: center;
}
.scroll-header-title {
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
color: #00ffff;
font-weight: bold;
/* border-bottom: 2px solid rgba(0, 255, 255, 0.3); */
background: rgba(0, 0, 0, 0.2);
padding-top: 3px;
}
.ripple {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
transform: scale(0);
pointer-events: none;
transition: transform 0.5s, opacity 0.5s;
opacity: 0;
z-index: 1;
}
.ripple.show {
transform: scale(2.5);
opacity: 1;
transition: transform 0.5s, opacity 0.5s;
}
/* 弹出动画 */
@keyframes popup {
0% {
opacity: 0;
transform: scale(0) translate(0, 0);
}
50% {
opacity: 0.8;
transform: scale(0.8) translate(0, -20px);
}
100% {
opacity: 1;
transform: scale(1) translate(0, 0);
}
}
/* 收回动画 */
@keyframes shrink {
0% {
opacity: 1;
transform: scale(1) translate(0, 0);
}
50% {
opacity: 0.8;
transform: scale(0.8) translate(0, -20px);
}
100% {
opacity: 0;
transform: scale(0) translate(0, 0);
}
}
.animate-popup {
animation: popup 0.3s ease-out forwards;
transform-origin: bottom right;
}
.animate-shrink {
animation: shrink 0.3s ease-in forwards;
transform-origin: bottom right;
}
/* 初始状态 - 隐藏且缩放为0 */
.opacity-0.scale-0 {
opacity: 0 !important;
transform: scale(0) !important;
transition: none !important;
}
</style>