feat: 抽取PR地图下钻代码

This commit is contained in:
奔跑的面条 2023-09-16 16:52:55 +08:00
parent ea179f9897
commit 19165c76d5
4 changed files with 369 additions and 167 deletions

View File

@ -11,7 +11,10 @@ export const option = {
dataset: dataJson, dataset: dataJson,
mapRegion: { mapRegion: {
adcode: 'china', adcode: 'china',
showHainanIsLands: true showHainanIsLands: true,
enter: false,
backSize: 20,
backColor: '#ffffff'
}, },
tooltip: { tooltip: {
show: true, show: true,
@ -103,19 +106,19 @@ export const option = {
borderColor: 'rgba(147, 235, 248, 0.8)', borderColor: 'rgba(147, 235, 248, 0.8)',
textStyle: { textStyle: {
color: '#FFFFFF', color: '#FFFFFF',
fontSize: 12, fontSize: 12
} }
}, },
label: { label: {
show: false, show: false,
color: '#FFFFFF', color: '#FFFFFF',
fontSize: 12, fontSize: 12
}, },
emphasis: { emphasis: {
disabled: false, disabled: false,
label: { label: {
color: '#FFFFFF', color: '#FFFFFF',
fontSize: 12, fontSize: 12
}, },
itemStyle: { itemStyle: {
areaColor: '#389BB7', areaColor: '#389BB7',
@ -148,6 +151,26 @@ export const option = {
shadowOffsetY: 2, shadowOffsetY: 2,
shadowBlur: 10 shadowBlur: 10
} }
},
{
type: 'lines',
zlevel: 2,
effect: {
show: true,
period: 4, //箭头指向速度,值越小速度越快
trailLength: 0.4, //特效尾迹长度[0,1]值越大,尾迹越长重
symbol: 'arrow', //箭头图标
symbolSize: 7 //图标大小
},
lineStyle: {
normal: {
color: '#4fb6d2',
width: 1, //线条宽度
opacity: 0.1, //尾迹线条透明度
curveness: 0.3 //尾迹线条曲直度
}
},
data: []
} }
] ]
} }

View File

