240 lines
5.4 KiB
Vue
240 lines
5.4 KiB
Vue
<template>
|
||
<div class="china-map-container">
|
||
<div ref="chartRef" class="chart-container"></div>
|
||
<el-empty v-if="!loading && data.length === 0" description="暂无省份分布数据" />
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { ref, onMounted, watch, onUnmounted } from 'vue'
|
||
import type { EChartsOption } from 'echarts'
|
||
import echarts from '@/plugins/echarts'
|
||
import { registerChinaMap } from '@/plugins/echarts'
|
||
import { ProvinceChartVO } from '@/api/prison/dashboard'
|
||
import { debounce } from 'lodash-es'
|
||
|
||
defineOptions({ name: 'ChinaMap' })
|
||
|
||
interface Props {
|
||
data: ProvinceChartVO[]
|
||
loading?: boolean
|
||
}
|
||
|
||
const props = withDefaults(defineProps<Props>(), {
|
||
loading: false
|
||
})
|
||
|
||
const chartRef = ref<HTMLDivElement>()
|
||
let chartInstance: echarts.ECharts | null = null
|
||
|
||
// 省份编码到名称的映射(支持 2 位和 6 位 adcode)
|
||
const provinceCodeMap: Record<string, string> = {
|
||
// 2位编码
|
||
'11': '北京市',
|
||
'12': '天津市',
|
||
'13': '河北省',
|
||
'14': '山西省',
|
||
'15': '内蒙古自治区',
|
||
'21': '辽宁省',
|
||
'22': '吉林省',
|
||
'23': '黑龙江省',
|
||
'31': '上海市',
|
||
'32': '江苏省',
|
||
'33': '浙江省',
|
||
'34': '安徽省',
|
||
'35': '福建省',
|
||
'36': '江西省',
|
||
'37': '山东省',
|
||
'41': '河南省',
|
||
'42': '湖北省',
|
||
'43': '湖南省',
|
||
'44': '广东省',
|
||
'45': '广西壮族自治区',
|
||
'46': '海南省',
|
||
'50': '重庆市',
|
||
'51': '四川省',
|
||
'52': '贵州省',
|
||
'53': '云南省',
|
||
'54': '西藏自治区',
|
||
'61': '陕西省',
|
||
'62': '甘肃省',
|
||
'63': '青海省',
|
||
'64': '宁夏回族自治区',
|
||
'65': '新疆维吾尔自治区',
|
||
'71': '台湾省',
|
||
'81': '香港特别行政区',
|
||
'82': '澳门特别行政区',
|
||
// 6位 adcode
|
||
'110000': '北京市',
|
||
'120000': '天津市',
|
||
'130000': '河北省',
|
||
'140000': '山西省',
|
||
'150000': '内蒙古自治区',
|
||
'210000': '辽宁省',
|
||
'220000': '吉林省',
|
||
'230000': '黑龙江省',
|
||
'310000': '上海市',
|
||
'320000': '江苏省',
|
||
'330000': '浙江省',
|
||
'340000': '安徽省',
|
||
'350000': '福建省',
|
||
'360000': '江西省',
|
||
'370000': '山东省',
|
||
'410000': '河南省',
|
||
'420000': '湖北省',
|
||
'430000': '湖南省',
|
||
'440000': '广东省',
|
||
'450000': '广西壮族自治区',
|
||
'460000': '海南省',
|
||
'500000': '重庆市',
|
||
'510000': '四川省',
|
||
'520000': '贵州省',
|
||
'530000': '云南省',
|
||
'540000': '西藏自治区',
|
||
'610000': '陕西省',
|
||
'620000': '甘肃省',
|
||
'630000': '青海省',
|
||
'640000': '宁夏回族自治区',
|
||
'650000': '新疆维吾尔自治区',
|
||
'710000': '台湾省',
|
||
'810000': '香港特别行政区',
|
||
'820000': '澳门特别行政区'
|
||
}
|
||
|
||
// 获取省份名称
|
||
const getProvinceName = (code: string | number): string => {
|
||
const codeStr = String(code)
|
||
// 优先尝试完整匹配(6位 adcode)
|
||
if (provinceCodeMap[codeStr]) {
|
||
return provinceCodeMap[codeStr]
|
||
}
|
||
// 如果是2位编码,尝试匹配
|
||
const shortCode = codeStr.padStart(2, '0')
|
||
return provinceCodeMap[shortCode] || codeStr
|
||
}
|
||
|
||
// 格式化数据为 ECharts 地图数据格式
|
||
const formatMapData = (data: ProvinceChartVO[]) => {
|
||
return data.map((item) => ({
|
||
name: getProvinceName(item.provinceCode),
|
||
value: item.count
|
||
}))
|
||
}
|
||
|
||
// 获取最大值
|
||
const getMaxValue = (data: ProvinceChartVO[]): number => {
|
||
if (data.length === 0) return 100
|
||
return Math.max(...data.map((item) => item.count), 100)
|
||
}
|
||
|
||
// 更新图表
|
||
const updateChart = () => {
|
||
if (!chartInstance || !chartRef.value) return
|
||
|
||
const mapData = formatMapData(props.data)
|
||
const maxValue = getMaxValue(props.data)
|
||
|
||
const option: EChartsOption = {
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: (params: any) => {
|
||
if (params.value === undefined) {
|
||
return `${params.name}: 暂无数据`
|
||
}
|
||
return `${params.name}: ${params.value}人`
|
||
}
|
||
},
|
||
visualMap: {
|
||
min: 0,
|
||
max: maxValue,
|
||
left: 'left',
|
||
top: 'bottom',
|
||
text: ['高', '低'],
|
||
calculable: true,
|
||
inRange: {
|
||
color: ['#e0f3f8', '#91c7ae', '#c23531']
|
||
}
|
||
},
|
||
series: [
|
||
{
|
||
name: '籍贯分布',
|
||
type: 'map',
|
||
map: 'china',
|
||
roam: true,
|
||
zoom: 1.2,
|
||
scaleLimit: {
|
||
min: 0.5,
|
||
max: 3
|
||
},
|
||
label: {
|
||
show: true,
|
||
fontSize: 10,
|
||
color: '#333'
|
||
},
|
||
emphasis: {
|
||
label: {
|
||
show: true,
|
||
fontSize: 12,
|
||
fontWeight: 'bold'
|
||
},
|
||
itemStyle: {
|
||
areaColor: '#ffd700'
|
||
}
|
||
},
|
||
data: mapData
|
||
}
|
||
]
|
||
}
|
||
|
||
chartInstance.setOption(option)
|
||
}
|
||
|
||
// 初始化图表
|
||
const initChart = () => {
|
||
if (!chartRef.value) return
|
||
|
||
chartInstance = echarts.init(chartRef.value)
|
||
updateChart()
|
||
}
|
||
|
||
// 监听数据变化
|
||
watch(
|
||
() => props.data,
|
||
() => {
|
||
updateChart()
|
||
},
|
||
{ deep: true }
|
||
)
|
||
|
||
// 响应式调整
|
||
const resizeHandler = debounce(() => {
|
||
chartInstance?.resize()
|
||
}, 100)
|
||
|
||
onMounted(async () => {
|
||
// 注册中国地图
|
||
await registerChinaMap()
|
||
// 初始化图表
|
||
initChart()
|
||
window.addEventListener('resize', resizeHandler)
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
window.removeEventListener('resize', resizeHandler)
|
||
chartInstance?.dispose()
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.china-map-container {
|
||
width: 100%;
|
||
height: 500px;
|
||
position: relative;
|
||
|
||
.chart-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
</style>
|