Echarts 省级别地图数据展示 (新疆)
2024.12.17 11:00 (暂无真数据支持)
2024.12.20 12:30 (调试完毕)
目标:
1.组件复用
2.可以切换各个地州、市
3.各地区根据数值不同显示不同颜色
4.地名展示,数值展示
终版效果:
终版地图
地图组件
<script setup lang="ts">
import { ref, onMounted, watch, nextTick, markRaw } from 'vue'
import * as echarts from 'echarts'
import instance from '@/utils/serve'
import { useNavTabStore } from '@/stores'
const storeNav = useNavTabStore() // 用户信息
const props = defineProps({
mapDataInfo: {
type: Array,
default: () => []
},
tooltipName: {
type: String,
default: '工单总量(单)'
},
mapArea: {
type: String,
default: 'qugongsi'
}
})
// 最大值
const maxValue = ref(0)
// 地图
// 默认数据
const mapData = ref<any>([
{
name: '乌鲁木齐',
value: undefined
},
{
name: '吐鲁番',
value: undefined
},
{
name: '克拉玛依',
value: undefined
},
{
name: '哈密',
value: undefined
},
{
name: '昌吉州',
value: undefined
},
{
name: '博州',
value: undefined
},
{
name: '巴州',
value: undefined
},
{
name: '阿克苏',
value: undefined
},
{
name: '克州',
value: undefined
},
{
name: '喀什',
value: undefined
},
{
name: '和田',
value: undefined
},
{
name: '伊犁',
value: undefined
},
{
name: '塔城',
value: undefined
},
{
name: '阿勒泰',
value: undefined
},
{
name: '胡杨河',
value: undefined
},
{
name: '图木舒克市',
value: undefined
},
{
name: '五家渠',
value: undefined
},
{
name: '昆玉市',
value: undefined
},
{
name: '石河子',
value: undefined
},
{
name: '阿拉尔市',
value: undefined
},
{
name: '北屯市',
value: undefined
},
{
name: '铁门关市',
value: undefined
},
{
name: '双河',
value: undefined
},
{
name: '可克达拉市',
value: undefined
},
{
name: '奎屯',
value: undefined
}
])
const mapEcharts = ref<any>()
const mapEchartsRef = ref<HTMLElement>()
const getOption = (mapName: string) => {
return {
tooltip: {
trigger: 'item',
formatter: function (params: any) {
if (!params.data.value) {
return params.data.name
} else {
return params.data.name + '<br/>' + `${props.tooltipName}: ` + params.data.value
}
}
},
roam: true,
toolbox: {
show: true,
orient: 'vertical',
right: 10,
top: 10,
feature: {
// restore: {
// title: '还原'
// }
}
},
geo: [
{
map: mapName,
roam: false, //是否允许缩放
zoom: 1.2, //默认显示级别
scaleLimit: {
min: 0,
max: 3
}, //缩放级别
itemStyle: {
areaColor: '#489EFF',
shadowColor: '#5e92fd',
shadowBlur: 1,
shadowOffsetX: 0,
shadowOffsetY: 9,
color: '#5e92fd'
}
}
],
visualMap: {
show: true,
type: 'continuous',
min: 0,
max: maxValue.value,
right: 24,
bottom: 0,
splitNumber: 4,
showLabel: true,
// text: ['高', '低'],
textStyle: {
color: '#000'
},
inRange: {
color: ['#D8EFFF', '#489EFF']
},
calculable: true
},
series: [
{
name: '新疆数据',
type: 'map',
map: mapName,
label: {
show: true
},
data: mapData.value,
coordinateSystem: 'geo',
zoom: 1.2, //默认显示级别
roam: false, //是否允许缩放
itemStyle: {
borderShadowColor: 'skyblue',
borderShadowBlur: 2,
borderWidth: 1,
shadowBlur: 4,
shadowColor: 'skyblue'
},
// 自定义名称映射
nameMap: {
乌鲁木齐市: '乌鲁木齐',
五家渠市: '五家渠',
昌吉回族自治州: '昌吉州',
石河子市: '石河子',
博尔塔拉蒙古自治州: '博州',
双河市: '双河',
塔城地区: '塔城',
克拉玛依市: '克拉玛依',
胡杨河市: '胡杨河',
克孜勒苏柯尔克孜自治州: '克州',
巴音郭楞蒙古自治州: '巴州',
伊犁哈萨克自治州: '伊犁',
阿勒泰地区: '阿勒泰',
吐鲁番市: '吐鲁番',
哈密市: '哈密',
和田地区: '和田',
喀什地区: '喀什',
阿克苏地区: '阿克苏',
奎屯市: '奎屯'
}
}
]
}
}
const initMapEcharts = async (mapName: string) => {
await instance.get(`/static/xinjiangArea/${mapName}.json`).then((res) => {
// @ts-ignore:无法被执行的代码的错误
echarts.registerMap(mapName, res)
})
mapEcharts.value = markRaw(echarts.init(mapEchartsRef.value as HTMLElement))
mapEcharts.value.setOption(getOption(mapName))
}
const resizeMapEcharts = () => {
mapEcharts.value?.dispose()
nextTick(() => {
initMapEcharts(props.mapArea)
})
}
defineExpose({
mapEcharts,
initMapEcharts,
resizeMapEcharts
})
watch(
() => props.mapArea,
(newVaild: any, oldVaild: any) => {
if (newVaild !== oldVaild) {
mapEcharts.value?.dispose()
}
nextTick(() => {
initMapEcharts(props.mapArea)
})
},
{ immediate: true, deep: true }
)
watch(
() => props.mapDataInfo,
() => {
mapData.value = mapData.value.map((item: any) => ({
...item,
value: undefined
}))
if (props.mapDataInfo[0]) {
const mapInfo = props.mapDataInfo[0] as any
// console.log('xxxx', mapInfo)
mapInfo?.name.forEach((item: any, index: number) => {
mapData.value.forEach((itemMap: any) => {
// console.log(itemMap.name)
// if (!itemMap) return
if (item.includes(itemMap.name)) {
itemMap.value = mapInfo.value[index]
}
})
})
maxValue.value = Math.max(
...mapData.value.map((item: any) => item.value).filter((item: any) => item !== undefined)
)
? Math.max(
...mapData.value
.map((item: any) => item.value)
.filter((item: any) => item !== undefined)
)
: 0
// console.log('cczxczxcz')
// mapEcharts.value?.dispose()
// nextTick(() => {
// initMapEcharts(props.mapArea)
// })
}
},
{ deep: true }
)
watch(
() => storeNav.navZoom,
(newVaild: any, oldVaild: any) => {
if (newVaild !== oldVaild) {
// mapEcharts.value?.resize()
setTimeout(() => {
mapEcharts.value?.resize()
}, 350)
}
},
{ immediate: true, deep: true }
)
onMounted(() => {
// initMapEcharts()
})
</script>
<template>
<div class="mapBox" ref="mapEchartsRef"></div>
</template>
<style scoped lang="less">
.mapBox {
width: 100%;
height: 100%;
}
</style>
我的页面所需
// 树形选择器数据
export const regionData = [
{
pid: '0',
id: '111',
label: '区公司',
value: '区公司',
mapName: 'xinjiang', // 这里是向后端请求地图JSON的名称
children: [
{
pid: '111',
id: '111.993',
label: '石河子本地网',
value: '石河子本地网',
mapName: 'shihezishi',
isLeaf: false
},
{
pid: '111',
id: '111.903',
label: '和田本地网',
value: '和田本地网',
mapName: 'hetiandiqu'
},
{
pid: '111',
id: '111.994',
label: '昌吉州本地网',
value: '昌吉州本地网',
mapName: 'changjihuizu'
},
{
pid: '111',
id: '111.991',
label: '乌鲁木齐本地网',
value: '乌鲁木齐本地网',
mapName: 'wulumuqi'
},
{
pid: '111',
id: '111.901',
label: '塔城本地网',
value: '塔城本地网',
mapName: 'tachengdiqu'
},
{
pid: '111',
id: '111.908',
label: '克州本地网',
value: '克州本地网',
mapName: 'kezilesukeerkezi'
},
{
pid: '111',
id: '111.990',
label: '克拉玛依本地网',
value: '克拉玛依本地网',
mapName: 'kelamayishi'
},
{
pid: '111',
id: '111.998',
label: '喀什本地网',
value: '喀什本地网',
mapName: 'kashidiqu'
},
{
pid: '111',
id: '111.996',
label: '巴州本地网',
value: '巴州本地网',
mapName: 'bayinguolengmenggu'
},
{
pid: '111',
id: '111.902',
label: '哈密本地网',
value: '哈密本地网',
mapName: 'hamishi'
},
{
pid: '111',
id: '111.992',
label: '奎屯本地网',
value: '奎屯本地网',
mapName: 'kuitunshi'
},
{
pid: '111',
id: '111.997',
label: '阿克苏本地网',
value: '阿克苏本地网',
mapName: 'akesudiqu'
},
{
pid: '111',
id: '111.999',
label: '伊犁本地网',
value: '伊犁本地网',
mapName: 'yilihasakezizhizhou'
},
{
pid: '111',
id: '111.909',
label: '博州本地网',
value: '博州本地网',
mapName: 'boertalamenggu'
},
{
pid: '111',
id: '111.995',
label: '吐鲁番本地网',
value: '吐鲁番本地网',
mapName: 'tulufanshi'
},
{
pid: '111',
id: '111.906',
label: '阿勒泰本地网',
value: '阿勒泰本地网',
mapName: 'aletaidiqu'
}
]
}
]
// 页面数据
<script setup lang="ts">
import { Connection, Download, FullScreen, Refresh, Search } from '@element-plus/icons-vue'
import { onMounted, reactive, ref, nextTick } from 'vue'
import {
localNetworks,
constructionTypeData,
customerTypeData,
regionData,
urbanRuralIndicatorData,
professionalTypeData
} from '@/views/leaderWindowPage/menu'
import { ElMessage, type FormRules } from 'element-plus'
import { timeFormat } from '@/utils/publicMethods'
import amplifyDialog from '@/components/amplifyDialog/index.vue'
import XinJiangMap from '@/components/EchartsMapMinMax/index.vue'
import EchartsColLine from '@/components/EchartsColLine/index.vue'
import { useUserStore } from '@/stores'
import {
getChartDataApi,
getGridExcelApi,
getMapDataApi,
getStatisticsDataApi
} from '@/api/leaderApi'
import { postLogMethod } from '@/utils/logMethod'
import { useRouter } from 'vue-router'
// 鉴权
const store = useUserStore()
const hxArry = localNetworks(store.getUser().hxNumber)
// console.log(hxArry)
// 鉴权
const authentication = () => {
if (!hxArry[0].children.length)
return ElMessage({ message: '暂无本地网授权,请联系管理员授权', type: 'error' })
}
const ruleFormRef = ref<any>()
interface RuleForm {
date: any[]
bdw: string[]
}
const rules = reactive<FormRules<RuleForm>>({
date: [{ required: true, message: '请选择数据日期', trigger: 'blur' }],
bdw: [{ required: true, message: '请选择本地网', trigger: 'blur' }]
})
const formData = ref({
date: [timeFormat().dayBeforeYesterdayStr, timeFormat().dayBeforeYesterdayStr],
latnId: '',
urban: '',
customer: '',
construction: [],
professional: [],
bdw: hxArry[0].children.map((item: any) => item.value),
page: 1,
rows: 20,
total: 0
})
const refreshData = ref({
date: [timeFormat().dayBeforeYesterdayStr, timeFormat().dayBeforeYesterdayStr],
latnId: '',
urban: '',
customer: '',
construction: [],
professional: [],
bdw: hxArry[0].children.map((item: any) => item.value),
page: 1,
rows: 20,
total: 0
})
const shortcuts = [
{
text: '最近一周',
value: () => {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
return [start, end]
}
},
{
text: '最近一月',
value: () => {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
return [start, end]
}
},
{
text: '最近三月',
value: () => {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
return [start, end]
}
}
]
// 显示那块地图
const mapShow = ref('xinjiang')
// 查询
const searchBtn = () => {
authentication()
if (!hxArry[0].children.length) return
if (formData.value.bdw.length > 1) {
mapShow.value = 'xinjiang'
} else {
const mapArea = regionData[0].children.find((item: any) => item.value === formData.value.bdw[0])
mapShow.value = mapArea?.mapName as string
}
getMapData()
getColLineData()
getTableList()
}
// 重置
const refreshBtn = () => {
formData.value = refreshData.value
ruleFormRef.value?.resetFields()
}
// 获得请求体数据
const getRequestBodyData = () => {
const params = {
// queryData: [
// timeFormat(formData.value.date[0]).YYYYMMDD,
// timeFormat(formData.value.date[1]).YYYYMMDD
// ],
startTime: timeFormat(formData.value.date[0]).YYYYMMDD,
endTime: timeFormat(formData.value.date[1]).YYYYMMDD,
bdw: formData.value.bdw,
urban: formData.value.urban,
customer: formData.value.customer,
professional: formData.value.professional.length ? formData.value.professional : null,
construction: formData.value.construction.length ? formData.value.construction : null
}
return params
}
// 地图数据请求 =====================================
const xinJiangMapRef = ref<any>(null)
// 地图数据 图表需要
const mapDatas = ref<any>([])
// 工单数据
const orderData = ref<any>([])
// 占比数据
const proportionData = ref<any>([])
const getMapData = async () => {
const getDataFn = (info: any) => {
const name = info.map((item: any) => item.name.replace('本地网', ''))
const value = info.map((item: any) => item.value)
const arryInfo = [{ name, value }]
return {
name,
value,
arryInfo
}
}
await getMapDataApi(getRequestBodyData())
.then((res: any) => {
// console.log(res)
if (res.code === 0) {
if (res.data.num === 1) {
orderData.value = res.data.mapsumData
proportionData.value = res.data.mapData
mapDatas.value = orderData.value
} else {
orderData.value = getDataFn(res.data.mapsumData).arryInfo
proportionData.value = getDataFn(res.data.mapData).arryInfo
mapDatas.value = orderData.value
}
// console.log('xxxx', mapDatas.value)
nextTick(() => {
xinJiangMapRef.value?.resizeMapEcharts()
})
} else {
ElMessage({ message: '地图数据获取失败,请稍后再试', type: 'error' })
}
})
.catch(() => {
ElMessage({ message: '地图数据获取错误,请稍后再试', type: 'error' })
})
}
const btnColor = ref(true)
const tooltipName = ref('工单总量(单)')
const cgtabBtn = (type: string) => {
if (type === '工单总量') {
tooltipName.value = '工单总量(单)'
btnColor.value = true
mapDatas.value = orderData.value
nextTick(() => {
xinJiangMapRef.value?.resizeMapEcharts()
})
} else if (type === '改约占比') {
tooltipName.value = '改约占比(%)'
btnColor.value = false
mapDatas.value = proportionData.value
nextTick(() => {
xinJiangMapRef.value?.resizeMapEcharts()
})
}
}
// 改约分析数据请求 =========================================
const echartsColLineRef = ref<any>(null)
const colLineData = ref<any>({
xdata: [],
ydata: [
{
data: [],
name: '一次'
},
{
data: [],
name: '二次'
},
{
data: [],
name: '三次'
},
{
data: [],
name: '四次'
},
{
data: [],
name: '五次'
},
{
data: [],
name: '五次以上'
}
]
})
const showGaiYue = ref(false)
const getColLineData = async () => {
await getChartDataApi(getRequestBodyData())
.then((res: any) => {
console.log('getColLineData', res)
if (res.code === 0) {
if (!res.data.result.xdata.length) {
colLineData.value
}
colLineData.value = res.data.result
nextTick(() => {
echartsColLineRef.value?.resizeMapEcharts()
})
} else {
ElMessage({ message: '改约分析数据获取失败,请稍后再试', type: 'error' })
}
})
.catch(() => {
ElMessage({ message: '改约分析数据获取错误,请稍后再试', type: 'error' })
})
}
const loading = ref(false)
const tableList = ref<any>([])
const getTableList = async () => {
loading.value = true
await getStatisticsDataApi(getRequestBodyData())
.then((res: any) => {
console.log(res)
if (res.code === 0) {
formData.value.total = res.data.totalCount
tableList.value = res.data.list
}
})
.catch(() => {
ElMessage({ message: '改约分析表格数据获取错误,请稍后再试', type: 'error' })
})
.finally(() => {
loading.value = false
})
}
const sizeChange = (val: number) => {
formData.value.page = 1
formData.value.rows = val
}
const currentChange = (val: number) => {
formData.value.page = val
}
const downloadBtn = async () => {
const params = {
fileType: 'xlsx',
containHidden: false,
remote: false,
showLoading: true,
dbName: 'doris',
fileName: '改约分析统计数据',
waterMark: 'xjgz ' + timeFormat(new Date()).YYYYMMDD,
userId: 'xjgz',
data: JSON.stringify(tableList.value),
title: `${formData.value.bdw.length > 1 ? '本地网' : '分局'},1,1,false;工单总量,1,1,false;改约工单量,1,1,false;改约占比(%),1,1,false;改约1次,1,1,false;改约2次,1,1,false;改约3次,1,1,false;改约4次,1,1,false;改约5次,1,1,false;改约5次以上,1,1,false;`,
columns: JSON.stringify([
formData.value.bdw.length > 1 ? 'bdw' : 'fjName',
'count',
'updateCount',
'proportion',
'one',
'two',
'three',
'four',
'five',
'fiveup'
])
}
await getGridExcelApi(params)
.then(async (res: any) => {
// console.log(res)
if (res) {
postLogMethod('改约监测(综调)改约分析统计数据excel导出')
if (res == 'error') return ElMessage({ message: '数据导出处理失败', type: 'error' })
const link = document.createElement('a')
link.style.display = 'none'
link.href =
import.meta.env.VITE_APP_URL +
'/common/getGridExcel?fileId=' +
res +
'&fileName=' +
encodeURI('改约分析统计数据') +
'&fileType=xlsx&remote=false'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
})
.finally(() => {
// exportLoading.value = false
})
}
const router = useRouter()
const gotoPage = () => {
// 跳转路由并且携带数据
router.push({
name: 'DongTaiJianKong/GaiYueJianCeZongTiaoXiangQingYe',
query: {
data: JSON.stringify(formData.value)
}
})
}
onMounted(() => {
authentication()
getMapData()
getColLineData()
})
</script>
<template>
<div class="contentEchartsPage">
<el-scrollbar class="scrollbarPageBox">
<div class="formBox">
<el-form :inline="true" ref="ruleFormRef" :rules="rules" :model="formData">
<el-form-item label="数据日期:" prop="date">
<el-date-picker
v-model="formData.date"
type="daterange"
unlink-panels
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
:shortcuts="shortcuts"
size="default"
style="width: 260px"
clearable
/>
</el-form-item>
<el-form-item label="城乡标识:">
<el-select
v-model="formData.urban"
clearable
placeholder="请选择城乡标识"
style="width: 160px"
>
<el-option
v-for="item in urbanRuralIndicatorData"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="开通专业类型:">
<el-select-v2
v-model="formData.professional"
:props="{
value: 'profCode',
label: 'profName'
}"
:options="professionalTypeData"
placeholder="请选择开通专业类型"
collapse-tags
style="width: 280px"
multiple
clearable
/>
</el-form-item>
<el-form-item label="客户类型:">
<el-select
v-model="formData.customer"
clearable
placeholder="请选择客户类型"
style="width: 160px"
>
<el-option
v-for="item in customerTypeData"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="施工类型:">
<el-select-v2
v-model="formData.construction"
:props="{
value: 'id',
label: 'name'
}"
:options="constructionTypeData"
placeholder="请选择施工类型"
collapse-tags
style="width: 220px"
multiple
clearable
/>
</el-form-item>
<el-form-item label="本地网:" prop="bdw">
<el-tree-select
v-model="formData.bdw"
style="width: 200px"
:data="hxArry"
multiple
:render-after-expand="false"
collapse-tags
show-checkbox
clearable
/>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="searchBtn">查询</el-button>
<el-button :icon="Refresh" @click="refreshBtn">重置</el-button>
</el-form-item>
</el-form>
</div>
<el-row :gutter="8" class="boxStyle">
<el-col :span="12">
<div class="conBox">
<div class="titleBox">
<i class="line"></i>
<span class="title">改约监控地图</span>
</div>
<div style="height: 600px; position: relative">
<el-button-group style="position: absolute; top: 0; right: 8px; z-index: 99">
<el-button @click="cgtabBtn('工单总量')" :type="btnColor ? 'primary' : ''">
工单总量
</el-button>
<el-button @click="cgtabBtn('改约占比')" :type="btnColor ? '' : 'primary'">
改约占比
</el-button>
</el-button-group>
<XinJiangMap
ref="xinJiangMapRef"
:map-data-info="mapDatas"
:map-area="mapShow"
:tooltip-name="tooltipName"
></XinJiangMap>
</div>
</div>
</el-col>
<el-col :span="12">
<div class="conBox">
<div class="titleBox">
<i class="line"></i>
<span class="title">改约分析</span>
<div class="options">
<el-button
@click="showGaiYue = !showGaiYue"
link
type="primary"
:icon="FullScreen"
style="font-size: 20px"
></el-button>
</div>
</div>
<div style="height: 600px">
<EchartsColLine
ref="echartsColLineRef"
:bar-num="2"
:col-line-data="colLineData"
download-name="改约分析"
></EchartsColLine>
<!-- <div class="columnarLineBox" ref="columnarLineEchartsRef"></div> -->
</div>
</div>
</el-col>
</el-row>
<el-row :gutter="8" class="boxStyle">
<el-col :span="24">
<div class="conBox">
<div class="titleBox">
<i class="line"></i>
<span class="title">改约分析</span>
<div class="options">
<el-button
@click="gotoPage()"
link
type="primary"
:icon="Connection"
style="font-size: 14px; margin-right: 20px"
>改约清单表</el-button
>
<el-button
@click="downloadBtn"
link
type="primary"
:icon="Download"
style="font-size: 20px"
></el-button>
</div>
</div>
<div>
<el-table
ref="singleTableRef"
class="TableBox"
v-loading="loading"
highlight-current-row
:data="tableList"
border
>
<el-table-column
v-if="formData.bdw.length > 1"
prop="bdw"
label="本地网"
min-width="160"
align="center"
/>
<el-table-column v-else prop="fjName" label="分局" min-width="160" align="center" />
<el-table-column prop="count" label="工单总量" min-width="120" align="center" />
<el-table-column
prop="updateCount"
label="改约工单量"
min-width="120"
align="center"
/>
<el-table-column
prop="proportion"
label="改约占比(%)"
min-width="120"
align="center"
/>
<el-table-column prop="one" label="改约1次" min-width="60" align="center" />
<el-table-column prop="two" label="改约2次" min-width="60" align="center" />
<el-table-column prop="three" label="改约3次" min-width="60" align="center" />
<el-table-column prop="four" label="改约4次" min-width="60" align="center" />
<el-table-column prop="five" label="改约5次" min-width="60" align="center" />
<el-table-column prop="fiveup" label="改约5次以上" min-width="60" align="center" />
<!-- gr_score -->
</el-table>
<el-pagination
class="paginationBox"
v-model:current-page="formData.page"
v-model:page-size="formData.rows"
:page-sizes="[20, 30, 40, 50]"
size="default"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="formData.total"
@size-change="sizeChange"
@current-change="currentChange"
></el-pagination>
</div>
</div>
</el-col>
</el-row>
</el-scrollbar>
</div>
<amplifyDialog v-model:is-show="showGaiYue">
<template #default>
<EchartsColLine
:bar-num="2"
:col-line-data="colLineData"
download-name="改约分析"
></EchartsColLine>
</template>
</amplifyDialog>
</template>
<style scoped lang="less"></style>