@ -69,11 +69,7 @@
</n-space> </n-space>
</SettingItem> </SettingItem>
<SettingItem name="字体颜色"> <SettingItem name="字体颜色">
<n-color-picker <n-color-picker size="small" :modes="['hex']" v-model:value="seriesList[1].label.color"></n-color-picker>
size="small"
:modes="['hex']"
v-model:value="seriesList[1].label.color"
></n-color-picker>
</SettingItem> </SettingItem>
<SettingItem name="字体大小"> <SettingItem name="字体大小">
<n-input-number <n-input-number
@ -129,7 +125,7 @@
></n-color-picker> ></n-color-picker>
</SettingItem> </SettingItem>
</SettingItemBox> </SettingItemBox>
<SettingItemBox name="悬浮弹窗"> <SettingItemBox name="悬浮弹窗">
<SettingItem name="显示"> <SettingItem name="显示">
<n-space> <n-space>
@ -180,6 +176,22 @@
<SettingItem> <SettingItem>
<n-checkbox v-model:checked="mapRegion.showHainanIsLands" size="small">显示南海群岛</n-checkbox> <n-checkbox v-model:checked="mapRegion.showHainanIsLands" size="small">显示南海群岛</n-checkbox>
</SettingItem> </SettingItem>
<SettingItem v-if="seriesList[2]">
<n-checkbox v-model:checked="mapRegion.enter" size="small">点击进入下级</n-checkbox>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="返回图标" v-if="mapRegion.enter">
<SettingItem name="颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="mapRegion.backColor"></n-color-picker>
</SettingItem>
<SettingItem name="大小">
<n-input-number
v-model:value="mapRegion.backSize"
:min="1"
size="small"
placeholder="请输入字体大小"
></n-input-number>
</SettingItem>
</SettingItemBox> </SettingItemBox>
</CollapseItem> </CollapseItem>
<CollapseItem name="标记" :expanded="true"> <CollapseItem name="标记" :expanded="true">
@ -191,7 +203,7 @@
<n-color-picker size="small" :modes="['hex']" v-model:value="seriesList[0].itemStyle.color"></n-color-picker> <n-color-picker size="small" :modes="['hex']" v-model:value="seriesList[0].itemStyle.color"></n-color-picker>
</SettingItem> </SettingItem>
</SettingItemBox> </SettingItemBox>
<SettingItemBox name="文本"> <SettingItemBox name="文本">
<SettingItem name="显示"> <SettingItem name="显示">
<n-space> <n-space>
@ -223,6 +235,47 @@
</SettingItem> </SettingItem>
</SettingItemBox> </SettingItemBox>
</CollapseItem> </CollapseItem>
<CollapseItem v-if="seriesList[2]" name="飞线" :expanded="true">
<SettingItemBox name="箭头">
<SettingItem name="速度">
<n-tooltip trigger="hover">
<template #trigger>
<n-input-number v-model:value="seriesList[2].effect.period" size="small" :min="0"></n-input-number>
</template>
值越小速度越快
</n-tooltip>
</SettingItem>
<SettingItem name="尾迹">
<n-tooltip trigger="hover">
<template #trigger>
<n-input-number
v-model:value="seriesList[2].effect.trailLength"
size="small"
:min="0"
:max="1"
></n-input-number>
</template>
特效尾迹长度[0,1]值越大尾迹越长重
</n-tooltip>
</SettingItem>
<SettingItem name="大小">
<n-input-number v-model:value="seriesList[2].effect.symbolSize" size="small" :min="0"></n-input-number>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="配置">
<SettingItem name="颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="seriesList[2].lineStyle.normal.color"
></n-color-picker>
</SettingItem>
<SettingItem name="宽度">
<n-input-number v-model:value="seriesList[2].lineStyle.normal.width" size="small" :min="1"></n-input-number>
</SettingItem>
</SettingItemBox>
</CollapseItem>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -21,6 +21,32 @@
"value": [126.642464, 45.756967, 101] "value": [126.642464, 45.756967, 101]
} }
], ],
"line": [
{
"coords": [
[113.665412, 34.757975],
[116.405285, 39.904989]
]
},
{
"coords": [
[101.778916, 36.623178],
[116.405285, 39.904989]
]
},
{
"coords": [
[106.278179, 38.46637],
[116.405285, 39.904989]
]
},
{
"coords": [
[126.642464, 45.756967],
[116.405285, 39.904989]
]
}
],
"map": [ "map": [
{ {
"name": "北京市", "name": "北京市",

View File

@ -1,156 +1,256 @@
<template> <template>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option.value" :manual-update="isPreview()" autoresize> <div>
</v-chart> <div class="back-icon" v-if="(enter && levelHistory.length !== 0) || (enter && !isPreview())" @click="backLevel">
</template> <n-icon :color="backColor" :size="backSize * 1.1">
<ArrowBackIcon />
<script setup lang="ts"> </n-icon>
import { PropType, reactive, watch, ref, nextTick } from 'vue' <span
import config, { includes } from './config' :style="{
import VChart from 'vue-echarts' 'font-weight': 200,
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook' color: backColor,
import { use, registerMap } from 'echarts/core' 'font-size': `${backSize}px`
import { EffectScatterChart, MapChart } from 'echarts/charts' }"
import { CanvasRenderer } from 'echarts/renderers' >
import { useChartDataFetch } from '@/hooks' 返回上级
import { mergeTheme, setOption } from '@/packages/public/chart' </span>
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' </div>
import { isPreview } from '@/utils' <v-chart
import mapJsonWithoutHainanIsLands from './mapWithoutHainanIsLands.json' ref="vChartRef"
import { DatasetComponent, GridComponent, TooltipComponent, GeoComponent, VisualMapComponent } from 'echarts/components' :init-options="initOptions"
:theme="themeColor"
const props = defineProps({ :option="option.value"
themeSetting: { :manual-update="isPreview()"
type: Object, autoresize
required: true @click="chartPEvents"
}, >
themeColor: { </v-chart>
type: Object, </div>
required: true </template>
},
chartConfig: { <script setup lang="ts">
type: Object as PropType<config>, import { PropType, reactive, watch, ref, nextTick, toRefs } from 'vue'
required: true import config, { includes } from './config'
} import VChart from 'vue-echarts'
}) import { icon } from '@/plugins'
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting) import { use, registerMap } from 'echarts/core'
import { EffectScatterChart, MapChart } from 'echarts/charts'
use([ import { CanvasRenderer } from 'echarts/renderers'
MapChart, import { useChartDataFetch } from '@/hooks'
DatasetComponent, import { mergeTheme, setOption } from '@/packages/public/chart'
CanvasRenderer, import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
GridComponent, import { isPreview } from '@/utils'
TooltipComponent, import mapJsonWithoutHainanIsLands from './mapWithoutHainanIsLands.json'
GeoComponent, import mapChinaJson from './mapGeojson/china.json'
EffectScatterChart, import { DatasetComponent, GridComponent, TooltipComponent, GeoComponent, VisualMapComponent } from 'echarts/components'
VisualMapComponent
]) const props = defineProps({
themeSetting: {
const option = reactive({ type: Object,
value: mergeTheme(props.chartConfig.option, props.themeSetting, includes) required: true
}) },
const vChartRef = ref<typeof VChart>() themeColor: {
type: Object,
//json required: true
const getGeojson = (regionId: string) => { },
return new Promise<boolean>(resolve => { chartConfig: {
import(`./mapGeojson/${regionId}.json`).then(data => { type: Object as PropType<config>,
registerMap(regionId, { geoJSON: data.default as any, specialAreas: {} }) required: true
resolve(true) }
}) })
})
} const { ArrowBackIcon } = icon.ionicons5
let levelHistory: any = ref([])
//
registerMap(`${props.chartConfig.option.mapRegion.adcode}`, { geoJSON: {} as any, specialAreas: {} }) const { backColor, backSize, enter } = toRefs(props.chartConfig.option.mapRegion)
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
// china
const registerMapInitAsync = async () => { use([
await nextTick() MapChart,
const adCode = `${props.chartConfig.option.mapRegion.adcode}`; DatasetComponent,
if (adCode !== 'china') { CanvasRenderer,
await getGeojson(adCode) GridComponent,
} else { TooltipComponent,
await hainanLandsHandle(props.chartConfig.option.mapRegion.showHainanIsLands) GeoComponent,
} EffectScatterChart,
vEchartsSetOption() VisualMapComponent
} ])
registerMapInitAsync()
const option = reactive({
// value: mergeTheme(props.chartConfig.option, props.themeSetting, includes)
const vEchartsSetOption = () => { })
option.value = props.chartConfig.option const vChartRef = ref<typeof VChart>()
setOption(vChartRef.value, props.chartConfig.option)
} //json
const getGeojson = (regionId: string) => {
// return new Promise<boolean>(resolve => {
const dataSetHandle = async (dataset: any) => { import(`./mapGeojson/${regionId}.json`).then(data => {
props.chartConfig.option.series.forEach((item: any) => { registerMap(regionId, { geoJSON: data.default as any, specialAreas: {} })
if (item.type === 'effectScatter' && dataset.point) item.data = dataset.point resolve(true)
else if (item.type === 'map' && dataset.map) item.data = dataset.map })
}) })
if (dataset.pieces) props.chartConfig.option.visualMap.pieces = dataset.pieces }
isPreview() && vEchartsSetOption() //
} registerMap(`${props.chartConfig.option.mapRegion.adcode}`, { geoJSON: {} as any, specialAreas: {} })
//
const hainanLandsHandle = async (newData: boolean) => { // china
if (newData) { const registerMapInitAsync = async () => {
await getGeojson('china') await nextTick()
} else { const adCode = `${props.chartConfig.option.mapRegion.adcode}`
registerMap('china', { geoJSON: mapJsonWithoutHainanIsLands as any, specialAreas: {} }) if (adCode !== 'china') {
} await getGeojson(adCode)
} } else {
// dataset await hainanLandsHandle(props.chartConfig.option.mapRegion.showHainanIsLands)
watch( }
() => props.chartConfig.option.dataset, vEchartsSetOption()
newData => { }
dataSetHandle(newData) registerMapInitAsync()
},
{ //
immediate: true, const vEchartsSetOption = () => {
deep: false option.value = props.chartConfig.option
} setOption(vChartRef.value, props.chartConfig.option)
) }
// //
watch( const dataSetHandle = async (dataset: any) => {
() => props.chartConfig.option.mapRegion.showHainanIsLands, props.chartConfig.option.series.forEach((item: any) => {
async newData => { if (item.type === 'effectScatter' && dataset.point) item.data = dataset.point
try { else if (item.type === 'lines' && dataset.line) {
await hainanLandsHandle(newData) item.data = dataset.line.map((it: any) => {
vEchartsSetOption() return {
} catch (error) { ...it,
console.log(error) lineStyle: {
} color: props.chartConfig.option.series[2].lineStyle.normal.color
}, }
{ }
deep: false })
} } else if (item.type === 'map' && dataset.map) item.data = dataset.map
) })
if (dataset.pieces) props.chartConfig.option.visualMap.pieces = dataset.pieces
//
watch( isPreview() && vEchartsSetOption()
() => `${props.chartConfig.option.mapRegion.adcode}`, }
async newData => { //
try { const hainanLandsHandle = async (newData: boolean) => {
await getGeojson(newData) if (newData) {
props.chartConfig.option.geo.map = newData await getGeojson('china')
props.chartConfig.option.series.forEach((item: any) => { } else {
if (item.type === 'map') item.map = newData registerMap('china', { geoJSON: mapJsonWithoutHainanIsLands as any, specialAreas: {} })
}) }
vEchartsSetOption() }
} catch (error) {
console.log(error) //
} const chartPEvents = (e: any) => {
}, if (e.seriesType !== 'map') return
{ if (!props.chartConfig.option.mapRegion.enter) {
deep: false return
} }
) mapChinaJson.features.forEach(item => {
var pattern = new RegExp(e.name)
// if (pattern.test(item.properties.name)) {
useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => { let code = String(item.properties.adcode)
dataSetHandle(newData) levelHistory.value.push(code)
}) checkOrMap(code)
</script> }
})
}
//
const backLevel = () => {
levelHistory.value = []
if (levelHistory.value.length > 1) {
levelHistory.value.pop()
const code = levelHistory[levelHistory.value.length - 1]
checkOrMap(code)
} else {
checkOrMap('china')
}
}
//
const checkOrMap = async (newData: string) => {
await getGeojson(newData)
props.chartConfig.option.geo.map = newData
props.chartConfig.option.series.forEach((item: any) => {
if (item.type === 'map') item.map = newData
})
vEchartsSetOption()
}
// dataset
watch(
() => props.chartConfig.option.dataset,
newData => {
dataSetHandle(newData)
},
{
immediate: true,
deep: false
}
)
// 线
if (props.chartConfig.option.series[2] && !isPreview()) {
watch(
() => props.chartConfig.option.series[2].lineStyle.normal.color,
() => {
dataSetHandle(props.chartConfig.option.dataset)
},
{
deep: false
}
)
}
//
if (!isPreview()) {
watch(
() => props.chartConfig.option.mapRegion.showHainanIsLands,
async newData => {
try {
await hainanLandsHandle(newData)
vEchartsSetOption()
} catch (error) {
console.log(error)
}
},
{
deep: false
}
)
}
//
watch(
() => `${props.chartConfig.option.mapRegion.adcode}`,
newData => {
try {
checkOrMap(newData)
} catch (error) {
console.log(error)
}
},
{
deep: false
}
)
//
useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
dataSetHandle(newData)
})
</script>
<style scope lang="scss">
.back-icon {
z-index: 50;
cursor: pointer;
position: absolute;
display: flex;
align-items: center;
top: 0;
left: 0;
gap: 2px;
}
</style>