子组件
<template>
<div>
<div>【云端报警风险】</div>
<div ref="target" class="w-full h-full"></div>
</div>
</template>
<script setup>
import { ref, onMounted,watch } from 'vue';
import * as echarts from "echarts";
// 定义接收父组件传来的值
const props = defineProps({
data: {
type: Object,
required: true,
},
});
// console.log(props.data);
// 1.初始化
let myChart = null;
const target = ref(null);
onMounted(() => {
myChart = echarts.init(target.value);
renderChart();
});
// 2.构建 option 配置对象
const renderChart = () => {
const options = {
// 雷达图坐标系配置
radar: {
name: {
textStyle: {
color: "#05D5FF",
fontSize: 14
}
},
shape: 'polygon',
center: ['50%', '50%'],
radius: '80%',
startAngle: 120,
// 轴线
axisLine: {
lineStyle: {
color: 'rgba(2,213,255,.8)'
}
},
// 网格线
splitLine: {
show: true,
lineStyle: {
with: 1,
color: 'rgba(5,213,255,.8)'
}
},
// 指示器名称
indicator: props.data.risks.map(item => ({
name: item.name,
max: 100
})),
splitArea: {
show:false
}
},
// 位置、极点
polar: {
center: ['50%', '50%'],
radius:'0%'
},
// 坐标角度
angleAxis: {
min: 0,
interval: 5,
clockwise:false,//刻度逆时针
},
// 径向轴
radiusAxis: {
min: 0,
interval: 20,
splitLine: {
show:true
}
},
// 图表核心配置
series: {
type: 'radar',
symbol: 'circle',
symbolSize: 10,
itemStyle: {
normal: {
color:'#05D5FF'
}
},
areaStyle: {
normal: {
color: '#05D5FF',
opacity:0.5
}
},
lineStyle: {
with: 2,
color:'#05D5FF'
},
label: {
normal: {
show:true,
color: '#05D5FF',
}
},
data: [
{
value:props.data.risks.map(item=>item.value)
}
]
}
}
// 3.通过实例.setOptions(option)
myChart.setOption(options);
};
watch(() => props.data,renderChart)
</script>
<style lang="scss" scoped>
</style>
监听浏览器
const $router = useRouter()
// 获取宽度
const windowSize = () => {
let width = document.documentElement.clientWidth;
width<=768?$router.push({ path: "/m" }):""
}
window.addEventListener("resize", windowSize);
windowSize()
echarts 地图
app.vue
<script setup>
import { RouterView } from 'vue-router'
import { provide } from 'vue';
import * as echarts from 'echarts'
import chalk from '@/assets/theme/chalk'
import SocketService from './utils/socket_service'
echarts.registerTheme('chalk', chalk)
provide('echarts', echarts) //重点
provide('socket', SocketService.Instance)
</script>
<template>
<RouterView />
</template>
<style scoped></style>
子组件
<script setup>
import { ref, inject, onMounted, onBeforeUnmount } from 'vue';
import useRequest from '@/composables/useRequest'
import chinaJson from '@/assets/map/china.json'
import { getProvinceMapInfo } from '@/utils/map_utils.js'
const echarts = inject('echarts')
const map_chart = ref(null)
let chartInstance = null
const initChart = () => {
chartInstance = echarts.init(map_chart.value, 'chalk')
echarts.registerMap('china', chinaJson)
chartInstance.on('click', async (arg) => {
const provinceInfo = getProvinceMapInfo(arg.name)
// console.log(import.meta); //读取该文件的模块路径
if (provinceInfo.key) {
const areaData = await getAreaData(provinceInfo.path)
echarts.registerMap(provinceInfo.key, { ...areaData })
chartInstance.setOption({
geo: {
map: provinceInfo.key
}
})
}
})
}
const getAreaData = (path) => import(/* @vite-ignore */path)
const updataChart = () => {
const titleFontSize = map_chart.value.offsetWidth / 100 * 3.6
const option = {
title: {
text: "▎商家分布",
left: 20,
top: 20,
textStyle: {
fontSize: map_chart.value.offsetWidth / 100 * 2.5,
}
},
legend: {
left: '5%',
bottom: '5%',
orient: 'vertical',
itemWidth: titleFontSize,
itemHeight: titleFontSize,
itemGap: titleFontSize,
textStyle: {
fontSize: titleFontSize / 2
}
},
geo: {
type: 'map',
map: 'china',
top: "5%",
bottom: "5%",
itemStyle: {
areaColor: '#2E72BF',
borderColor: '#333'
}
},
}
chartInstance.setOption(option)
}
const updataChartData = () => {
const option = {
series: seriesArr.value
}
chartInstance.setOption(option)
}
const allData = ref([])
const seriesArr = ref([]) //放弃使用dataset,有点复杂不会弄。。。
const getData = async () => {
const res = await useRequest('/map')
allData.value = res
seriesArr.value = res.map(i => ({
type: 'effectScatter',
rippleEffect: {
scale: 5,
brushType: 'stroke'
},
name: i.name,
data: i.children,
coordinateSystem: 'geo',//在地图使用散点需要加上这项
}))
updataChartData()
}
const screenAdapter = () => {
updataChart()
chartInstance.resize()
}
onMounted(() => {
getData()
initChart()
screenAdapter()
window.addEventListener('resize', screenAdapter)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', screenAdapter)
})
defineExpose({
screenAdapter
})
</script>
<template>
<div class="map_container">
<div class="map_chart" ref="map_chart" @dblclick="updataChart">
</div>
</div>
</template>
<style scoped>
.map_container,
.map_chart {
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
使用
<div class="map" :class="fullScreenStatus.map ? 'fullscreen' : ''">
<Map ref="map"></Map>
<span @click="changeSize('map')" class="iconfont enlargement"
:class="fullScreenStatus.map ? 'icon-compress-alt' : 'icon-expand-alt'"></span>
</div>
map_utils.js
const nameChange = {
安徽: 'anhui',
陕西: 'shanxi1',
澳门: 'aomen',
北京: 'beijing',
重庆: 'chongqing',
福建: 'fujian',
甘肃: 'gansu',
广东: 'guangdong',
广西: 'guangxi',
贵州: 'guizhou',
海南: 'hainan',
河北: 'hebei',
黑龙江: 'heilongjiang',
河南: 'henan',
湖北: 'hubei',
湖南: 'hunan',
江苏: 'jiangsu',
江西: 'jiangxi',
吉林: 'jilin',
辽宁: 'liaoning',
内蒙古: 'neimenggu',
宁夏: 'ningxia',
青海: 'qinghai',
山东: 'shandong',
上海: 'shanghai',
山西: 'shanxi',
四川: 'sichuan',
台湾: 'taiwan',
天津: 'tianjin',
香港: 'xianggang',
新疆: 'xinjiang',
西藏: 'xizang',
云南: 'yunnan',
浙江: 'zhejiang'
}
export function getProvinceMapInfo (arg) {
const path = `/src/assets/map/province/${nameChange[arg]}`
return {
key: nameChange[arg],
path: path
}
}
socket_service.js
export default class SocketService {
static instance = null
static get Instance() {
if (!this.instance) {
this.instance = new SocketService()
}
return this.instance
}
ws = null
connected = false
connectRetryCount = 0
connect() {
if (!window.WebSocket) {
return console.log('浏览器不支持websocket');
}
this.ws = new WebSocket('ws://localhost:9998')
this.ws.onopen = () => {
console.log('服务器连接成功');
this.connected = true
this.connectRetryCount = 0
}
// 链接失败或连接后断开会调用
this.ws.onclose = () => {
console.log('连接服务器失败');
this.connected = false
this.connectRetryCount++
setTimeout(() => {
this.connect()
}, this.connectRetryCount * 500)
}
this.ws.onmessage = msg => {
console.log('从服务器获取到了数据');
// console.log(msg.data);
const recvData = JSON.parse(msg.data)
const socketType = recvData.socketType
if (this.callBackMapping[socketType]) {
const action = recvData.action
if (action === 'getData') {
const realData = JSON.parse(recvData.data)
this.callBackMapping[socketType].call(this, realData)
} else if (action === 'fullScreen') {
} else if (action === 'themeChange') {
}
}
}
}
callBackMapping = {}
registerCallBack(socketType, callBack) {
this.callBackMapping[socketType] = callBack
}
unRegisterCallBack(socketType) {
this.callBackMapping[socketType] = callBack
}
sendRetryCount = 0
send(data) {
if (this.connected) {
this.sendRetryCount = 0
this.ws.send(JSON.stringify(data))
} else {
this.sendRetryCount++
setTimeout(() => {
this.send(data)
}, this.sendRetryCount * 500)
}
}
}
main.js引入
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
// 重置样式
import 'normalize.css'
// 字体文件
import './assets/font/iconfont.css'
import './assets/main.css'
import SocketService from './utils/socket_service'
// 开启websocket
SocketService.Instance.connect()
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')
onMounted(() => {
initChartsCenterOne();
});
// 初始化中间图表1
const initChartsCenterOne = () => {
const myChart = echarts.init(chartsCenterOneRef.value);
const option = {
grid: {
top: 15,
right: 15,
bottom: 20,
left: 30,
},
tooltip: {},
series: [
{
type: 'wordCloud',
sizeRange: [12, 40],
rotationRange: [0, 0],
rotationStep: 45,
gridSize: Math.random() * 20 + 5,
shape: 'circle',
width: '100%',
height: '100%',
textStyle: {
fontFamily: 'sans-serif',
fontWeight: 'bold',
color: function () {
return `rgb(${[Math.round(Math.random() * 160), Math.round(Math.random() * 160), Math.round(Math.random() * 160)].join(',')})`;
},
},
data: [
{ name: 'vue-next-admin', value: 520 },
{ name: 'lyt', value: 520 },
{ name: 'next-admin', value: 500 },
{ name: '更名', value: 420 },
{ name: '智慧农业', value: 520 },
{ name: '男神', value: 2.64 },
{ name: '好身材', value: 4.03 },
{ name: '校草', value: 24.95 },
{ name: '酷', value: 4.04 },
{ name: '时尚', value: 5.27 },
{ name: '阳光活力', value: 5.8 },
{ name: '初恋', value: 3.09 },
{ name: '英俊潇洒', value: 24.71 },
{ name: '霸气', value: 6.33 },
{ name: '腼腆', value: 2.55 },
{ name: '蠢萌', value: 3.88 },
{ name: '青春', value: 8.04 },
{ name: '网红', value: 5.87 },
{ name: '萌', value: 6.97 },
{ name: '认真', value: 2.53 },
{ name: '古典', value: 2.49 },
{ name: '温柔', value: 3.91 },
{ name: '有个性', value: 3.25 },
{ name: '可爱', value: 9.93 },
{ name: '幽默诙谐', value: 3.65 },
],
},
],
};
myChart.setOption(option);
state.myCharts.push(myChart);
};
const chartsCenterOneRef = ref();
<div class="big-data-down-center-one">
<div class="big-data-down-center-one-content">
<div style="height: 100%" ref="chartsCenterOneRef"></div>
</div>
</div>