Showing
48 changed files
with
2095 additions
and
252 deletions
src/assets/images/chart/charts/func_line.png
0 → 100644
78.5 KB
390 KB
387 KB
... | ... | @@ -24,6 +24,7 @@ |
24 | 24 | class="rows" |
25 | 25 | :style="`height: ${h - (status.header.length ? status.mergedConfig.headerHeight : 0)}px;`" |
26 | 26 | > |
27 | + <div v-if="status.rows.length"> | |
27 | 28 | <div |
28 | 29 | class="row-item" |
29 | 30 | v-for="(row, ri) in status.rows" |
... | ... | @@ -43,6 +44,10 @@ |
43 | 44 | v-html="ceil" |
44 | 45 | /> |
45 | 46 | </div> |
47 | + </div> | |
48 | + <div v-else class="nullData"> | |
49 | + 暂无数据 | |
50 | + </div> | |
46 | 51 | </div> |
47 | 52 | </div> |
48 | 53 | </template> |
... | ... | @@ -384,5 +389,14 @@ onUnmounted(() => { |
384 | 389 | overflow: hidden; |
385 | 390 | } |
386 | 391 | } |
392 | + .nullData{ | |
393 | + display: flex; | |
394 | + justify-content: center; | |
395 | + align-items: center; | |
396 | + color:white; | |
397 | + height: 100%; | |
398 | + width: 100%; | |
399 | + background: #356b80; | |
400 | + } | |
387 | 401 | } |
388 | 402 | </style> | ... | ... |
1 | +import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public' | |
2 | +import { AddLinePlotConfig } from './index' | |
3 | +import { CreateComponentType } from '@/packages/index.d' | |
4 | +import cloneDeep from 'lodash/cloneDeep' | |
5 | +import dataJson from './data.json' | |
6 | + | |
7 | +export const includes = ['legend', 'xAxis', 'yAxis', 'grid'] | |
8 | +export const seriesItem = { | |
9 | + type: 'line', | |
10 | + showSymbol: false, | |
11 | + clip: true, | |
12 | + data: dataJson | |
13 | +} | |
14 | + | |
15 | +export const option = { | |
16 | + animation: false, | |
17 | + grid: { | |
18 | + top: 40, | |
19 | + left: 50, | |
20 | + right: 40, | |
21 | + bottom: 50 | |
22 | + }, | |
23 | + xAxis: { | |
24 | + name: 'x', | |
25 | + minorTick: { | |
26 | + show: true | |
27 | + }, | |
28 | + minorSplitLine: { | |
29 | + show: true | |
30 | + } | |
31 | + }, | |
32 | + yAxis: { | |
33 | + name: 'y', | |
34 | + minorTick: { | |
35 | + show: true | |
36 | + }, | |
37 | + minorSplitLine: { | |
38 | + show: true | |
39 | + } | |
40 | + }, | |
41 | + dataZoom: [ | |
42 | + { | |
43 | + show: true, | |
44 | + type: 'inside', | |
45 | + filterMode: 'none', | |
46 | + xAxisIndex: [0], | |
47 | + startValue: -20, | |
48 | + endValue: 20 | |
49 | + }, | |
50 | + { | |
51 | + show: true, | |
52 | + type: 'inside', | |
53 | + filterMode: 'none', | |
54 | + yAxisIndex: [0], | |
55 | + startValue: -20, | |
56 | + endValue: 20 | |
57 | + } | |
58 | + ], | |
59 | + dataset:dataJson, | |
60 | + series: [seriesItem] | |
61 | +} | |
62 | + | |
63 | +export default class Config extends PublicConfigClass implements CreateComponentType { | |
64 | + public key: string = AddLinePlotConfig.key | |
65 | + public chartConfig = cloneDeep(AddLinePlotConfig) | |
66 | + // 图表配置项 | |
67 | + public option = echartOptionProfixHandle(option, includes) | |
68 | +} | ... | ... |
1 | +<template> | |
2 | + <!-- Echarts 全局设置 --> | |
3 | + <global-setting :optionData="optionData"></global-setting> | |
4 | + <CollapseItem v-for="(item, index) in seriesList" :key="index" :name="`函数绘图-${index + 1}`" :expanded="true"> | |
5 | + <setting-item-box name="显示线标点"> | |
6 | + <setting-item> | |
7 | + <n-space> | |
8 | + <n-switch v-model:value="item.showSymbol" size="small" /> | |
9 | + <n-text>显示线标点</n-text> | |
10 | + </n-space> | |
11 | + </setting-item> | |
12 | + <setting-item> | |
13 | + <n-space> | |
14 | + <n-switch v-model:value="item.clip" size="small" /> | |
15 | + <n-text>显示修剪</n-text> | |
16 | + </n-space> | |
17 | + </setting-item> | |
18 | + </setting-item-box> | |
19 | + </CollapseItem> | |
20 | +</template> | |
21 | + | |
22 | +<script setup lang="ts"> | |
23 | +import { PropType, computed } from 'vue' | |
24 | +import { GlobalThemeJsonType } from '@/settings/chartThemes/index' | |
25 | +import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' | |
26 | + | |
27 | +const props = defineProps({ | |
28 | + optionData: { | |
29 | + type: Object as PropType<GlobalThemeJsonType>, | |
30 | + required: true | |
31 | + } | |
32 | +}) | |
33 | + | |
34 | +const seriesList = computed(() => { | |
35 | + return props.optionData.series | |
36 | +}) | |
37 | +</script> | ... | ... |
1 | +[ | |
2 | + [ | |
3 | + -1.5999999999999996, | |
4 | + -6.18608508879996 | |
5 | + ], | |
6 | + [ | |
7 | + -1.4999999999999996, | |
8 | + -5.713593654471708 | |
9 | + ], | |
10 | + [ | |
11 | + -1.3999999999999995, | |
12 | + -5.245243488748958 | |
13 | + ], | |
14 | + [ | |
15 | + -1.2999999999999994, | |
16 | + -4.78285911291312 | |
17 | + ], | |
18 | + [ | |
19 | + -1.1999999999999993, | |
20 | + -4.328201018063869 | |
21 | + ], | |
22 | + [ | |
23 | + -1.0999999999999992, | |
24 | + -3.8829585351580356 | |
25 | + ], | |
26 | + [ | |
27 | + -0.9999999999999992, | |
28 | + -3.4487430163340895 | |
29 | + ], | |
30 | + [ | |
31 | + -0.8999999999999992, | |
32 | + -3.027081353505773 | |
33 | + ], | |
34 | + [ | |
35 | + -0.7999999999999993, | |
36 | + -2.619409858909628 | |
37 | + ], | |
38 | + [ | |
39 | + -0.6999999999999993, | |
40 | + -2.227068530901179 | |
41 | + ], | |
42 | + [ | |
43 | + -0.5999999999999993, | |
44 | + -1.8512957268186445 | |
45 | + ], | |
46 | + [ | |
47 | + -0.49999999999999933, | |
48 | + -1.4932232631765194 | |
49 | + ], | |
50 | + [ | |
51 | + -0.39999999999999936, | |
52 | + -1.1538719618202085 | |
53 | + ], | |
54 | + [ | |
55 | + -0.2999999999999994, | |
56 | + -0.8341476589728255 | |
57 | + ], | |
58 | + [ | |
59 | + -0.19999999999999937, | |
60 | + -0.5348376923428088 | |
61 | + ], | |
62 | + [ | |
63 | + -0.09999999999999937, | |
64 | + -0.2566078796424947 | |
65 | + ], | |
66 | + [ | |
67 | + 6.38378239159465e-16, | |
68 | + 1.5681617696021829e-15 | |
69 | + ], | |
70 | + [ | |
71 | + 0.10000000000000064, | |
72 | + 0.2345702121634671 | |
73 | + ], | |
74 | + [ | |
75 | + 0.20000000000000065, | |
76 | + 0.4468145531643023 | |
77 | + ], | |
78 | + [ | |
79 | + 0.30000000000000066, | |
80 | + 0.6365733103425596 | |
81 | + ], | |
82 | + [ | |
83 | + 0.4000000000000007, | |
84 | + 0.8038156929453971 | |
85 | + ], | |
86 | + [ | |
87 | + 0.5000000000000007, | |
88 | + 0.9486397319584274 | |
89 | + ], | |
90 | + [ | |
91 | + 0.6000000000000006, | |
92 | + 1.0712716492500896 | |
93 | + ], | |
94 | + [ | |
95 | + 0.7000000000000006, | |
96 | + 1.1720646984075929 | |
97 | + ], | |
98 | + [ | |
99 | + 0.8000000000000006, | |
100 | + 1.2514974816478484 | |
101 | + ] | |
102 | +] | |
\ No newline at end of file | ... | ... |
1 | +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d' | |
2 | +import { ChatCategoryEnum, ChatCategoryEnumName } from '@/packages/components/Charts/index.d' | |
3 | +import { useWidgetKey } from '@/packages/external/useWidgetKey' | |
4 | + | |
5 | +const { key, conKey, chartKey } = useWidgetKey('AddLinePlot', true) | |
6 | + | |
7 | +export const AddLinePlotConfig: ConfigType = { | |
8 | + key, | |
9 | + chartKey, | |
10 | + conKey, | |
11 | + title: '函数绘图', | |
12 | + category: ChatCategoryEnum.LINE, | |
13 | + categoryName: ChatCategoryEnumName.LINE, | |
14 | + package: PackagesCategoryEnum.CHARTS, | |
15 | + chartFrame: ChartFrameEnum.ECHARTS, | |
16 | + image: 'func_line.png' | |
17 | +} | ... | ... |
1 | +<template> | |
2 | + <v-chart | |
3 | + ref="vChartRef" | |
4 | + :init-options="initOptions" | |
5 | + :theme="themeColor" | |
6 | + :option="option" | |
7 | + :manual-update="isPreview()" | |
8 | + :update-options="{ | |
9 | + replaceMerge: replaceMergeArr | |
10 | + }" | |
11 | + autoresize | |
12 | + > | |
13 | + </v-chart> | |
14 | +</template> | |
15 | + | |
16 | +<script setup lang="ts"> | |
17 | +import { PropType, computed, watch, ref, nextTick } from 'vue' | |
18 | +import VChart from 'vue-echarts' | |
19 | +import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook' | |
20 | +import { use } from 'echarts/core' | |
21 | +import { CanvasRenderer } from 'echarts/renderers' | |
22 | +import { LineChart } from 'echarts/charts' | |
23 | +import config, { includes } from './config' | |
24 | +import { mergeTheme } from '@/packages/public/chart' | |
25 | +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' | |
26 | +import { useChartDataFetch } from '@/hooks' | |
27 | +import { isPreview } from '@/utils' | |
28 | +import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components' | |
29 | + | |
30 | +const props = defineProps({ | |
31 | + themeSetting: { | |
32 | + type: Object, | |
33 | + required: true | |
34 | + }, | |
35 | + themeColor: { | |
36 | + type: Object, | |
37 | + required: true | |
38 | + }, | |
39 | + chartConfig: { | |
40 | + type: Object as PropType<config>, | |
41 | + required: true | |
42 | + } | |
43 | +}) | |
44 | + | |
45 | +const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting) | |
46 | + | |
47 | +use([DatasetComponent, CanvasRenderer, LineChart, GridComponent, TooltipComponent, LegendComponent]) | |
48 | + | |
49 | +const replaceMergeArr = ref<string[]>() | |
50 | + | |
51 | +const option = computed(() => { | |
52 | + return mergeTheme(props.chartConfig.option, props.themeSetting, includes) | |
53 | +}) | |
54 | + | |
55 | +// dataset 无法变更条数的补丁 | |
56 | +watch( | |
57 | + () => props.chartConfig.option.dataset, | |
58 | + (newData) => { | |
59 | + try { | |
60 | + if (Array.isArray(newData)) { | |
61 | + replaceMergeArr.value = ['series'] | |
62 | + props.chartConfig.option.series[0].data = newData | |
63 | + nextTick(() => { | |
64 | + replaceMergeArr.value = [] | |
65 | + }) | |
66 | + } | |
67 | + } catch (error) { | |
68 | + console.log(error) | |
69 | + } | |
70 | + }, | |
71 | + { | |
72 | + deep: false | |
73 | + } | |
74 | +) | |
75 | + | |
76 | +const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore) | |
77 | +</script> | ... | ... |
... | ... | @@ -16,22 +16,22 @@ |
16 | 16 | </template> |
17 | 17 | |
18 | 18 | <script setup lang="ts"> |
19 | -import { PropType, computed, watch, ref, onMounted, unref, toRaw, toRefs,nextTick } from 'vue' | |
19 | +import { PropType, computed, watch, ref, onMounted, unref, toRaw, toRefs, nextTick } from 'vue' | |
20 | 20 | import VChart from 'vue-echarts' |
21 | 21 | import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook' |
22 | 22 | import { use } from 'echarts/core' |
23 | 23 | import { CanvasRenderer } from 'echarts/renderers' |
24 | 24 | import { LineChart } from 'echarts/charts' |
25 | -import config, { includes,seriesItem } from './config' | |
25 | +import config, { includes, seriesItem } from './config' | |
26 | 26 | import { mergeTheme } from '@/packages/public/chart' |
27 | 27 | import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' |
28 | -import { useChartDataFetch } from '@/hooks' | |
28 | +import { useChartDataFetch } from '@/hooks/external/useChartDataFetch.hook' | |
29 | 29 | import { isPreview } from '@/utils' |
30 | 30 | import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components' |
31 | 31 | import dataJson from './data.json' |
32 | 32 | import { useFullScreen } from '../../utls/fullScreen' |
33 | 33 | import { SocketReceiveMessageType } from '@/store/external/modules/socketStore.d' |
34 | -import { useAssembleDataHooks } from '@/hooks/external/useAssembleData.hook' | |
34 | +import { useAssembleDataHooks } from './useAssembleData.hook' | |
35 | 35 | import isObject from 'lodash/isObject' |
36 | 36 | import cloneDeep from 'lodash/cloneDeep' |
37 | 37 | |
... | ... | @@ -205,25 +205,25 @@ watch( |
205 | 205 | //fix 修复v-chart图表绑定联动组件视图不更新问题 |
206 | 206 | const updateVChart = (newData: SocketReceiveMessageType) => { |
207 | 207 | if (!newData) return |
208 | + const { option } = props.chartConfig | |
209 | + const { dataset: overrideDataset } = option | |
210 | + const { dimensions } = overrideDataset | |
211 | + if (!Array.isArray(dimensions)) return | |
208 | 212 | const { data } = newData |
209 | - const {keys,record}= useAssembleDataHooks(data) | |
213 | + const { keys, record } = useAssembleDataHooks(data,dimensions) | |
210 | 214 | if (unref(realTimeList).length >= chartMaxDataPoint.value) { |
211 | 215 | unref(realTimeList).splice(0, 1) |
212 | 216 | } |
213 | 217 | realTimeList.value.push(record) |
214 | 218 | const dataset = { |
215 | - dimensions: ['ts', ...keys], | |
216 | - source: toRaw(unref(realTimeList)) | |
219 | + dimensions: ['ts', ...keys], | |
220 | + source: toRaw(unref(realTimeList)) | |
217 | 221 | } |
218 | 222 | vChartRef.value?.setOption({ |
219 | 223 | ...option.value, |
220 | 224 | dataset |
221 | - }, | |
222 | - { | |
223 | - notMerge: true // 修复ws 属性对应属性值不匹配 | |
224 | 225 | }) |
225 | 226 | } |
226 | - | |
227 | 227 | const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, newData => { |
228 | 228 | updateVChart(newData) |
229 | 229 | }) | ... | ... |
src/packages/components/external/Charts/Lines/OverrideLineRealTime/useAssembleData.hook.ts
0 → 100644
1 | +/** | |
2 | + * 此hook的作用是 ws 组装图表需要的数据格式 | |
3 | + * @param data {'wendu':[[xxxxxx,xxxx]]} | |
4 | + * @returns keys:[xx,xx,xx] record:{'x':'xx'} | |
5 | + */ | |
6 | +import dayjs from 'dayjs' | |
7 | + | |
8 | +export const useAssembleDataHooks = (data: Record<string, string | [number, string]>, dimensions: string[]) => { | |
9 | + const formatValueToNumber = (value: string | number, defaultValue = 0) => | |
10 | + isNaN(value as number) ? defaultValue : Number(value) | |
11 | + const tempObj: Recordable = {} | |
12 | + dimensions | |
13 | + .filter(f => f !== 'ts') | |
14 | + .forEach((item: string) => { | |
15 | + Object.assign(tempObj, { | |
16 | + [item]: data[item] | |
17 | + }) | |
18 | + }) | |
19 | + const keys = Object.keys(tempObj) | |
20 | + const record = keys.reduce((prev, next) => { | |
21 | + const [latest] = data[next] || [] | |
22 | + const [ts, value] = latest as unknown as string[] | |
23 | + //把值转为number类型 wendu:"23" 转为 wendu:23 | |
24 | + return { ...prev, ts: dayjs(ts).format('YYYY-MM-DD HH:mm:ss'), [next]: formatValueToNumber(value) } | |
25 | + }, {}) | |
26 | + | |
27 | + return { | |
28 | + keys, | |
29 | + record | |
30 | + } | |
31 | +} | ... | ... |
1 | +import { PublicConfigClass } from '@/packages/public' | |
2 | +import { CreateComponentType } from '@/packages/index.d' | |
3 | +import { AddHistoryTimeTrajectoryAmapConfig } from './index' | |
4 | +import { chartInitConfig } from '@/settings/designSetting' | |
5 | +import cloneDeep from 'lodash/cloneDeep' | |
6 | +import dataJson from './data.json' | |
7 | + | |
8 | +export type dataJsonType = typeof dataJson //data.json类型 | |
9 | + | |
10 | +export enum ThemeEnum { | |
11 | + NORMAL = 'normal', | |
12 | + DARK = 'dark', | |
13 | + LIGHT = 'light', | |
14 | + WHITES_MOKE = 'whitesmoke', | |
15 | + FRESH = 'fresh', | |
16 | + GREY = 'grey', | |
17 | + GRAFFITI = 'graffiti', | |
18 | + MACARON = 'macaron', | |
19 | + BLUE = 'blue', | |
20 | + DARKBLUE = 'darkblue', | |
21 | + WINE = 'wine', | |
22 | + WEIXIN = 'tileLayer' | |
23 | +} | |
24 | + | |
25 | +export enum LangEnum { | |
26 | + ZH_CN = 'zh_cn', | |
27 | + EN = 'en', | |
28 | + ZH_EN = 'zh_en' | |
29 | +} | |
30 | + | |
31 | +export enum ViewModeEnum { | |
32 | + PLANE = '2D', | |
33 | + STEREOSCOPIC = '3D' | |
34 | +} | |
35 | + | |
36 | +export enum FeaturesEnum { | |
37 | + BG = 'bg', | |
38 | + POINT = 'point', | |
39 | + ROAD = 'road', | |
40 | + BUILDING = 'building' | |
41 | +} | |
42 | + | |
43 | +export enum MarkerEnum { | |
44 | + // 圆圈 | |
45 | + CIRCLE_MARKER = 'CircleMarker', | |
46 | + // 定位标点 | |
47 | + MARKER = 'Marker', | |
48 | + // 暂无 | |
49 | + NONE = 'none' | |
50 | +} | |
51 | + | |
52 | +export const option = { | |
53 | + dataset: dataJson, | |
54 | + mapOptions: { | |
55 | + lineColor: '#53A9DE', | |
56 | + lineWidth: 2, | |
57 | + pitch: 60, | |
58 | + skyColor: '#53A9DE', | |
59 | + amapKey: 'd5f3e16589dbecae64d05fe90e2ba4f2', | |
60 | + amapStyleKey: ThemeEnum.DARK, | |
61 | + amapStyleKeyCustom: '', | |
62 | + amapLon: 104.108689, | |
63 | + amapLat: 30.66176, | |
64 | + amapZindex: 11, | |
65 | + iconMarker: '1.png', | |
66 | + lineDuration: 2000, | |
67 | + lineOpacity: 0.5, | |
68 | + lineShowDir: false, | |
69 | + mpBorderConfig: { | |
70 | + value: 'Border01' | |
71 | + }, | |
72 | + bgColor: 'rgba(255, 255, 255, 0.05)', | |
73 | + marker: { | |
74 | + fillColor: '#E98984FF', | |
75 | + fillOpacity: 0.5, | |
76 | + strokeColor: 'white', | |
77 | + strokeWeight: 2, | |
78 | + strokeOpacity: 0.5, | |
79 | + zIndex: 10, | |
80 | + bubble: true, | |
81 | + cursor: 'pointer', | |
82 | + clickable: true | |
83 | + }, | |
84 | + mapMarkerType: MarkerEnum.MARKER, | |
85 | + viewMode: ViewModeEnum.STEREOSCOPIC, | |
86 | + lang: LangEnum.ZH_CN, | |
87 | + features: [FeaturesEnum.BG, FeaturesEnum.POINT, FeaturesEnum.ROAD, FeaturesEnum.BUILDING] | |
88 | + } | |
89 | +} | |
90 | + | |
91 | +export type mapOption = typeof option['mapOptions'] | |
92 | + | |
93 | +export default class Config extends PublicConfigClass implements CreateComponentType { | |
94 | + public key = AddHistoryTimeTrajectoryAmapConfig.key | |
95 | + public attr = { ...chartInitConfig, w: 1000, h: 800, zIndex: -1 } | |
96 | + public chartConfig = cloneDeep(AddHistoryTimeTrajectoryAmapConfig) | |
97 | + public option = cloneDeep(option) | |
98 | +} | ... | ... |
1 | +<template> | |
2 | + <collapse-item name="基础" :expanded="true"> | |
3 | + <setting-item-box name="语言类型" :alone="true"> | |
4 | + <setting-item> | |
5 | + <n-select size="small" v-model:value="optionData.mapOptions.lang" :options="langOptions" /> | |
6 | + </setting-item> | |
7 | + </setting-item-box> | |
8 | + <setting-item-box name="Key" :alone="true"> | |
9 | + <setting-item name="请务必使用自己的高德应用 key"> | |
10 | + <n-input v-model:value="optionData.mapOptions.amapKey" size="small"></n-input> | |
11 | + </setting-item> | |
12 | + </setting-item-box> | |
13 | + <setting-item-box name="自定义地图样式ID" :alone="true"> | |
14 | + <setting-item> | |
15 | + <n-input size="small" v-model:value="optionData.mapOptions.amapStyleKeyCustom" /> | |
16 | + </setting-item> | |
17 | + </setting-item-box> | |
18 | + </collapse-item> | |
19 | + <collapse-item name="地图" :expanded="true"> | |
20 | + <setting-item-box name="主题"> | |
21 | + <setting-item> | |
22 | + <n-select size="small" v-model:value="optionData.mapOptions.amapStyleKey" :options="themeOptions" /> | |
23 | + </setting-item> | |
24 | + </setting-item-box> | |
25 | + <setting-item-box name="内容" :alone="true"> | |
26 | + <n-checkbox-group v-model:value="optionData.mapOptions.features"> | |
27 | + <n-space item-style="display: flex;"> | |
28 | + <n-checkbox :value="item.value" :label="item.label" v-for="(item, index) in featuresOptions" :key="index" /> | |
29 | + </n-space> | |
30 | + </n-checkbox-group> | |
31 | + </setting-item-box> | |
32 | + <setting-item-box name="位置"> | |
33 | + <setting-item name="经度"> | |
34 | + <n-input-number v-model:value="optionData.mapOptions.amapLon" :show-button="false" size="small"> | |
35 | + <template #suffix>°</template> | |
36 | + </n-input-number> | |
37 | + </setting-item> | |
38 | + <setting-item name="纬度"> | |
39 | + <n-input-number v-model:value="optionData.mapOptions.amapLat" :show-button="false" size="small"> | |
40 | + <template #suffix>°</template> | |
41 | + </n-input-number> | |
42 | + </setting-item> | |
43 | + <setting-item name="初始缩放"> | |
44 | + <n-input-number v-model:value="optionData.mapOptions.amapZindex" :min="0" size="small"></n-input-number> | |
45 | + </setting-item> | |
46 | + </setting-item-box> | |
47 | + <setting-item-box name="模式" :alone="true"> | |
48 | + <setting-item> | |
49 | + <n-radio-group v-model:value="optionData.mapOptions.viewMode" name="radiogroup"> | |
50 | + <n-space> | |
51 | + <n-radio v-for="song in viewModeOptions" :key="song.value" :value="song.value"> | |
52 | + {{ song.label }} | |
53 | + </n-radio> | |
54 | + </n-space> | |
55 | + </n-radio-group> | |
56 | + </setting-item> | |
57 | + </setting-item-box> | |
58 | + <template v-if="optionData.mapOptions.viewMode === '3D'"> | |
59 | + <setting-item-box> | |
60 | + <setting-item name="天空色"> | |
61 | + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.mapOptions.skyColor"></n-color-picker> | |
62 | + </setting-item> | |
63 | + <setting-item name="俯仰角"> | |
64 | + <n-input-number v-model:value="optionData.mapOptions.pitch" :min="0" :max="83" size="small"></n-input-number> | |
65 | + </setting-item> | |
66 | + </setting-item-box> | |
67 | + </template> | |
68 | + </collapse-item> | |
69 | + <collapse-item name="标记图标" :expanded="true"> | |
70 | + <setting-item-box name="样式"> | |
71 | + <setting-item> | |
72 | + <NSelect | |
73 | + size="small" | |
74 | + placeholder="请选择您要使用的图标" | |
75 | + style="width: 250px" | |
76 | + :value="iconMarkerValue" | |
77 | + :options="iconMarkerOptions" | |
78 | + :render-label="renderOption" | |
79 | + clearable | |
80 | + filterable | |
81 | + @update:value="selectHandle" | |
82 | + /> | |
83 | + </setting-item> | |
84 | + </setting-item-box> | |
85 | + </collapse-item> | |
86 | + <collapse-item name="标记" :expanded="true"> | |
87 | + <setting-item-box name="样式"> | |
88 | + <setting-item name="类型"> | |
89 | + <n-select size="small" v-model:value="optionData.mapOptions.mapMarkerType" :options="MarkerOptions" /> | |
90 | + </setting-item> | |
91 | + <setting-item v-if="optionData.mapOptions.mapMarkerType === MarkerEnum.CIRCLE_MARKER" name="颜色"> | |
92 | + <n-color-picker v-model:value="optionData.mapOptions.marker.fillColor" size="small"></n-color-picker> | |
93 | + </setting-item> | |
94 | + </setting-item-box> | |
95 | + </collapse-item> | |
96 | + <collapse-item name="线条" :expanded="true"> | |
97 | + <setting-item-box name="样式"> | |
98 | + <setting-item name="颜色"> | |
99 | + <n-color-picker size="small" :show-alpha="false" v-model:value="optionData.mapOptions.lineColor"></n-color-picker> | |
100 | + </setting-item> | |
101 | + <setting-item name="线宽"> | |
102 | + <n-input-number v-model:value="optionData.mapOptions.lineWidth" :min="0" :max="100" size="small"></n-input-number> | |
103 | + </setting-item> | |
104 | + <setting-item name="线路径显示方向箭头"> | |
105 | + <n-switch v-model:value="optionData.mapOptions.lineShowDir" size="small" /> | |
106 | + </setting-item> | |
107 | + <setting-item name="线透明度"> | |
108 | + <n-input-number v-model:value="optionData.mapOptions.lineOpacity" :min="0" :max="1" size="small"></n-input-number> | |
109 | + </setting-item> | |
110 | + <setting-item name="动画时效"> | |
111 | + <n-input-number v-model:value="optionData.mapOptions.lineDuration" :min="0" :max="10000" size="small"></n-input-number> | |
112 | + </setting-item> | |
113 | + </setting-item-box> | |
114 | + </collapse-item> | |
115 | +</template> | |
116 | + | |
117 | +<script setup lang="ts"> | |
118 | +import { PropType, ref, computed, h, onMounted } from 'vue' | |
119 | +import { option, MarkerEnum, ThemeEnum, LangEnum, ViewModeEnum, FeaturesEnum } from './config' | |
120 | +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' | |
121 | +import { NEllipsis, NImage,NSelect, NSpace, SelectOption } from 'naive-ui' | |
122 | +import { getUrlBase64 } from '@/utils/external/imageUrlToBase64' | |
123 | + | |
124 | +const props = defineProps({ | |
125 | + optionData: { | |
126 | + type: Object as PropType<typeof option>, | |
127 | + required: true | |
128 | + } | |
129 | +}) | |
130 | + | |
131 | +const themeOptions = [ | |
132 | + { | |
133 | + value: ThemeEnum.NORMAL, | |
134 | + label: '标准' | |
135 | + }, | |
136 | + { | |
137 | + value: ThemeEnum.DARK, | |
138 | + label: '幻影黑' | |
139 | + }, | |
140 | + { | |
141 | + value: ThemeEnum.LIGHT, | |
142 | + label: '月光银' | |
143 | + }, | |
144 | + { | |
145 | + value: ThemeEnum.WHITES_MOKE, | |
146 | + label: '远山黛' | |
147 | + }, | |
148 | + { | |
149 | + value: ThemeEnum.FRESH, | |
150 | + label: '草色青' | |
151 | + }, | |
152 | + { | |
153 | + value: ThemeEnum.GREY, | |
154 | + label: '雅士灰' | |
155 | + }, | |
156 | + { | |
157 | + value: ThemeEnum.GRAFFITI, | |
158 | + label: '涂鸦' | |
159 | + }, | |
160 | + { | |
161 | + value: ThemeEnum.MACARON, | |
162 | + label: '马卡龙' | |
163 | + }, | |
164 | + { | |
165 | + value: ThemeEnum.BLUE, | |
166 | + label: '靛青蓝' | |
167 | + }, | |
168 | + { | |
169 | + value: ThemeEnum.DARKBLUE, | |
170 | + label: '极夜蓝' | |
171 | + }, | |
172 | + { | |
173 | + value: ThemeEnum.WINE, | |
174 | + label: '酱籽' | |
175 | + }, | |
176 | + { | |
177 | + value: ThemeEnum.WEIXIN, | |
178 | + label: '卫星' | |
179 | + } | |
180 | +] | |
181 | + | |
182 | +const langOptions = [ | |
183 | + { | |
184 | + value: LangEnum.ZH_CN, | |
185 | + label: '中文简体' | |
186 | + }, | |
187 | + { | |
188 | + value: LangEnum.EN, | |
189 | + label: '英文' | |
190 | + }, | |
191 | + { | |
192 | + value: LangEnum.ZH_EN, | |
193 | + label: '中英文对照' | |
194 | + } | |
195 | +] | |
196 | + | |
197 | +const viewModeOptions = [ | |
198 | + { | |
199 | + value: ViewModeEnum.PLANE, | |
200 | + label: '2D' | |
201 | + }, | |
202 | + { | |
203 | + value: ViewModeEnum.STEREOSCOPIC, | |
204 | + label: '3D' | |
205 | + } | |
206 | +] | |
207 | + | |
208 | +const featuresOptions = [ | |
209 | + { | |
210 | + value: FeaturesEnum.BG, | |
211 | + label: '显示地图背景' | |
212 | + }, | |
213 | + { | |
214 | + value: FeaturesEnum.POINT, | |
215 | + label: '显示标识' | |
216 | + }, | |
217 | + { | |
218 | + value: FeaturesEnum.ROAD, | |
219 | + label: '显示道路' | |
220 | + }, | |
221 | + { | |
222 | + value: FeaturesEnum.BUILDING, | |
223 | + label: '显示建筑' | |
224 | + } | |
225 | +] | |
226 | + | |
227 | +const MarkerOptions = [ | |
228 | + // { | |
229 | + // value: MarkerEnum.CIRCLE_MARKER, | |
230 | + // label: '圆形标点' //在地图里点击无效,所以注释,有需要自行打开即可 | |
231 | + // }, | |
232 | + { | |
233 | + value: MarkerEnum.MARKER, | |
234 | + label: '定位标点' | |
235 | + }, | |
236 | + { | |
237 | + value: MarkerEnum.NONE, | |
238 | + label: '隐藏标点' | |
239 | + } | |
240 | +] | |
241 | + | |
242 | +const iconMarkerValue = ref<string | null>('1.png') | |
243 | + | |
244 | +const isHref = (url: string) => { | |
245 | + try { | |
246 | + new URL(url) | |
247 | + return true | |
248 | + } catch (error) { | |
249 | + return false | |
250 | + } | |
251 | +} | |
252 | + | |
253 | +// import.meta.glob 这个不能封装,必须是字符串,不能通过传值传进去 | |
254 | +const iconMarkerOptions = computed(() => { | |
255 | + const pathList = import.meta.glob('../../../../../../assets/external/marker/*') | |
256 | + return Object.keys(pathList).map(item => { | |
257 | + const imgName = item.split('/').at(-1) | |
258 | + return { | |
259 | + label: imgName, | |
260 | + value: imgName | |
261 | + } as SelectOption | |
262 | + }) | |
263 | +}) | |
264 | + | |
265 | +const getMarkerImagePath = (name: string) => { | |
266 | + return isHref(name) ? name : new URL(`../../../../../../assets/external/marker/${name}`, import.meta.url).href | |
267 | +} | |
268 | + | |
269 | +const renderOption = (option: SelectOption) => { | |
270 | + return h(NSpace, { justify: 'space-between', style: 'padding: 0 15px; height: 28px; line-height: 28px;' }, () => [ | |
271 | + h(NImage, { | |
272 | + width: 25, | |
273 | + src: getMarkerImagePath(option.value as string), | |
274 | + previewDisabled: true, | |
275 | + style: { height: '25px' } | |
276 | + } as Recordable), | |
277 | + h(NEllipsis, null, () => option.label) | |
278 | + ]) | |
279 | +} | |
280 | + | |
281 | +const selectHandle = (value: string) => { | |
282 | + iconMarkerValue.value = value | |
283 | + getUrlBase64(getMarkerImagePath(value),'png',(baseEncodeText: string)=>{ | |
284 | + props.optionData.mapOptions.iconMarker =baseEncodeText | |
285 | + }) | |
286 | +} | |
287 | +onMounted(() => { | |
288 | + getUrlBase64(getMarkerImagePath(props.optionData.mapOptions.iconMarker),'png',(baseEncodeText: string)=>{ | |
289 | + props.optionData.mapOptions.iconMarker = baseEncodeText | |
290 | + }) | |
291 | +}) | |
292 | +</script> | ... | ... |
1 | +[ | |
2 | + [ | |
3 | + 104.078262, | |
4 | + 30.652383 | |
5 | + ], | |
6 | + [ | |
7 | + 104.078099, | |
8 | + 30.652181 | |
9 | + ], | |
10 | + [ | |
11 | + 104.07795, | |
12 | + 30.651984 | |
13 | + ], | |
14 | + [ | |
15 | + 104.077611, | |
16 | + 30.651518 | |
17 | + ], | |
18 | + [ | |
19 | + 104.077257, | |
20 | + 30.651088 | |
21 | + ], | |
22 | + [ | |
23 | + 104.076913, | |
24 | + 30.65067 | |
25 | + ], | |
26 | + [ | |
27 | + 104.07667, | |
28 | + 30.650346 | |
29 | + ] | |
30 | +] | |
\ No newline at end of file | ... | ... |
1 | +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d' | |
2 | +import { ChatCategoryEnum, ChatCategoryEnumName } from '@/packages/components/Charts/index.d' | |
3 | +import { useWidgetKey } from '@/packages/external/useWidgetKey' | |
4 | + | |
5 | +const { key, conKey, chartKey } = useWidgetKey('AddHistoryTimeTrajectoryAmap', true) | |
6 | + | |
7 | +export const AddHistoryTimeTrajectoryAmapConfig: ConfigType = { | |
8 | + key, | |
9 | + chartKey, | |
10 | + conKey, | |
11 | + title: '设备历史轨迹', | |
12 | + category: ChatCategoryEnum.MAP, | |
13 | + categoryName: ChatCategoryEnumName.MAP, | |
14 | + package: PackagesCategoryEnum.CHARTS, | |
15 | + chartFrame: ChartFrameEnum.COMMON, | |
16 | + image: 'history_tim_trajectory.png' | |
17 | +} | ... | ... |
1 | +<template> | |
2 | + <div> | |
3 | + <div class="chart-amap" ref="vChartRef"> | |
4 | + </div> | |
5 | + <div class="map-action-card"> | |
6 | + <n-card title="历史轨迹操作面板"> | |
7 | + <n-space> | |
8 | + <n-button @click="handleStartAnimation">开始动画</n-button> | |
9 | + <n-button @click="handlePauseAnimation">暂停动画</n-button> | |
10 | + </n-space> | |
11 | + <div style="height: 1rem"></div> | |
12 | + <n-space> | |
13 | + <n-button @click="handleResumeAnimation">继续动画</n-button> | |
14 | + <n-button @click="handleStopAnimation">结束动画</n-button> | |
15 | + </n-space> | |
16 | + </n-card> | |
17 | + </div> | |
18 | + </div> | |
19 | +</template> | |
20 | + | |
21 | +<script setup lang="ts"> | |
22 | +import { ref, PropType, toRefs, watch } from 'vue' | |
23 | +import AMapLoader from '@amap/amap-jsapi-loader' | |
24 | +import { CreateComponentType } from '@/packages/index.d' | |
25 | +import { useChartDataFetch } from '@/hooks' | |
26 | +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' | |
27 | +import { ThemeEnum, mapOption } from './config' | |
28 | + | |
29 | +const props = defineProps({ | |
30 | + chartConfig: { | |
31 | + type: Object as PropType<CreateComponentType>, | |
32 | + required: true | |
33 | + } | |
34 | +}) | |
35 | + | |
36 | +const { | |
37 | + amapKey, | |
38 | + amapStyleKey, | |
39 | + amapLon, | |
40 | + amapLat, | |
41 | + amapZindex, | |
42 | + lang, | |
43 | + amapStyleKeyCustom, | |
44 | + features, | |
45 | + viewMode, | |
46 | + pitch, | |
47 | + skyColor, | |
48 | + lineColor, | |
49 | + lineWidth, | |
50 | + iconMarker, | |
51 | + lineDuration, | |
52 | + lineOpacity, | |
53 | + lineShowDir | |
54 | +} = toRefs(props.chartConfig.option.mapOptions) | |
55 | + | |
56 | +//官方没有高德地图api的ts,所以关于官方api,类型全用的any | |
57 | +let mapIns: any = null | |
58 | + | |
59 | +let AMapIns: any = null | |
60 | + | |
61 | +let marker: any = null | |
62 | + | |
63 | +let polyLine: any = null | |
64 | + | |
65 | +let passedPolyline: any = null | |
66 | +// | |
67 | + | |
68 | +const vChartRef = ref<HTMLElement>() | |
69 | + | |
70 | +const linePoints = ref<number[][]>([]) //保存一份坐标集数据 | |
71 | + | |
72 | +const initAMap = (newData: mapOption) => { | |
73 | + // 地图初始化 | |
74 | + AMapLoader.load({ | |
75 | + key: amapKey.value, //api服务key--另外需要在public中使用安全密钥!!! | |
76 | + version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15 | |
77 | + plugins: ['AMap.ToolBar', 'AMap.Scale', 'AMap.PlaceSearch', 'AMap.AutoComplete', 'AMap.MoveAnimation'] // 需要使用的的插件列表 | |
78 | + }) | |
79 | + .then(AMap => { | |
80 | + AMapIns = AMap | |
81 | + mapIns = new AMap.Map(vChartRef.value, { | |
82 | + resizeEnable: true, | |
83 | + zoom: amapZindex.value, // 地图显示的缩放级别 | |
84 | + center: [amapLon.value, amapLat.value], | |
85 | + lang: lang.value, | |
86 | + features: features.value, | |
87 | + pitch: pitch.value, // 地图俯仰角度,有效范围 0 度- 83 度 | |
88 | + skyColor: skyColor.value, | |
89 | + viewMode: viewMode.value, // 地图模式 | |
90 | + willReadFrequently: true, | |
91 | + buildingAnimation: true, //楼块出现是否带动画 | |
92 | + WebGLParams: { | |
93 | + preserveDrawingBuffer: true | |
94 | + } | |
95 | + }) | |
96 | + let satellite = new AMap.TileLayer.Satellite() | |
97 | + let roadNet = new AMap.TileLayer.RoadNet() | |
98 | + if (newData.amapStyleKey === ThemeEnum.WEIXIN) { | |
99 | + mapIns.add([satellite, roadNet]) | |
100 | + } else { | |
101 | + mapIns.remove([satellite, roadNet]) | |
102 | + mapIns.setMapStyle( | |
103 | + `amap://styles/${amapStyleKeyCustom.value !== '' ? amapStyleKeyCustom.value : amapStyleKey.value}` | |
104 | + ) | |
105 | + } | |
106 | + //实例化地图marker | |
107 | + marker = new AMap.Marker({ | |
108 | + map: mapIns, | |
109 | + // position: [104.03, 30.39], | |
110 | + icon: new AMap.Icon({ | |
111 | + image: iconMarker.value, | |
112 | + size: new AMap.Size(32, 32), //图标大小 | |
113 | + imageSize: new AMap.Size(26, 26) | |
114 | + }), | |
115 | + offset: new AMap.Pixel(-13, -26) | |
116 | + }) | |
117 | + //实例化地图线段 | |
118 | + polyLine = new AMapIns.Polyline({ | |
119 | + map: mapIns, | |
120 | + strokeColor: lineColor.value, //线颜色 | |
121 | + strokeWeight: lineWidth.value //线宽 | |
122 | + }) | |
123 | + //实例化地图线段 | |
124 | + passedPolyline = new AMapIns.Polyline({ | |
125 | + map: mapIns, | |
126 | + strokeColor: lineColor.value, //线颜色 | |
127 | + strokeWeight: lineWidth.value, //线宽 | |
128 | + strokeOpacity: lineOpacity.value, //线透明度 | |
129 | + showDir: lineShowDir.value, //是否延路径显示白色方向箭头 | |
130 | + lineCap: 'square' //是否延路径显示白色方向箭头 | |
131 | + }) | |
132 | + }) | |
133 | + .catch(e => { | |
134 | + console.error(e) | |
135 | + }) | |
136 | +} | |
137 | + | |
138 | +// 绘制轨迹 | |
139 | +const renderLocaLayerLine = () => { | |
140 | + polyLine?.setPath(linePoints.value) | |
141 | +} | |
142 | + | |
143 | +//marker移动 | |
144 | +const handleMarkerMove = () => { | |
145 | + marker.on('moving', function (e: Recordable) { | |
146 | + passedPolyline?.setPath(e.passedPath) | |
147 | + // 设置地图中心点 | |
148 | + // mapIns.setCenter(e.target.getPosition()) | |
149 | + // 设置旋转角 | |
150 | + // mapIns.setRotation(-e.target.getOrientation()) | |
151 | + }) | |
152 | + mapIns.setFitView() | |
153 | +} | |
154 | + | |
155 | +//开启动画 | |
156 | +const handleStartAnimation = () => { | |
157 | + renderLocaLayerLine() | |
158 | + marker.moveAlong(linePoints.value, { | |
159 | + // 每一段的时长 | |
160 | + duration: lineDuration.value, | |
161 | + // JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置 | |
162 | + autoRotation: true | |
163 | + }) | |
164 | + handleMarkerMove() | |
165 | +} | |
166 | + | |
167 | +//暂停动画 | |
168 | +const handlePauseAnimation = () => marker.pauseMove() | |
169 | + | |
170 | +//继续动画 | |
171 | +const handleResumeAnimation = () => marker.resumeMove() | |
172 | + | |
173 | +//停止动画 | |
174 | +const handleStopAnimation = () => marker.stopMove() | |
175 | + | |
176 | +const stopWatch = watch( | |
177 | + () => props.chartConfig.option.mapOptions, | |
178 | + (option: mapOption) => { | |
179 | + initAMap(option) | |
180 | + }, | |
181 | + { | |
182 | + immediate: true, | |
183 | + deep: true | |
184 | + } | |
185 | +) | |
186 | + | |
187 | +// 预览 | |
188 | +useChartDataFetch(props.chartConfig, useChartEditStore, newData => { | |
189 | + //这里是坐标集数据 | |
190 | + linePoints.value = newData | |
191 | + stopWatch() | |
192 | +}) | |
193 | +</script> | |
194 | + | |
195 | +<style lang="scss" scoped> | |
196 | +.chart-amap { | |
197 | + position: relative; | |
198 | + height: 100%; | |
199 | + width: 100%; | |
200 | +} | |
201 | + .map-action-card { | |
202 | + z-index: 1; | |
203 | + position: absolute; | |
204 | + bottom: 1.5rem; | |
205 | + right: 1.5rem; | |
206 | + } | |
207 | +</style> | ... | ... |
1 | +import { PublicConfigClass } from '@/packages/public' | |
2 | +import { CreateComponentType } from '@/packages/index.d' | |
3 | +import { AddRealTimeTrajectoryAmapConfig } from './index' | |
4 | +import { chartInitConfig } from '@/settings/designSetting' | |
5 | +import cloneDeep from 'lodash/cloneDeep' | |
6 | +import dataJson from './data.json' | |
7 | + | |
8 | +export type dataJsonType = typeof dataJson //data.json类型 | |
9 | + | |
10 | +export enum ThemeEnum { | |
11 | + NORMAL = 'normal', | |
12 | + DARK = 'dark', | |
13 | + LIGHT = 'light', | |
14 | + WHITES_MOKE = 'whitesmoke', | |
15 | + FRESH = 'fresh', | |
16 | + GREY = 'grey', | |
17 | + GRAFFITI = 'graffiti', | |
18 | + MACARON = 'macaron', | |
19 | + BLUE = 'blue', | |
20 | + DARKBLUE = 'darkblue', | |
21 | + WINE = 'wine', | |
22 | + WEIXIN = 'tileLayer' | |
23 | +} | |
24 | + | |
25 | +export enum LangEnum { | |
26 | + ZH_CN = 'zh_cn', | |
27 | + EN = 'en', | |
28 | + ZH_EN = 'zh_en' | |
29 | +} | |
30 | + | |
31 | +export enum ViewModeEnum { | |
32 | + PLANE = '2D', | |
33 | + STEREOSCOPIC = '3D' | |
34 | +} | |
35 | + | |
36 | +export enum FeaturesEnum { | |
37 | + BG = 'bg', | |
38 | + POINT = 'point', | |
39 | + ROAD = 'road', | |
40 | + BUILDING = 'building' | |
41 | +} | |
42 | + | |
43 | +export enum MarkerEnum { | |
44 | + // 圆圈 | |
45 | + CIRCLE_MARKER = 'CircleMarker', | |
46 | + // 定位标点 | |
47 | + MARKER = 'Marker', | |
48 | + // 暂无 | |
49 | + NONE = 'none' | |
50 | +} | |
51 | + | |
52 | +export const option = { | |
53 | + dataset: dataJson, | |
54 | + mapOptions: { | |
55 | + lineColor:'#53A9DE', | |
56 | + lineWidth:2, | |
57 | + pitch: 60, | |
58 | + skyColor: '#53A9DE', | |
59 | + amapKey: 'd5f3e16589dbecae64d05fe90e2ba4f2', | |
60 | + amapStyleKey: ThemeEnum.DARK, | |
61 | + amapStyleKeyCustom: '', | |
62 | + amapLon: 104.108689, | |
63 | + amapLat: 30.66176, | |
64 | + amapZindex: 11, | |
65 | + iconMarker: '1.png', | |
66 | + lineOpacity: 0.5, | |
67 | + lineShowDir: false, | |
68 | + mpBorderConfig: { | |
69 | + value: 'Border01' | |
70 | + }, | |
71 | + bgColor: 'rgba(255, 255, 255, 0.05)', | |
72 | + marker: { | |
73 | + fillColor: '#E98984FF', | |
74 | + fillOpacity: 0.5, | |
75 | + strokeColor: 'white', | |
76 | + strokeWeight: 2, | |
77 | + strokeOpacity: 0.5, | |
78 | + zIndex: 10, | |
79 | + bubble: true, | |
80 | + cursor: 'pointer', | |
81 | + clickable: true | |
82 | + }, | |
83 | + mapMarkerType: MarkerEnum.MARKER, | |
84 | + viewMode: ViewModeEnum.STEREOSCOPIC, | |
85 | + lang: LangEnum.ZH_CN, | |
86 | + features: [FeaturesEnum.BG, FeaturesEnum.POINT, FeaturesEnum.ROAD, FeaturesEnum.BUILDING] | |
87 | + } | |
88 | +} | |
89 | + | |
90 | +export type mapOption =typeof option['mapOptions'] | |
91 | + | |
92 | +export default class Config extends PublicConfigClass implements CreateComponentType { | |
93 | + public key = AddRealTimeTrajectoryAmapConfig.key | |
94 | + public attr = { ...chartInitConfig, w: 1000, h: 800, zIndex: -1 } | |
95 | + public chartConfig = cloneDeep(AddRealTimeTrajectoryAmapConfig) | |
96 | + public option = cloneDeep(option) | |
97 | +} | ... | ... |
1 | +<template> | |
2 | + <collapse-item name="基础" :expanded="true"> | |
3 | + <setting-item-box name="语言类型" :alone="true"> | |
4 | + <setting-item> | |
5 | + <n-select size="small" v-model:value="optionData.mapOptions.lang" :options="langOptions" /> | |
6 | + </setting-item> | |
7 | + </setting-item-box> | |
8 | + <setting-item-box name="Key" :alone="true"> | |
9 | + <setting-item name="请务必使用自己的高德应用 key"> | |
10 | + <n-input v-model:value="optionData.mapOptions.amapKey" size="small"></n-input> | |
11 | + </setting-item> | |
12 | + </setting-item-box> | |
13 | + <setting-item-box name="自定义地图样式ID" :alone="true"> | |
14 | + <setting-item> | |
15 | + <n-input size="small" v-model:value="optionData.mapOptions.amapStyleKeyCustom" /> | |
16 | + </setting-item> | |
17 | + </setting-item-box> | |
18 | + </collapse-item> | |
19 | + <collapse-item name="地图" :expanded="true"> | |
20 | + <setting-item-box name="主题"> | |
21 | + <setting-item> | |
22 | + <n-select size="small" v-model:value="optionData.mapOptions.amapStyleKey" :options="themeOptions" /> | |
23 | + </setting-item> | |
24 | + </setting-item-box> | |
25 | + <setting-item-box name="内容" :alone="true"> | |
26 | + <n-checkbox-group v-model:value="optionData.mapOptions.features"> | |
27 | + <n-space item-style="display: flex;"> | |
28 | + <n-checkbox :value="item.value" :label="item.label" v-for="(item, index) in featuresOptions" :key="index" /> | |
29 | + </n-space> | |
30 | + </n-checkbox-group> | |
31 | + </setting-item-box> | |
32 | + <setting-item-box name="位置"> | |
33 | + <setting-item name="经度"> | |
34 | + <n-input-number v-model:value="optionData.mapOptions.amapLon" :show-button="false" size="small"> | |
35 | + <template #suffix>°</template> | |
36 | + </n-input-number> | |
37 | + </setting-item> | |
38 | + <setting-item name="纬度"> | |
39 | + <n-input-number v-model:value="optionData.mapOptions.amapLat" :show-button="false" size="small"> | |
40 | + <template #suffix>°</template> | |
41 | + </n-input-number> | |
42 | + </setting-item> | |
43 | + <setting-item name="初始缩放"> | |
44 | + <n-input-number v-model:value="optionData.mapOptions.amapZindex" :min="0" size="small"></n-input-number> | |
45 | + </setting-item> | |
46 | + </setting-item-box> | |
47 | + <setting-item-box name="模式" :alone="true"> | |
48 | + <setting-item> | |
49 | + <n-radio-group v-model:value="optionData.mapOptions.viewMode" name="radiogroup"> | |
50 | + <n-space> | |
51 | + <n-radio v-for="song in viewModeOptions" :key="song.value" :value="song.value"> | |
52 | + {{ song.label }} | |
53 | + </n-radio> | |
54 | + </n-space> | |
55 | + </n-radio-group> | |
56 | + </setting-item> | |
57 | + </setting-item-box> | |
58 | + <template v-if="optionData.mapOptions.viewMode === '3D'"> | |
59 | + <setting-item-box> | |
60 | + <setting-item name="天空色"> | |
61 | + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.mapOptions.skyColor"></n-color-picker> | |
62 | + </setting-item> | |
63 | + <setting-item name="俯仰角"> | |
64 | + <n-input-number v-model:value="optionData.mapOptions.pitch" :min="0" :max="83" size="small"></n-input-number> | |
65 | + </setting-item> | |
66 | + </setting-item-box> | |
67 | + </template> | |
68 | + </collapse-item> | |
69 | + <collapse-item name="标记图标" :expanded="true"> | |
70 | + <setting-item-box name="样式"> | |
71 | + <setting-item> | |
72 | + <NSelect | |
73 | + size="small" | |
74 | + placeholder="请选择您要使用的图标" | |
75 | + style="width: 250px" | |
76 | + :value="iconMarkerValue" | |
77 | + :options="iconMarkerOptions" | |
78 | + :render-label="renderOption" | |
79 | + clearable | |
80 | + filterable | |
81 | + @update:value="selectHandle" | |
82 | + /> | |
83 | + </setting-item> | |
84 | + </setting-item-box> | |
85 | + </collapse-item> | |
86 | + <collapse-item name="标记" :expanded="true"> | |
87 | + <setting-item-box name="样式"> | |
88 | + <setting-item name="类型"> | |
89 | + <n-select size="small" v-model:value="optionData.mapOptions.mapMarkerType" :options="MarkerOptions" /> | |
90 | + </setting-item> | |
91 | + <setting-item v-if="optionData.mapOptions.mapMarkerType === MarkerEnum.CIRCLE_MARKER" name="颜色"> | |
92 | + <n-color-picker v-model:value="optionData.mapOptions.marker.fillColor" size="small"></n-color-picker> | |
93 | + </setting-item> | |
94 | + </setting-item-box> | |
95 | + </collapse-item> | |
96 | + <collapse-item name="线条" :expanded="true"> | |
97 | + <setting-item-box name="样式"> | |
98 | + <setting-item name="颜色"> | |
99 | + <n-color-picker size="small" :show-alpha="false" v-model:value="optionData.mapOptions.lineColor"></n-color-picker> | |
100 | + </setting-item> | |
101 | + <setting-item name="线宽"> | |
102 | + <n-input-number v-model:value="optionData.mapOptions.lineWidth" :min="0" :max="100" size="small"></n-input-number> | |
103 | + </setting-item> | |
104 | + <setting-item name="线路径显示方向箭头"> | |
105 | + <n-switch v-model:value="optionData.mapOptions.lineShowDir" size="small" /> | |
106 | + </setting-item> | |
107 | + <setting-item name="线透明度"> | |
108 | + <n-input-number v-model:value="optionData.mapOptions.lineOpacity" :min="0" :max="1" size="small"></n-input-number> | |
109 | + </setting-item> | |
110 | + </setting-item-box> | |
111 | + </collapse-item> | |
112 | +</template> | |
113 | + | |
114 | +<script setup lang="ts"> | |
115 | +import { PropType, ref, computed, h, onMounted } from 'vue' | |
116 | +import { option, MarkerEnum, ThemeEnum, LangEnum, ViewModeEnum, FeaturesEnum } from './config' | |
117 | +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' | |
118 | +import { NEllipsis, NImage,NSelect, NSpace, SelectOption } from 'naive-ui' | |
119 | +import { getUrlBase64 } from '@/utils/external/imageUrlToBase64' | |
120 | + | |
121 | +const props = defineProps({ | |
122 | + optionData: { | |
123 | + type: Object as PropType<typeof option>, | |
124 | + required: true | |
125 | + } | |
126 | +}) | |
127 | + | |
128 | +const themeOptions = [ | |
129 | + { | |
130 | + value: ThemeEnum.NORMAL, | |
131 | + label: '标准' | |
132 | + }, | |
133 | + { | |
134 | + value: ThemeEnum.DARK, | |
135 | + label: '幻影黑' | |
136 | + }, | |
137 | + { | |
138 | + value: ThemeEnum.LIGHT, | |
139 | + label: '月光银' | |
140 | + }, | |
141 | + { | |
142 | + value: ThemeEnum.WHITES_MOKE, | |
143 | + label: '远山黛' | |
144 | + }, | |
145 | + { | |
146 | + value: ThemeEnum.FRESH, | |
147 | + label: '草色青' | |
148 | + }, | |
149 | + { | |
150 | + value: ThemeEnum.GREY, | |
151 | + label: '雅士灰' | |
152 | + }, | |
153 | + { | |
154 | + value: ThemeEnum.GRAFFITI, | |
155 | + label: '涂鸦' | |
156 | + }, | |
157 | + { | |
158 | + value: ThemeEnum.MACARON, | |
159 | + label: '马卡龙' | |
160 | + }, | |
161 | + { | |
162 | + value: ThemeEnum.BLUE, | |
163 | + label: '靛青蓝' | |
164 | + }, | |
165 | + { | |
166 | + value: ThemeEnum.DARKBLUE, | |
167 | + label: '极夜蓝' | |
168 | + }, | |
169 | + { | |
170 | + value: ThemeEnum.WINE, | |
171 | + label: '酱籽' | |
172 | + }, | |
173 | + { | |
174 | + value: ThemeEnum.WEIXIN, | |
175 | + label: '卫星' | |
176 | + } | |
177 | +] | |
178 | + | |
179 | +const langOptions = [ | |
180 | + { | |
181 | + value: LangEnum.ZH_CN, | |
182 | + label: '中文简体' | |
183 | + }, | |
184 | + { | |
185 | + value: LangEnum.EN, | |
186 | + label: '英文' | |
187 | + }, | |
188 | + { | |
189 | + value: LangEnum.ZH_EN, | |
190 | + label: '中英文对照' | |
191 | + } | |
192 | +] | |
193 | + | |
194 | +const viewModeOptions = [ | |
195 | + { | |
196 | + value: ViewModeEnum.PLANE, | |
197 | + label: '2D' | |
198 | + }, | |
199 | + { | |
200 | + value: ViewModeEnum.STEREOSCOPIC, | |
201 | + label: '3D' | |
202 | + } | |
203 | +] | |
204 | + | |
205 | +const featuresOptions = [ | |
206 | + { | |
207 | + value: FeaturesEnum.BG, | |
208 | + label: '显示地图背景' | |
209 | + }, | |
210 | + { | |
211 | + value: FeaturesEnum.POINT, | |
212 | + label: '显示标识' | |
213 | + }, | |
214 | + { | |
215 | + value: FeaturesEnum.ROAD, | |
216 | + label: '显示道路' | |
217 | + }, | |
218 | + { | |
219 | + value: FeaturesEnum.BUILDING, | |
220 | + label: '显示建筑' | |
221 | + } | |
222 | +] | |
223 | + | |
224 | +const MarkerOptions = [ | |
225 | + // { | |
226 | + // value: MarkerEnum.CIRCLE_MARKER, | |
227 | + // label: '圆形标点' //在地图里点击无效,所以注释,有需要自行打开即可 | |
228 | + // }, | |
229 | + { | |
230 | + value: MarkerEnum.MARKER, | |
231 | + label: '定位标点' | |
232 | + }, | |
233 | + { | |
234 | + value: MarkerEnum.NONE, | |
235 | + label: '隐藏标点' | |
236 | + } | |
237 | +] | |
238 | + | |
239 | +const iconMarkerValue = ref<string | null>('1.png') | |
240 | + | |
241 | +const isHref = (url: string) => { | |
242 | + try { | |
243 | + new URL(url) | |
244 | + return true | |
245 | + } catch (error) { | |
246 | + return false | |
247 | + } | |
248 | +} | |
249 | + | |
250 | +// import.meta.glob 这个不能封装,必须是字符串,不能通过传值传进去 | |
251 | +const iconMarkerOptions = computed(() => { | |
252 | + const pathList = import.meta.glob('../../../../../../assets/external/marker/*') | |
253 | + return Object.keys(pathList).map(item => { | |
254 | + const imgName = item.split('/').at(-1) | |
255 | + return { | |
256 | + label: imgName, | |
257 | + value: imgName | |
258 | + } as SelectOption | |
259 | + }) | |
260 | +}) | |
261 | + | |
262 | +const getMarkerImagePath = (name: string) => { | |
263 | + return isHref(name) ? name : new URL(`../../../../../../assets/external/marker/${name}`, import.meta.url).href | |
264 | +} | |
265 | + | |
266 | +const renderOption = (option: SelectOption) => { | |
267 | + return h(NSpace, { justify: 'space-between', style: 'padding: 0 15px; height: 28px; line-height: 28px;' }, () => [ | |
268 | + h(NImage, { | |
269 | + width: 25, | |
270 | + src: getMarkerImagePath(option.value as string), | |
271 | + previewDisabled: true, | |
272 | + style: { height: '25px' } | |
273 | + } as Recordable), | |
274 | + h(NEllipsis, null, () => option.label) | |
275 | + ]) | |
276 | +} | |
277 | + | |
278 | +const selectHandle = (value: string) => { | |
279 | + iconMarkerValue.value = value | |
280 | + getUrlBase64(getMarkerImagePath(value),'png',(baseEncodeText: string)=>{ | |
281 | + props.optionData.mapOptions.iconMarker =baseEncodeText | |
282 | + }) | |
283 | +} | |
284 | +onMounted(() => { | |
285 | + getUrlBase64(getMarkerImagePath(props.optionData.mapOptions.iconMarker),'png',(baseEncodeText: string) => { | |
286 | + props.optionData.mapOptions.iconMarker = baseEncodeText | |
287 | + }) | |
288 | +}) | |
289 | +</script> | ... | ... |
1 | +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d' | |
2 | +import { ChatCategoryEnum, ChatCategoryEnumName } from '@/packages/components/Charts/index.d' | |
3 | +import { useWidgetKey } from '@/packages/external/useWidgetKey' | |
4 | + | |
5 | +const { key, conKey, chartKey } = useWidgetKey('AddRealTimeTrajectoryAmap', true) | |
6 | + | |
7 | +export const AddRealTimeTrajectoryAmapConfig: ConfigType = { | |
8 | + key, | |
9 | + chartKey, | |
10 | + conKey, | |
11 | + title: '设备实时轨迹', | |
12 | + category: ChatCategoryEnum.MAP, | |
13 | + categoryName: ChatCategoryEnumName.MAP, | |
14 | + package: PackagesCategoryEnum.CHARTS, | |
15 | + chartFrame: ChartFrameEnum.COMMON, | |
16 | + image: 'real_time_trajectory.png' | |
17 | +} | ... | ... |
1 | +<template> | |
2 | + <div class="chart-amap" ref="vChartRef"></div> | |
3 | +</template> | |
4 | + | |
5 | +<script setup lang="ts"> | |
6 | +import { ref, PropType, toRefs, watch } from 'vue' | |
7 | +import AMapLoader from '@amap/amap-jsapi-loader' | |
8 | +import { CreateComponentType } from '@/packages/index.d' | |
9 | +import { useChartDataFetch } from '@/hooks' | |
10 | +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' | |
11 | +import { ThemeEnum, mapOption } from './config' | |
12 | + | |
13 | +const props = defineProps({ | |
14 | + chartConfig: { | |
15 | + type: Object as PropType<CreateComponentType>, | |
16 | + required: true | |
17 | + } | |
18 | +}) | |
19 | + | |
20 | +const { | |
21 | + amapKey, | |
22 | + amapStyleKey, | |
23 | + amapLon, | |
24 | + amapLat, | |
25 | + amapZindex, | |
26 | + lang, | |
27 | + amapStyleKeyCustom, | |
28 | + features, | |
29 | + viewMode, | |
30 | + pitch, | |
31 | + skyColor, | |
32 | + lineColor, | |
33 | + lineWidth, | |
34 | + iconMarker, | |
35 | + lineOpacity, | |
36 | + lineShowDir | |
37 | +} = toRefs(props.chartConfig.option.mapOptions) | |
38 | + | |
39 | +//官方没有高德地图api的ts,所以关于官方api,类型全用的any | |
40 | +let mapIns: any = null | |
41 | + | |
42 | +let AMapIns: any = null | |
43 | + | |
44 | +let marker: any = null | |
45 | + | |
46 | +let polyLine: any = null | |
47 | +// | |
48 | + | |
49 | +const vChartRef = ref<HTMLElement>() | |
50 | + | |
51 | +const viewControlPaths = ref<number[][]>([]) | |
52 | + | |
53 | +const initAMap = (newData: mapOption) => { | |
54 | + // 地图初始化 | |
55 | + AMapLoader.load({ | |
56 | + key: amapKey.value, //api服务key--另外需要在public中使用安全密钥!!! | |
57 | + version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15 | |
58 | + plugins: ['AMap.ToolBar', 'AMap.Scale', 'AMap.PlaceSearch', 'AMap.AutoComplete', 'AMap.MoveAnimation'] // 需要使用的的插件列表 | |
59 | + }) | |
60 | + .then(AMap => { | |
61 | + AMapIns = AMap | |
62 | + mapIns = new AMap.Map(vChartRef.value, { | |
63 | + resizeEnable: true, | |
64 | + zoom: amapZindex.value, // 地图显示的缩放级别 | |
65 | + center: [amapLon.value, amapLat.value], | |
66 | + lang: lang.value, | |
67 | + features: features.value, | |
68 | + pitch: pitch.value, // 地图俯仰角度,有效范围 0 度- 83 度 | |
69 | + skyColor: skyColor.value, | |
70 | + viewMode: viewMode.value, // 地图模式 | |
71 | + willReadFrequently: true, | |
72 | + buildingAnimation: true, //楼块出现是否带动画 | |
73 | + WebGLParams: { | |
74 | + preserveDrawingBuffer: true | |
75 | + } | |
76 | + }) | |
77 | + let satellite = new AMap.TileLayer.Satellite() | |
78 | + let roadNet = new AMap.TileLayer.RoadNet() | |
79 | + if (newData.amapStyleKey === ThemeEnum.WEIXIN) { | |
80 | + mapIns.add([satellite, roadNet]) | |
81 | + } else { | |
82 | + mapIns.remove([satellite, roadNet]) | |
83 | + mapIns.setMapStyle( | |
84 | + `amap://styles/${amapStyleKeyCustom.value !== '' ? amapStyleKeyCustom.value : amapStyleKey.value}` | |
85 | + ) | |
86 | + } | |
87 | + //实例化地图marker | |
88 | + marker = new AMap.Marker({ | |
89 | + map: mapIns, | |
90 | + position: [104.03, 30.39], | |
91 | + icon: new AMap.Icon({ | |
92 | + image: iconMarker.value, | |
93 | + size: new AMap.Size(32, 32), //图标大小 | |
94 | + imageSize: new AMap.Size(26, 26) | |
95 | + }), | |
96 | + offset: new AMap.Pixel(-13, -26) | |
97 | + }) | |
98 | + //实例化地图线段 | |
99 | + polyLine = new AMapIns.Polyline({ | |
100 | + map: mapIns, | |
101 | + strokeColor: lineColor.value, //线颜色 | |
102 | + strokeWeight: lineWidth.value, //线宽 | |
103 | + strokeOpacity: lineOpacity.value, //线透明度 | |
104 | + showDir: lineShowDir.value, //是否延路径显示白色方向箭头 | |
105 | + }) | |
106 | + }) | |
107 | + .catch(e => { | |
108 | + console.error(e) | |
109 | + }) | |
110 | +} | |
111 | + | |
112 | +// 绘制轨迹 | |
113 | +const renderLocaLayerLine = (linePoints: number[][]) => { | |
114 | + polyLine.setPath(linePoints) | |
115 | + //marker移动直接获取最后坐标点 | |
116 | + handleMarkerMove([[linePoints.flat().at(-2), linePoints.flat().at(-1)]] as number[][]) | |
117 | +} | |
118 | + | |
119 | +//marker移动 | |
120 | +const handleMarkerMove = (linePoints: number[][]) => { | |
121 | + marker.setPosition(linePoints[0]) | |
122 | + mapIns.setFitView() | |
123 | +} | |
124 | + | |
125 | +const stopWatch = watch( | |
126 | + () => props.chartConfig.option.mapOptions, | |
127 | + (option: mapOption) => { | |
128 | + initAMap(option) | |
129 | + }, | |
130 | + { | |
131 | + immediate: true, | |
132 | + deep: true | |
133 | + } | |
134 | +) | |
135 | + | |
136 | +// 预览 | |
137 | +useChartDataFetch(props.chartConfig, useChartEditStore, newData => { | |
138 | + const { data } = newData | |
139 | + if (Reflect.get(data, 'longitude') && Reflect.get(data, 'latitude')) { | |
140 | + //必须有经纬度 | |
141 | + for (let _ in data) { | |
142 | + //由于上报的是固定格式,所有直接获取对应索引值 | |
143 | + viewControlPaths.value.push([Number(data['longitude'][0][1]), Number(data['latitude'][0][1])]) | |
144 | + } | |
145 | + renderLocaLayerLine(viewControlPaths.value) | |
146 | + } | |
147 | + stopWatch() | |
148 | +}) | |
149 | +</script> | |
150 | + | |
151 | +<style lang="scss" scoped> | |
152 | +.chart-amap { | |
153 | + position: relative; | |
154 | + height: 100%; | |
155 | + width: 100%; | |
156 | +} | |
157 | +</style> | ... | ... |
... | ... | @@ -11,15 +11,40 @@ export const enum areaEnum { |
11 | 11 | CITY = 'CITY', //城市 |
12 | 12 | COUNTY = 'COUNTY', //县 |
13 | 13 | COUNTRY = 'COUNTRY', //国家 |
14 | - TOWN = 'TOWN' //镇 | |
14 | + TOWN = 'TOWN', //镇 | |
15 | + DISTRICT = 'DISTRICT' //地区 | |
16 | +} | |
17 | + | |
18 | +export interface regionInfo { | |
19 | + provinceValue:string | |
20 | + cityValue: string|null | |
21 | + countyValue: string|null | |
22 | + levelStr:areaEnum | |
23 | +} | |
24 | + | |
25 | +type itemOption = { | |
26 | + label:string | |
27 | + value:string | |
28 | +} | |
29 | +export interface regionOption{ | |
30 | + provinceOptions: itemOption[], | |
31 | + cityOptions: itemOption[], | |
32 | + countryOptions: itemOption[] | |
15 | 33 | } |
16 | 34 | |
17 | 35 | //父级地区编码和级别接口 |
18 | -export interface HistoryParentType { | |
19 | - adcode: string | number | |
36 | +export interface historyParentType { | |
37 | + adcode: number | |
20 | 38 | level: string |
21 | 39 | } |
22 | 40 | |
41 | +export interface backMapLevel{ | |
42 | + (levelStr:string): boolean | |
43 | + (level:string): boolean | |
44 | + (): void | |
45 | + (): void | |
46 | +} | |
47 | + | |
23 | 48 | //数据源接口 |
24 | 49 | export interface dataPointI { |
25 | 50 | name: string |
... | ... | @@ -50,7 +75,10 @@ export const option = { |
50 | 75 | adcode: 'china', |
51 | 76 | showHainanIsLands: true, |
52 | 77 | saveSelect: { |
53 | - levelStr: areaEnum.COUNTRY | |
78 | + levelStr: areaEnum.COUNTRY, | |
79 | + cityValue:null, | |
80 | + countyValue:null, | |
81 | + provinceValue:'china' | |
54 | 82 | } |
55 | 83 | }, |
56 | 84 | tooltip: { |
... | ... | @@ -111,6 +139,9 @@ export const option = { |
111 | 139 | } |
112 | 140 | ] |
113 | 141 | } |
142 | + | |
143 | +export type optionType = typeof option | |
144 | + | |
114 | 145 | export const MapDefaultConfig = { ...option } |
115 | 146 | export default class Config extends PublicConfigClass implements CreateComponentType { |
116 | 147 | public key: string = AddThreeDimensionalMapConfig.key | ... | ... |
... | ... | @@ -139,6 +139,7 @@ import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/Ch |
139 | 139 | import { GlobalThemeJsonType } from '@/settings/chartThemes/index' |
140 | 140 | import { GlobalSetting } from '@/components/Pages/ChartItemSetting' |
141 | 141 | import SelectCity from './components/SelectCity.vue' |
142 | +import { regionInfo } from './config' | |
142 | 143 | |
143 | 144 | const props = defineProps({ |
144 | 145 | optionData: { |
... | ... | @@ -186,9 +187,9 @@ const symbolOption = ref([ |
186 | 187 | } |
187 | 188 | ]) |
188 | 189 | |
189 | -const SelectCityRef = ref<typeof SelectCity>() | |
190 | +const SelectCityRef = ref<InstanceType<typeof SelectCity>>() | |
190 | 191 | |
191 | -const onHandleSelectValues = (values: any) => { | |
192 | +const onHandleSelectValues = (values: regionInfo) => { | |
192 | 193 | const { cityValue, countyValue, provinceValue } = values |
193 | 194 | props.optionData.mapRegion.saveSelect = values |
194 | 195 | props.optionData.mapRegion.adcode = countyValue | ... | ... |
... | ... | @@ -8,7 +8,7 @@ export const AddThreeDimensionalMapConfig: ConfigType = { |
8 | 8 | key, |
9 | 9 | chartKey, |
10 | 10 | conKey, |
11 | - title: '3d地图', | |
11 | + title: '3D地图(支持下钻)', | |
12 | 12 | category: ChatCategoryEnum.MAP, |
13 | 13 | categoryName: ChatCategoryEnumName.MAP, |
14 | 14 | package: PackagesCategoryEnum.CHARTS, | ... | ... |
... | ... | @@ -12,7 +12,7 @@ import { onMounted, ref, nextTick, PropType, toRefs, watch, reactive } from 'vue |
12 | 12 | import * as echarts from 'echarts' |
13 | 13 | import { registerMap } from 'echarts/core' |
14 | 14 | import 'echarts-gl' |
15 | -import config, { areaEnum, dataPointI, HistoryParentType } from './config' | |
15 | +import config, { areaEnum, dataPointI, optionType, historyParentType, backMapLevel } from './config' | |
16 | 16 | import { getGeoJsonMap } from '@/api/external/common' |
17 | 17 | import dataMaps from './data.json' |
18 | 18 | |
... | ... | @@ -23,9 +23,7 @@ const props = defineProps({ |
23 | 23 | } |
24 | 24 | }) |
25 | 25 | |
26 | -const iconStr = ref( | |
27 | - 'path://M853.333333 245.333333H245.333333l93.866667-93.866666c12.8-12.8 12.8-34.133333 0-46.933334-12.8-12.8-34.133333-12.8-46.933333 0l-145.066667 145.066667c-12.8 12.8-12.8 34.133333 0 46.933333l145.066667 145.066667c6.4 6.4 14.933333 10.666667 23.466666 10.666667s17.066667-4.266667 23.466667-10.666667c12.8-12.8 12.8-34.133333 0-46.933333L256 311.466667h597.333333c6.4 0 10.666667 4.266667 10.666667 10.666666v426.666667c0 6.4-4.266667 10.666667-10.666667 10.666667H170.666667c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h682.666666c40.533333 0 74.666667-34.133333 74.666667-74.666667V320c0-40.533333-34.133333-74.666667-74.666667-74.666667z' | |
28 | -) | |
26 | +const backIcon = 'path://M853.333333 245.333333H245.333333l93.866667-93.866666c12.8-12.8 12.8-34.133333 0-46.933334-12.8-12.8-34.133333-12.8-46.933333 0l-145.066667 145.066667c-12.8 12.8-12.8 34.133333 0 46.933333l145.066667 145.066667c6.4 6.4 14.933333 10.666667 23.466666 10.666667s17.066667-4.266667 23.466667-10.666667c12.8-12.8 12.8-34.133333 0-46.933333L256 311.466667h597.333333c6.4 0 10.666667 4.266667 10.666667 10.666666v426.666667c0 6.4-4.266667 10.666667-10.666667 10.666667H170.666667c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h682.666666c40.533333 0 74.666667-34.133333 74.666667-74.666667V320c0-40.533333-34.133333-74.666667-74.666667-74.666667z' | |
29 | 27 | |
30 | 28 | const { w, h } = toRefs(props.chartConfig.attr) |
31 | 29 | |
... | ... | @@ -43,47 +41,51 @@ const toolBoxOption = ref({ |
43 | 41 | myFullButton: { |
44 | 42 | show: false, |
45 | 43 | title: '返回', |
46 | - icon: iconStr.value, | |
44 | + icon: backIcon, | |
47 | 45 | iconStyle: { |
48 | 46 | color: '' |
49 | 47 | }, |
50 | - onclick: () => watchAdcode() | |
48 | + onclick: () => handleBack() | |
51 | 49 | } |
52 | 50 | } |
53 | 51 | }) |
54 | 52 | |
53 | +const excludeCountryLevels = ['PROVINCE','CITY'] //如果从右侧配置选择全中国 | |
54 | + | |
55 | +const includeCityLevels = ['CITY'] //如果从右侧配置选择省份 | |
56 | + | |
57 | +//元组 优化if elseif else分支 隐藏返回图标 | |
58 | +const backIconMappingLevels: any[][] =[ | |
59 | + [ | |
60 | + (levelStr:string)=>levelStr===areaEnum.COUNTRY, | |
61 | + (level:string)=>excludeCountryLevels.includes(level), | |
62 | + ()=>toolBoxOption.value.feature.myFullButton.show=true, | |
63 | + ()=>toolBoxOption.value.feature.myFullButton.show=false | |
64 | + ], | |
65 | + [ | |
66 | + (levelStr:string)=>levelStr===areaEnum.PROVINCE, | |
67 | + (level:string)=>includeCityLevels.includes(level), | |
68 | + ()=>toolBoxOption.value.feature.myFullButton.show=true, | |
69 | + ()=>toolBoxOption.value.feature.myFullButton.show=false | |
70 | + ], | |
71 | +] | |
72 | + | |
73 | + | |
55 | 74 | watch( |
56 | 75 | () => props.chartConfig.option, |
57 | - newData => { | |
58 | - const { iconColor, drillingIn, iconDistanceRight, iconDistanceTop, mapRegion } = newData | |
59 | - if (drillingIn && !newData.saveClickRegion.level) { | |
60 | - toolBoxOption.value.feature.myFullButton.show = !drillingIn | |
61 | - } else if ( | |
62 | - drillingIn && | |
63 | - newData.saveClickRegion.level === areaEnum.PROVINCE && | |
64 | - mapRegion.saveSelect.levelStr === areaEnum.COUNTRY | |
65 | - ) { | |
66 | - toolBoxOption.value.feature.myFullButton.show = drillingIn | |
67 | - } else if ( | |
68 | - drillingIn && | |
69 | - newData.saveClickRegion.level === areaEnum.COUNTRY && | |
70 | - mapRegion.saveSelect.levelStr === areaEnum.COUNTRY | |
71 | - ) { | |
72 | - toolBoxOption.value.feature.myFullButton.show = !drillingIn | |
73 | - } else if ( | |
74 | - drillingIn && | |
75 | - newData.saveClickRegion.level === areaEnum.CITY && | |
76 | - mapRegion.saveSelect.levelStr === areaEnum.PROVINCE | |
77 | - ) { | |
78 | - toolBoxOption.value.feature.myFullButton.show = drillingIn | |
79 | - } else if ( | |
80 | - drillingIn && | |
81 | - newData.saveClickRegion.level === areaEnum.PROVINCE && | |
82 | - mapRegion.saveSelect.levelStr === areaEnum.PROVINCE | |
83 | - ) { | |
84 | - toolBoxOption.value.feature.myFullButton.show = !drillingIn | |
76 | + (newData:optionType) => { | |
77 | + const { iconColor, iconDistanceRight, iconDistanceTop, mapRegion } = newData | |
78 | + const { saveSelect }=mapRegion | |
79 | + const { levelStr }=saveSelect | |
80 | + const findBackLevel = backIconMappingLevels.find((backLevelItem: backMapLevel[])=>backLevelItem[0](levelStr)) as backMapLevel[] | |
81 | + if(findBackLevel){ | |
82 | + if(findBackLevel[0]){ | |
83 | + const findLevel = findBackLevel[1](saveLevelStr.level) | |
84 | + if(findLevel)findBackLevel[2]() | |
85 | + else findBackLevel[3]() | |
86 | + } | |
85 | 87 | } |
86 | - toolBoxOption.value.feature.myFullButton.iconStyle.color = iconColor | |
88 | + toolBoxOption.value.feature.myFullButton.iconStyle.color = iconColor //返回图标颜色 | |
87 | 89 | toolBoxOption.value.right = iconDistanceRight |
88 | 90 | toolBoxOption.value.top = iconDistanceTop |
89 | 91 | }, |
... | ... | @@ -92,26 +94,27 @@ watch( |
92 | 94 | } |
93 | 95 | ) |
94 | 96 | |
97 | +//追加echarts右上角自带toolbox | |
95 | 98 | props.chartConfig.option = { |
96 | 99 | ...props.chartConfig.option, |
97 | 100 | ...{ toolbox: toolBoxOption.value } |
98 | 101 | } |
99 | 102 | |
100 | 103 | //地图点击返回 |
101 | -const watchAdcode = async () => { | |
104 | +const handleBack = async () => { | |
102 | 105 | stopWatch() |
103 | 106 | if (props.chartConfig.option.drillingIn) { |
104 | 107 | //如果是从右边配置里设置的,比如点击四川省,然后点击返回 |
105 | 108 | const savePopParent = saveHistoryParent.value.pop() |
106 | 109 | let saveAdcode = savePopParent?.adcode as string | number |
107 | - saveLevelStr.level = savePopParent?.level | |
110 | + saveLevelStr.level = savePopParent?.level as string | |
108 | 111 | if (!savePopParent) { |
109 | 112 | saveAdcode = getParentAdcode(props.chartConfig.option.mapRegion.adcode) |
110 | 113 | saveLevelStr.level = (regionMapParentArea as Recordable)[props.chartConfig.option.mapRegion.saveSelect.levelStr] |
111 | 114 | } |
112 | 115 | if (saveAdcode === 0) { |
113 | 116 | saveAdcode = 'china' |
114 | - saveLevelStr.level = 'COUNTRY' | |
117 | + saveLevelStr.level = areaEnum.COUNTRY | |
115 | 118 | } |
116 | 119 | const exist = await getGeojson(saveAdcode) |
117 | 120 | const adcode = saveAdcode === 100000 ? 'china' : saveAdcode |
... | ... | @@ -123,11 +126,12 @@ const watchAdcode = async () => { |
123 | 126 | } |
124 | 127 | } |
125 | 128 | |
129 | +//地区上级对应配置 | |
126 | 130 | const regionMapParentArea = { |
127 | - [areaEnum.PROVINCE]: areaEnum.COUNTRY, //省份的上一级 中国 | |
128 | - [areaEnum.CITY]: areaEnum.PROVINCE, //城市的上一级 省份 | |
129 | - [areaEnum.COUNTY]: areaEnum.CITY, //县或者区的上一级 城市 | |
130 | - [areaEnum.TOWN]: areaEnum.COUNTY //镇的上一级 县或者区 | |
131 | + PROVINCE: areaEnum.COUNTRY, //省份的上一级 中国 | |
132 | + CITY: areaEnum.PROVINCE, //城市的上一级 省份 | |
133 | + COUNTY: areaEnum.CITY, //县或者区的上一级 城市 | |
134 | + TOWN: areaEnum.COUNTY //镇的上一级 县或者区 | |
131 | 135 | } |
132 | 136 | |
133 | 137 | //地图点击 |
... | ... | @@ -138,8 +142,8 @@ const handleMap3DClick = async (params: Recordable) => { |
138 | 142 | if (item.properties.name === name) { |
139 | 143 | const level = item.properties.level.toUpperCase() |
140 | 144 | const adcode = item.properties.adcode |
141 | - if (level === 'DISTRICT') return | |
142 | - if (String(adcode).startsWith('15') && level === areaEnum.CITY) return | |
145 | + if (level === 'DISTRICT') return //下钻暂且不支持地区 | |
146 | + if (String(adcode).startsWith('15') && level === areaEnum.CITY) return //特殊处理地区码15开头的 | |
143 | 147 | props.chartConfig.option.mapRegion.adcode = adcode |
144 | 148 | props.chartConfig.option.saveClickRegion.level = level |
145 | 149 | saveLevelStr.level = level |
... | ... | @@ -157,12 +161,12 @@ const saveGeojson: Recordable = ref({}) // 保存geojson |
157 | 161 | |
158 | 162 | const chinaDefaultRegionId = ref(100000) //如果是china则adcode为100000 |
159 | 163 | |
160 | -// 保存地区级别 | |
161 | -const saveLevelStr = reactive<{ level: string | undefined }>({ | |
164 | +const saveLevelStr = reactive<{level:historyParentType["level"]}>({ | |
165 | + // 地区级别 | |
162 | 166 | level: '' |
163 | 167 | }) |
164 | 168 | |
165 | -const saveHistoryParent = ref<HistoryParentType[]>([]) | |
169 | +const saveHistoryParent = ref<historyParentType[]>([]) | |
166 | 170 | |
167 | 171 | //动态注册地图 |
168 | 172 | const getGeojson = (regionId: number | string) => { |
... | ... | @@ -171,14 +175,14 @@ const getGeojson = (regionId: number | string) => { |
171 | 175 | const { levelStr } = props.chartConfig.option.mapRegion.saveSelect //右侧配置项获取的行政级别 |
172 | 176 | getGeoJsonMap( |
173 | 177 | regionId === 'china' ? chinaDefaultRegionId.value : regionId, |
174 | - !saveLevelStr.level ? levelStr : saveLevelStr.level | |
178 | + !saveLevelStr.level ? levelStr : saveLevelStr.level //没有则获取右侧配置的行政级别 | |
175 | 179 | ).then(res => { |
176 | 180 | const { geoJson, name, code, level } = res.data |
177 | 181 | const geoJsonFile = JSON.parse(geoJson) |
178 | 182 | if (!geoJsonFile) return |
179 | - saveGeojson.value = geoJsonFile | |
183 | + saveGeojson.value = geoJsonFile//保存一份服务端返回的geojson | |
180 | 184 | const nameChina = name === '中国' ? 'china' : name |
181 | - registerMap(level === areaEnum.COUNTRY ? nameChina : code, { geoJSON: geoJsonFile as any, specialAreas: {} }) | |
185 | + registerMap(level === areaEnum.COUNTRY ? nameChina : code, { geoJSON: geoJsonFile, specialAreas: {} }) | |
182 | 186 | show.value = false |
183 | 187 | resolve(true) |
184 | 188 | }) |
... | ... | @@ -191,6 +195,9 @@ const getGeojson = (regionId: number | string) => { |
191 | 195 | } |
192 | 196 | } |
193 | 197 | |
198 | +//异步时先注册空的 保证初始化不报错 | |
199 | +registerMap(props.chartConfig.option.mapRegion.adcode, { geoJSON: {} as any, specialAreas: {} }) | |
200 | + | |
194 | 201 | //传adcode 获取上级 |
195 | 202 | const getParentAdcode = (adcode: number) => { |
196 | 203 | let adcodeNum = 100000 | ... | ... |
... | ... | @@ -10,7 +10,14 @@ import AMapLoader from '@amap/amap-jsapi-loader' |
10 | 10 | import { CreateComponentType } from '@/packages/index.d' |
11 | 11 | import { useChartDataFetch } from '@/hooks' |
12 | 12 | import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' |
13 | -import { MarkerEnum, ThemeEnum, dataExtraInfoType, dataJsonType, dataJsonMarkersType, devicePartInfoInterface } from './config' | |
13 | +import { | |
14 | + MarkerEnum, | |
15 | + ThemeEnum, | |
16 | + dataExtraInfoType, | |
17 | + dataJsonType, | |
18 | + dataJsonMarkersType, | |
19 | + devicePartInfoInterface | |
20 | +} from './config' | |
14 | 21 | import { isArray } from '@/utils' |
15 | 22 | import inactive from './images/inactive.png' |
16 | 23 | import online from './images/online.png' |
... | ... | @@ -29,7 +36,7 @@ const props = defineProps({ |
29 | 36 | } |
30 | 37 | }) |
31 | 38 | |
32 | -const deviceLatestTableRef = ref<Nullable<InstanceType<typeof DeviceLatestTable>>>(null); | |
39 | +const deviceLatestTableRef = ref<Nullable<InstanceType<typeof DeviceLatestTable>>>(null) | |
33 | 40 | |
34 | 41 | const { |
35 | 42 | amapKey, |
... | ... | @@ -76,7 +83,7 @@ const initMap = (newData: any) => { |
76 | 83 | viewMode: viewMode.value, // 地图模式 |
77 | 84 | willReadFrequently: true, |
78 | 85 | WebGLParams: { |
79 | - preserveDrawingBuffer: true, | |
86 | + preserveDrawingBuffer: true | |
80 | 87 | } |
81 | 88 | }) |
82 | 89 | dataHandle(props.chartConfig.option.dataset) //处理地图标点 |
... | ... | @@ -99,19 +106,19 @@ const initMap = (newData: any) => { |
99 | 106 | }) |
100 | 107 | } |
101 | 108 | |
102 | - | |
103 | 109 | //携带设备的tbDeviceId和别名或者名称 |
104 | 110 | const devicePartInfo = reactive<devicePartInfoInterface>({ |
105 | 111 | tbDeviceId: '', |
106 | 112 | name: '', |
107 | 113 | alias: '', |
108 | - deviceProfileId: '', | |
114 | + deviceProfileId: '' | |
109 | 115 | }) |
110 | 116 | |
111 | 117 | //创建信息弹窗 |
112 | 118 | const createInfoWindow = async (extraInfo: dataExtraInfoType) => { |
113 | 119 | try { |
114 | - const { name, alias, organizationDTO, deviceState, deviceProfile, deviceInfo, tbDeviceId, deviceProfileId } = extraInfo | |
120 | + const { name, alias, organizationDTO, deviceState, deviceProfile, deviceInfo, tbDeviceId, deviceProfileId } = | |
121 | + extraInfo | |
115 | 122 | devicePartInfo.tbDeviceId = tbDeviceId |
116 | 123 | devicePartInfo.alias = alias |
117 | 124 | devicePartInfo.name = name |
... | ... | @@ -140,30 +147,35 @@ const createInfoWindow = async (extraInfo: dataExtraInfoType) => { |
140 | 147 | <div style="display:flex;flex-direction:column;margin:3.5rem 5.5rem 2rem 6.5rem;position:relative;"> |
141 | 148 | <div style="display:flex;justify-content:space-between;align-items:center;color:white;"> |
142 | 149 | <span style="${textOverflowFontBold}">${alias || name}</span> |
143 | - ${deviceState === 'INACTIVE' | |
144 | - ? `<div style="${deviceStateContainer}"> | |
150 | + ${ | |
151 | + deviceState === 'INACTIVE' | |
152 | + ? `<div style="${deviceStateContainer}"> | |
145 | 153 | <img onclick="handleOpenDrawer()" title="最新数据" style="${deviceStateImg};cursor:pointer;" src="${listView}"/> |
146 | 154 | <img style="${deviceStateImg};margin-left:0.3rem" src="${inactive}"/> |
147 | 155 | <span style="${deviceStateText}">待激活</span> |
148 | 156 | </div>` |
149 | - : deviceState === 'ONLINE' | |
150 | - ? `<div style="${deviceStateContainer}"> | |
157 | + : deviceState === 'ONLINE' | |
158 | + ? `<div style="${deviceStateContainer}"> | |
151 | 159 | <img onclick="handleOpenDrawer()" title="最新数据" style="${deviceStateImg};cursor:pointer;" src="${listView}"/> |
152 | 160 | <img style="${deviceStateImg};margin-left:0.3rem" src="${online}"/> |
153 | 161 | <span style="${deviceStateText}">在线</span> |
154 | 162 | </div>` |
155 | - : `<div style="${deviceStateContainer}"> | |
163 | + : `<div style="${deviceStateContainer}"> | |
156 | 164 | <img onclick="handleOpenDrawer()" title="最新数据" style="${deviceStateImg};cursor:pointer;" src="${listView}"/> |
157 | 165 | <img style="${deviceStateImg};margin-left:0.3rem" src="${offline}"/> |
158 | 166 | <span style="${deviceStateText}">离线</span> |
159 | 167 | </div>` |
160 | - } | |
168 | + } | |
161 | 169 | </div> |
162 | 170 | <div style="display:flex;flex-direction:column;justify-content:space-between;color:white;margin-top:1rem;gap:0.95rem;"> |
163 | 171 | <div style="${textOverflow}">所属组织:${organizationDTO.name}</div> |
164 | 172 | <div style="${textOverflow}">接入协议:${deviceProfile.transportType}</div> |
165 | - <div style="${textOverflow}">设备位置:${!deviceInfo.address ? '该设备暂无地理位置' : deviceInfo.address}</div> | |
166 | - <div style="${textOverflow}">${deviceState === 'ONLINE' ? '在线' : deviceState === 'INACTIVE' ? '创建' : '离线'}时间:${lastUpdateFormatTs}</div> | |
173 | + <div style="${textOverflow}">设备位置:${ | |
174 | + !deviceInfo.address ? '该设备暂无地理位置' : deviceInfo.address | |
175 | + }</div> | |
176 | + <div style="${textOverflow}">${ | |
177 | + deviceState === 'ONLINE' ? '在线' : deviceState === 'INACTIVE' ? '创建' : '离线' | |
178 | + }时间:${lastUpdateFormatTs}</div> | |
167 | 179 | </div> |
168 | 180 | </div> |
169 | 181 | </div> |
... | ... | @@ -183,12 +195,12 @@ const handleOpenDrawer = async () => { |
183 | 195 | } |
184 | 196 | |
185 | 197 | onMounted(() => { |
186 | - (window as any).handleOpenDrawer = handleOpenDrawer; | |
187 | -}); | |
198 | + ;(window as any).handleOpenDrawer = handleOpenDrawer | |
199 | +}) | |
188 | 200 | |
189 | 201 | onUnmounted(() => { |
190 | - (window as any).handleOpenDrawer = null; | |
191 | -}); | |
202 | + ;(window as any).handleOpenDrawer = null | |
203 | +}) | |
192 | 204 | |
193 | 205 | //地图鼠标hover |
194 | 206 | const mapClick = (markerInstance: any, markerItem: dataJsonMarkersType) => { |
... | ... | @@ -225,7 +237,11 @@ const dataHandle = (newData: dataJsonType) => { |
225 | 237 | const markerInstance = new AMapIns.Marker({ |
226 | 238 | position: [markerItem.position[0], markerItem.position[1]], |
227 | 239 | offset: new AMapIns.Pixel(-13, 5), |
228 | - icon: iconMarker.value | |
240 | + icon: new AMapIns.Icon({ | |
241 | + image: iconMarker.value, | |
242 | + size: new AMapIns.Size(22, 22), //图标所处区域大小 | |
243 | + imageSize: new AMapIns.Size(22, 22) //图标大小 | |
244 | + }) | |
229 | 245 | }) |
230 | 246 | // 原作者这种方式添加,属于JS API 1.4.8版本的 |
231 | 247 | markers.push(markerInstance) | ... | ... |
1 | 1 | <script lang="ts" setup name="SelectCity"> |
2 | 2 | import { onMounted, reactive } from 'vue' |
3 | 3 | import { getAreaList } from '@/api/external/common/index' |
4 | -import { areaEnum } from '../config' | |
4 | +import { areaEnum, regionInfo, regionOption } from '../config' | |
5 | 5 | |
6 | 6 | const props = defineProps({ |
7 | 7 | drillingIn: { |
... | ... | @@ -15,35 +15,35 @@ const props = defineProps({ |
15 | 15 | |
16 | 16 | const emits = defineEmits(['submit']) |
17 | 17 | |
18 | -const selectOptions = reactive({ | |
18 | +const selectOptions = reactive<regionOption>({ | |
19 | 19 | provinceOptions: [], |
20 | 20 | cityOptions: [], |
21 | 21 | countryOptions: [] |
22 | 22 | }) |
23 | 23 | |
24 | -const selectValues = reactive({ | |
24 | +const selectValues = reactive<regionInfo>({ | |
25 | 25 | provinceValue: 'china', |
26 | 26 | cityValue: null, |
27 | 27 | countyValue: null, |
28 | 28 | levelStr: areaEnum.COUNTRY |
29 | 29 | }) |
30 | 30 | |
31 | -const getAreaLists = async (level = areaEnum.PROVINCE, parentId = 1) => { | |
31 | +const getAreaLists = async (level = areaEnum.PROVINCE, parentId:number|string = 1) => { | |
32 | 32 | const resp = await getAreaList({ |
33 | 33 | level, |
34 | 34 | parentId |
35 | 35 | }) |
36 | 36 | if (!resp) return [] |
37 | - return resp.map((item: any) => ({ label: item.name, value: item.code })) | |
37 | + return resp.map((item: Recordable) => ({ label: item.name, value: item.code })) | |
38 | 38 | } |
39 | 39 | |
40 | 40 | onMounted(async () => { |
41 | 41 | selectOptions.provinceOptions = await getAreaLists() |
42 | - ;(selectOptions.provinceOptions as never as any).unshift({ | |
42 | + ;(selectOptions.provinceOptions).unshift({ | |
43 | 43 | label: '中国', |
44 | 44 | value: 'china' |
45 | 45 | }) |
46 | - onHandleSelectProvince(props.optionData?.mapRegion.saveSelect['provinceValue']) | |
46 | + onHandleSelectProvince('china') | |
47 | 47 | for (let i in selectValues) Reflect.set(selectValues, i, props.optionData?.mapRegion.saveSelect[i]) |
48 | 48 | }) |
49 | 49 | |
... | ... | @@ -51,7 +51,7 @@ const onHandleSelectProvince = async (value: number | string) => { |
51 | 51 | selectValues.cityValue = null |
52 | 52 | selectValues.countyValue = null |
53 | 53 | if (value === 'china') return (selectValues.levelStr = areaEnum.COUNTRY) |
54 | - selectOptions.cityOptions = await getAreaLists(areaEnum.CITY, value as any) | |
54 | + selectOptions.cityOptions = await getAreaLists(areaEnum.CITY, value) | |
55 | 55 | selectValues.levelStr = areaEnum.PROVINCE |
56 | 56 | } |
57 | 57 | ... | ... |
... | ... | @@ -11,8 +11,40 @@ export const enum areaEnum { |
11 | 11 | CITY = 'CITY', //城市 |
12 | 12 | COUNTY = 'COUNTY', //县 |
13 | 13 | COUNTRY = 'COUNTRY', //国家 |
14 | - TOWN = 'TOWN' //镇 | |
14 | + TOWN = 'TOWN', //镇 | |
15 | + DISTRICT = 'DISTRICT' //地区 | |
15 | 16 | } |
17 | + | |
18 | +export interface regionInfo { | |
19 | + provinceValue:string | |
20 | + cityValue: string|null | |
21 | + countyValue: string|null | |
22 | + levelStr:areaEnum | |
23 | +} | |
24 | + | |
25 | +type itemOption = { | |
26 | + label:string | |
27 | + value:string | |
28 | +} | |
29 | +export interface regionOption{ | |
30 | + provinceOptions: itemOption[], | |
31 | + cityOptions: itemOption[], | |
32 | + countryOptions: itemOption[] | |
33 | +} | |
34 | + | |
35 | +//父级地区编码和级别接口 | |
36 | +export interface historyParentType { | |
37 | + adcode: number | |
38 | + level: string | |
39 | +} | |
40 | + | |
41 | +export interface backMapLevel{ | |
42 | + (levelStr:string): boolean | |
43 | + (level:string): boolean | |
44 | + (): void | |
45 | + (): void | |
46 | +} | |
47 | + | |
16 | 48 | export const includes = [] |
17 | 49 | |
18 | 50 | export const option = { |
... | ... | @@ -29,7 +61,10 @@ export const option = { |
29 | 61 | adcode: 'china', |
30 | 62 | showHainanIsLands: true, |
31 | 63 | saveSelect: { |
32 | - levelStr: areaEnum.COUNTRY | |
64 | + levelStr: areaEnum.COUNTRY, | |
65 | + cityValue:null, | |
66 | + countyValue:null, | |
67 | + provinceValue:'china' | |
33 | 68 | } |
34 | 69 | }, |
35 | 70 | tooltip: { |
... | ... | @@ -187,6 +222,9 @@ export const option = { |
187 | 222 | } |
188 | 223 | ] |
189 | 224 | } |
225 | + | |
226 | +export type optionType = typeof option | |
227 | + | |
190 | 228 | export const MapDefaultConfig = { ...option } |
191 | 229 | export default class Config extends PublicConfigClass implements CreateComponentType { |
192 | 230 | public key: string = OverrideMapBaseConfig.key | ... | ... |
... | ... | @@ -296,6 +296,14 @@ import { GlobalSetting } from '@/components/Pages/ChartItemSetting' |
296 | 296 | import { ref } from 'vue' |
297 | 297 | import mapChinaJson from './mapGeojson/china.json' |
298 | 298 | import SelectCity from './components/SelectCity.vue' |
299 | +import { regionInfo } from './config' | |
300 | + | |
301 | +const props = defineProps({ | |
302 | + optionData: { | |
303 | + type: Object as PropType<GlobalThemeJsonType>, | |
304 | + required: true | |
305 | + } | |
306 | +}) | |
299 | 307 | |
300 | 308 | const mapRegionOptions = ref([ |
301 | 309 | { |
... | ... | @@ -315,17 +323,10 @@ const rippleEffectOptions = ref([ |
315 | 323 | } |
316 | 324 | ]) |
317 | 325 | |
318 | -const props = defineProps({ | |
319 | - optionData: { | |
320 | - type: Object as PropType<GlobalThemeJsonType>, | |
321 | - required: true | |
322 | - } | |
323 | -}) | |
324 | - | |
325 | -const SelectCityRef = ref<typeof SelectCity>() | |
326 | +const selectCityRef = ref<InstanceType<typeof SelectCity>>() | |
326 | 327 | |
327 | 328 | const initMapRegionOptions = () => { |
328 | - mapChinaJson.features.forEach((element: any) => { | |
329 | + mapChinaJson.features.forEach((element: Recordable) => { | |
329 | 330 | if (element.properties.name) { |
330 | 331 | mapRegionOptions.value.push({ ...element.properties }) |
331 | 332 | } |
... | ... | @@ -341,7 +342,7 @@ const mapRegion = computed(() => { |
341 | 342 | return props.optionData.mapRegion |
342 | 343 | }) |
343 | 344 | |
344 | -const onHandleSelectValues = (values: any) => { | |
345 | +const onHandleSelectValues = (values: regionInfo) => { | |
345 | 346 | const { cityValue, countyValue, provinceValue } = values |
346 | 347 | props.optionData.mapRegion.saveSelect = values |
347 | 348 | props.optionData.mapRegion.adcode = countyValue |
... | ... | @@ -354,6 +355,6 @@ const onHandleSelectValues = (values: any) => { |
354 | 355 | } |
355 | 356 | |
356 | 357 | const handleChangeDrillingIn = () => { |
357 | - SelectCityRef.value?.resetValue() | |
358 | + selectCityRef.value?.resetValue() | |
358 | 359 | } |
359 | 360 | </script> | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | |
16 | 16 | <script setup lang="ts"> |
17 | 17 | import { PropType, reactive, watch, ref, nextTick } from 'vue' |
18 | -import config, { includes, areaEnum } from './config' | |
18 | +import config, { includes, areaEnum, optionType, historyParentType, backMapLevel } from './config' | |
19 | 19 | import VChart from 'vue-echarts' |
20 | 20 | import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook' |
21 | 21 | import { use, registerMap } from 'echarts/core' |
... | ... | @@ -60,9 +60,7 @@ use([ |
60 | 60 | |
61 | 61 | const loading = ref(true) |
62 | 62 | |
63 | -const iconStr = ref( | |
64 | - 'path://M853.333333 245.333333H245.333333l93.866667-93.866666c12.8-12.8 12.8-34.133333 0-46.933334-12.8-12.8-34.133333-12.8-46.933333 0l-145.066667 145.066667c-12.8 12.8-12.8 34.133333 0 46.933333l145.066667 145.066667c6.4 6.4 14.933333 10.666667 23.466666 10.666667s17.066667-4.266667 23.466667-10.666667c12.8-12.8 12.8-34.133333 0-46.933333L256 311.466667h597.333333c6.4 0 10.666667 4.266667 10.666667 10.666666v426.666667c0 6.4-4.266667 10.666667-10.666667 10.666667H170.666667c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h682.666666c40.533333 0 74.666667-34.133333 74.666667-74.666667V320c0-40.533333-34.133333-74.666667-74.666667-74.666667z' | |
65 | -) | |
63 | +const backIcon = 'path://M853.333333 245.333333H245.333333l93.866667-93.866666c12.8-12.8 12.8-34.133333 0-46.933334-12.8-12.8-34.133333-12.8-46.933333 0l-145.066667 145.066667c-12.8 12.8-12.8 34.133333 0 46.933333l145.066667 145.066667c6.4 6.4 14.933333 10.666667 23.466666 10.666667s17.066667-4.266667 23.466667-10.666667c12.8-12.8 12.8-34.133333 0-46.933333L256 311.466667h597.333333c6.4 0 10.666667 4.266667 10.666667 10.666666v426.666667c0 6.4-4.266667 10.666667-10.666667 10.666667H170.666667c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h682.666666c40.533333 0 74.666667-34.133333 74.666667-74.666667V320c0-40.533333-34.133333-74.666667-74.666667-74.666667z' | |
66 | 64 | |
67 | 65 | const option = reactive({ |
68 | 66 | value: mergeTheme(props.chartConfig.option, props.themeSetting, includes) |
... | ... | @@ -76,55 +74,60 @@ const toolBoxOption = ref({ |
76 | 74 | myFullButton: { |
77 | 75 | show: false, |
78 | 76 | title: '返回', |
79 | - icon: iconStr.value, | |
77 | + icon: backIcon, | |
80 | 78 | iconStyle: { |
81 | 79 | color: '' |
82 | 80 | }, |
83 | - onclick: () => watchAdcode() | |
81 | + onclick: () => handleBack() | |
84 | 82 | } |
85 | 83 | } |
86 | 84 | }) |
87 | 85 | |
86 | +const excludeCountryLevels = ['PROVINCE','CITY'] //如果从右侧配置选择全中国 | |
87 | + | |
88 | +const includeCityLevels = ['CITY'] //如果从右侧配置选择省份 | |
89 | + | |
90 | +//元组 优化if elseif else分支 隐藏返回图标 | |
91 | +const backIconMappingLevels: any[][] =[ | |
92 | + [ | |
93 | + (levelStr:string)=>levelStr===areaEnum.COUNTRY, | |
94 | + (level:string)=>excludeCountryLevels.includes(level), | |
95 | + ()=>toolBoxOption.value.feature.myFullButton.show=true, | |
96 | + ()=>toolBoxOption.value.feature.myFullButton.show=false | |
97 | + ], | |
98 | + [ | |
99 | + (levelStr:string)=>levelStr===areaEnum.PROVINCE, | |
100 | + (level:string)=>includeCityLevels.includes(level), | |
101 | + ()=>toolBoxOption.value.feature.myFullButton.show=true, | |
102 | + ()=>toolBoxOption.value.feature.myFullButton.show=false | |
103 | + ], | |
104 | +] | |
105 | + | |
88 | 106 | watch( |
89 | 107 | () => props.chartConfig.option, |
90 | - newData => { | |
91 | - const { iconColor, drillingIn, iconDistanceRight, iconDistanceTop, mapRegion } = newData | |
92 | - if (drillingIn && !newData.saveClickRegion.level) { | |
93 | - toolBoxOption.value.feature.myFullButton.show = !drillingIn | |
94 | - } else if ( | |
95 | - drillingIn && | |
96 | - newData.saveClickRegion.level === areaEnum.PROVINCE && | |
97 | - mapRegion.saveSelect.levelStr === areaEnum.COUNTRY | |
98 | - ) { | |
99 | - toolBoxOption.value.feature.myFullButton.show = drillingIn | |
100 | - } else if ( | |
101 | - drillingIn && | |
102 | - newData.saveClickRegion.level === areaEnum.COUNTRY && | |
103 | - mapRegion.saveSelect.levelStr === areaEnum.COUNTRY | |
104 | - ) { | |
105 | - toolBoxOption.value.feature.myFullButton.show = !drillingIn | |
106 | - } else if ( | |
107 | - drillingIn && | |
108 | - newData.saveClickRegion.level === areaEnum.CITY && | |
109 | - mapRegion.saveSelect.levelStr === areaEnum.PROVINCE | |
110 | - ) { | |
111 | - toolBoxOption.value.feature.myFullButton.show = drillingIn | |
112 | - } else if ( | |
113 | - drillingIn && | |
114 | - newData.saveClickRegion.level === areaEnum.PROVINCE && | |
115 | - mapRegion.saveSelect.levelStr === areaEnum.PROVINCE | |
116 | - ) { | |
117 | - toolBoxOption.value.feature.myFullButton.show = !drillingIn | |
108 | + (newData:optionType) => { | |
109 | + const { iconColor, iconDistanceRight, iconDistanceTop, mapRegion } = newData | |
110 | + const { saveSelect }=mapRegion | |
111 | + const { levelStr }=saveSelect | |
112 | + const findBackLevel = backIconMappingLevels.find((backLevelItem: backMapLevel[])=>backLevelItem[0](levelStr)) as backMapLevel[] | |
113 | + if(findBackLevel){ | |
114 | + if(findBackLevel[0]){ | |
115 | + const findLevel = findBackLevel[1](saveLevelStr.level) | |
116 | + if(findLevel)findBackLevel[2]() | |
117 | + else findBackLevel[3]() | |
118 | + } | |
118 | 119 | } |
119 | - toolBoxOption.value.feature.myFullButton.iconStyle.color = iconColor | |
120 | + toolBoxOption.value.feature.myFullButton.iconStyle.color = iconColor //返回图标颜色 | |
120 | 121 | toolBoxOption.value.right = iconDistanceRight |
121 | 122 | toolBoxOption.value.top = iconDistanceTop |
122 | 123 | vEchartsSetOption() |
123 | 124 | }, |
124 | 125 | { |
125 | - deep: true, | |
126 | + deep:true | |
126 | 127 | } |
127 | 128 | ) |
129 | + | |
130 | +//追加echarts右上角自带toolbox | |
128 | 131 | props.chartConfig.option = { |
129 | 132 | ...props.chartConfig.option, |
130 | 133 | ...{ toolbox: toolBoxOption.value } |
... | ... | @@ -132,25 +135,25 @@ props.chartConfig.option = { |
132 | 135 | |
133 | 136 | |
134 | 137 | //地图点击返回 adcode=100000,名字必须是china |
135 | -const watchAdcode = async () => { | |
138 | +const handleBack = async () => { | |
136 | 139 | if (props.chartConfig.option.drillingIn) { |
137 | 140 | //如果是从右边配置里设置的,比如点击四川省,然后点击返回 |
138 | 141 | const savePopParent = saveHistoryParent.value.pop() |
139 | - let saveAdcode: any = savePopParent?.adcode | |
142 | + let saveAdcode = savePopParent?.adcode as string | number | |
140 | 143 | saveLevelStr.level = savePopParent?.level as string |
141 | 144 | if (!savePopParent) { |
142 | 145 | saveAdcode = getParentAdcode(props.chartConfig.option.mapRegion.adcode) |
143 | - saveLevelStr.level = (regionMapParentArea as any)[props.chartConfig.option.mapRegion.saveSelect.levelStr] | |
146 | + saveLevelStr.level = (regionMapParentArea as Recordable)[props.chartConfig.option.mapRegion.saveSelect.levelStr] | |
144 | 147 | } |
145 | 148 | if (saveAdcode === 0) { |
146 | 149 | saveAdcode = 'china' |
147 | - saveLevelStr.level = 'COUNTRY' | |
150 | + saveLevelStr.level = areaEnum.COUNTRY | |
148 | 151 | } |
149 | 152 | await getGeojson(saveAdcode) |
150 | 153 | const adcode = saveAdcode === 100000 ? 'china' : saveAdcode |
151 | 154 | props.chartConfig.option.saveClickRegion.level = saveLevelStr.level |
152 | 155 | props.chartConfig.option.geo.map = adcode |
153 | - props.chartConfig.option.series.forEach((item: any) => { | |
156 | + props.chartConfig.option.series.forEach((item: Recordable) => { | |
154 | 157 | if (item.type === 'map') item.map = adcode |
155 | 158 | }) |
156 | 159 | vEchartsSetOption() |
... | ... | @@ -160,42 +163,36 @@ const watchAdcode = async () => { |
160 | 163 | |
161 | 164 | const vChartRef = ref<typeof VChart>() |
162 | 165 | |
163 | -const saveGeojson: any = ref({}) // 保存geojson | |
166 | +const saveGeojson: Recordable = ref({}) //保存geojson | |
164 | 167 | |
165 | 168 | const chinaDefaultRegionId = ref(100000) //如果是china则adcode为100000 |
166 | 169 | |
167 | -const saveLevelStr = reactive({ | |
170 | +const saveLevelStr = reactive<{level:historyParentType["level"]}>({ | |
168 | 171 | // 地区级别 |
169 | 172 | level: '' |
170 | 173 | }) |
171 | 174 | |
172 | -//父级地区编码和级别接口 | |
173 | -interface HistoryParentType { | |
174 | - adcode: number | |
175 | - level: string | |
176 | -} | |
177 | - | |
178 | -const saveHistoryParent = ref<HistoryParentType[]>([]) | |
175 | +const saveHistoryParent = ref<historyParentType[]>([]) | |
179 | 176 | |
180 | 177 | //动态注册地图 |
181 | -const getGeojson = async (regionId: any) => { | |
178 | +const getGeojson = async (regionId: string | number) => { | |
182 | 179 | try { |
183 | - const { levelStr } = props.chartConfig.option.mapRegion.saveSelect //右侧配置项获取的行政级别 | |
180 | + const { levelStr } = props.chartConfig.option.mapRegion.saveSelect //从右侧配置项获取的行政级别 | |
184 | 181 | const { data } = await getGeoJsonMap( |
185 | 182 | regionId === 'china' ? chinaDefaultRegionId.value : regionId, |
186 | - !saveLevelStr.level ? levelStr : saveLevelStr.level | |
183 | + !saveLevelStr.level ? levelStr : saveLevelStr.level //没有则获取右侧配置的行政级别 | |
187 | 184 | ) |
188 | 185 | const { geoJson, name, code, level } = data |
189 | 186 | const geoJsonFile = JSON.parse(geoJson) |
190 | 187 | if (!geoJsonFile) return |
191 | - saveGeojson.value = geoJsonFile | |
188 | + saveGeojson.value = geoJsonFile//保存一份服务端返回的geojson | |
192 | 189 | const nameChina = name === '中国' ? 'china' : name |
193 | - registerMap(level === areaEnum.COUNTRY ? nameChina : code, { geoJSON: geoJsonFile as any, specialAreas: {} }) | |
190 | + registerMap(level === areaEnum.COUNTRY ? nameChina : code, { geoJSON: geoJsonFile, specialAreas: {} })//注册地图 geoJSON未做ts类型 | |
194 | 191 | loading.value = false |
195 | 192 | } catch (error) { |
196 | 193 | loading.value = false |
197 | 194 | console.error('动态注册地图出错', error) |
198 | - //注册出错则注册空的,不然在选择正确的adcode,则视图无法更新 | |
195 | + //注册出错则注册空的,不然在选择正确的adcode,视图无法更新 | |
199 | 196 | registerMap(props.chartConfig.option.mapRegion.adcode, { geoJSON: {} as any, specialAreas: {} }) |
200 | 197 | } |
201 | 198 | } |
... | ... | @@ -227,11 +224,11 @@ const vEchartsSetOption = async () => { |
227 | 224 | } |
228 | 225 | |
229 | 226 | // 更新数据处理 |
230 | -const dataSetHandle = async (dataset: any) => { | |
231 | - props.chartConfig.option.series.forEach((item: any) => { | |
227 | +const dataSetHandle = async (dataset: Recordable) => { | |
228 | + props.chartConfig.option.series.forEach((item: Recordable) => { | |
232 | 229 | if (item.type === 'effectScatter' && dataset.point) item.data = dataset.point |
233 | 230 | else if (item.type === 'lines' && dataset.line) { |
234 | - item.data = dataset.line.map((it: any) => { | |
231 | + item.data = dataset.line.map((it: Recordable) => { | |
235 | 232 | return { |
236 | 233 | ...it, |
237 | 234 | lineStyle: { |
... | ... | @@ -242,11 +239,10 @@ const dataSetHandle = async (dataset: any) => { |
242 | 239 | } else if (item.type === 'map' && dataset.map) item.data = dataset.map |
243 | 240 | }) |
244 | 241 | if (dataset.pieces) props.chartConfig.option.visualMap.pieces = dataset.pieces |
245 | - | |
246 | 242 | isPreview() && vEchartsSetOption() |
247 | 243 | } |
248 | 244 | |
249 | -// 处理海南群岛 | |
245 | +// 单独处理海南群岛 | |
250 | 246 | const hainanLandsHandle = async (newData: boolean) => { |
251 | 247 | if (newData) { |
252 | 248 | await getGeojson('china') |
... | ... | @@ -275,7 +271,7 @@ watch( |
275 | 271 | await hainanLandsHandle(newData) |
276 | 272 | vEchartsSetOption() |
277 | 273 | } catch (error) { |
278 | - console.log(error) | |
274 | + console.log('监听是否显示南海群岛出错',error) | |
279 | 275 | } |
280 | 276 | }, |
281 | 277 | { |
... | ... | @@ -284,12 +280,11 @@ watch( |
284 | 280 | ) |
285 | 281 | |
286 | 282 | //处理数据标点包括飞线 |
287 | -const handleDataPoint = (newData: any) => { | |
283 | +const handleDataPoint = (newData: string | number) => { | |
288 | 284 | if (newData === 'china') { |
289 | 285 | dataSetHandle(dataJson) |
290 | 286 | } else { |
291 | - const filterPoint = dataJson.point.filter((item: any) => item.adcode === newData) | |
292 | - // const filterLine = dataJson.line.filter((item: any) => item.adcode === newData) | |
287 | + const filterPoint = dataJson.point.filter((item: Recordable) => item.adcode === newData) | |
293 | 288 | dataSetHandle({ |
294 | 289 | point: filterPoint, |
295 | 290 | line: [] //由于点击单个地图模块,所以去除飞线 |
... | ... | @@ -304,13 +299,13 @@ watch( |
304 | 299 | try { |
305 | 300 | await getGeojson(newData) |
306 | 301 | props.chartConfig.option.geo.map = newData |
307 | - props.chartConfig.option.series.forEach((item: any) => { | |
302 | + props.chartConfig.option.series.forEach((item: Recordable) => { | |
308 | 303 | if (item.type === 'map') item.map = newData |
309 | 304 | }) |
310 | 305 | vEchartsSetOption() |
311 | 306 | handleDataPoint(newData) |
312 | 307 | } catch (error) { |
313 | - console.log(error) | |
308 | + console.log('监听地图展示区域发生变化出错',error) | |
314 | 309 | } |
315 | 310 | }, |
316 | 311 | { |
... | ... | @@ -319,22 +314,23 @@ watch( |
319 | 314 | } |
320 | 315 | ) |
321 | 316 | |
322 | -// 预览 | |
323 | -useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => { | |
317 | +//预览 | |
318 | +useChartDataFetch(props.chartConfig, useChartEditStore, (newData: Recordable) => { | |
324 | 319 | dataSetHandle(newData) |
325 | 320 | }) |
326 | 321 | |
322 | +//地区上级对应配置 | |
327 | 323 | const regionMapParentArea = { |
328 | - [areaEnum.PROVINCE]: areaEnum.COUNTRY, //省份的上一级 中国 | |
329 | - [areaEnum.CITY]: areaEnum.PROVINCE, //城市的上一级 省份 | |
330 | - [areaEnum.COUNTY]: areaEnum.CITY, //县或者区的上一级 城市 | |
331 | - [areaEnum.TOWN]: areaEnum.COUNTY //镇的上一级 县或者区 | |
324 | + PROVINCE: areaEnum.COUNTRY, //省份的上一级 中国 | |
325 | + CITY: areaEnum.PROVINCE, //城市的上一级 省份 | |
326 | + COUNTY: areaEnum.CITY, //县或者区的上一级 城市 | |
327 | + TOWN: areaEnum.COUNTY //镇的上一级 县或者区 | |
332 | 328 | } |
333 | 329 | |
334 | -//传adcode 获取上级 | |
330 | +//传地区adcode 获取上级 | |
335 | 331 | const getParentAdcode = (adcode: number) => { |
336 | 332 | let adcodeNum = 100000 |
337 | - saveGeojson.value?.features.forEach((item: any) => { | |
333 | + saveGeojson.value?.features.forEach((item: Recordable) => { | |
338 | 334 | if (item.properties.adcode === adcode) { |
339 | 335 | adcodeNum = item.properties.parent.adcode |
340 | 336 | } |
... | ... | @@ -343,22 +339,22 @@ const getParentAdcode = (adcode: number) => { |
343 | 339 | } |
344 | 340 | |
345 | 341 | //地图点击下钻 |
346 | -const handleVChartClick = async (params: any) => { | |
342 | +const handleVChartClick = async (params: Recordable) => { | |
347 | 343 | if (props.chartConfig.option.drillingIn) { |
348 | 344 | const { name } = params |
349 | - saveGeojson.value?.features.forEach((item: any) => { | |
345 | + saveGeojson.value?.features.forEach((item: Recordable) => { | |
350 | 346 | if (item.properties.name === name) { |
351 | 347 | const level = item.properties.level.toUpperCase() |
352 | 348 | const adcode = item.properties.adcode |
353 | - if (level === 'DISTRICT') return | |
354 | - if (String(adcode).startsWith('15') && level === areaEnum.CITY) return | |
349 | + if (level === areaEnum.DISTRICT) return //下钻暂且不支持地区 | |
350 | + if (String(adcode).startsWith('15') && level === areaEnum.CITY) return //特殊处理地区码15开头的 | |
355 | 351 | props.chartConfig.option.mapRegion.adcode = adcode |
356 | 352 | props.chartConfig.option.saveClickRegion.level = level |
357 | 353 | saveLevelStr.level = level |
358 | 354 | handleDataPoint(adcode) |
359 | 355 | saveHistoryParent.value.push({ |
360 | 356 | adcode: item.properties.parent.adcode, |
361 | - level: (regionMapParentArea as any)[level] | |
357 | + level: (regionMapParentArea as Recordable)[level] | |
362 | 358 | }) |
363 | 359 | } |
364 | 360 | }) | ... | ... |
... | ... | @@ -18,10 +18,12 @@ |
18 | 18 | :dampingFactor="dampingFactor" |
19 | 19 | @process="onProcess" |
20 | 20 | @load="onLoad" |
21 | + @mousedown="handleMouseDown" | |
21 | 22 | :position="position" |
22 | 23 | :rotation="rotation" |
23 | 24 | :lights="[lights[lightInput]]" |
24 | 25 | :showFps="showFps" |
26 | + :intersectRecursive="true" | |
25 | 27 | :labels="labels" |
26 | 28 | /> |
27 | 29 | <div v-show="show" class="process"> |
... | ... | @@ -96,6 +98,11 @@ const { |
96 | 98 | labels, |
97 | 99 | lightInput |
98 | 100 | } = toRefs(props.chartConfig.option) as any |
101 | + | |
102 | +const handleMouseDown = (e:any,d:any)=>{ | |
103 | + console.log(e) | |
104 | + console.log(d) | |
105 | +} | |
99 | 106 | </script> |
100 | 107 | |
101 | 108 | <style lang="scss" scoped> | ... | ... |
... | ... | @@ -19,7 +19,8 @@ export const option = { |
19 | 19 | buttonTextColor: 'white', |
20 | 20 | buttonTextSize: '16', |
21 | 21 | buttonTextBold: 500, |
22 | - selectTargetItems: [] | |
22 | + selectTargetItems: [], | |
23 | + quaternary: false | |
23 | 24 | } |
24 | 25 | |
25 | 26 | export default class Config extends PublicConfigClass implements CreateComponentType { | ... | ... |
... | ... | @@ -13,6 +13,11 @@ |
13 | 13 | <n-switch v-model:value="optionData.buttonGhost" /> |
14 | 14 | </setting-item> |
15 | 15 | </setting-item-box> |
16 | + <setting-item-box name="边框"> | |
17 | + <setting-item name="是否开启"> | |
18 | + <n-switch v-model:value="optionData.quaternary" /> | |
19 | + </setting-item> | |
20 | + </setting-item-box> | |
16 | 21 | <setting-item-box name="颜色"> |
17 | 22 | <setting-item name=""> |
18 | 23 | <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.buttonColor"></n-color-picker> | ... | ... |
1 | 1 | <template> |
2 | 2 | <n-button |
3 | + :quaternary="quaternary" | |
3 | 4 | :style="{ width: `${w}px`, height: `${h}px` }" |
4 | 5 | :type="buttonType" |
5 | 6 | :dashed="buttonDashed" |
... | ... | @@ -29,7 +30,7 @@ const props = defineProps({ |
29 | 30 | |
30 | 31 | const { w, h } = toRefs(props.chartConfig.attr) |
31 | 32 | |
32 | -const { buttonType, buttonDashed, buttonGhost, buttonColor, buttonTextColor, buttonTextSize, dataset,buttonTextBold } = toRefs( | |
33 | +const { buttonType, buttonDashed, buttonGhost, buttonColor, buttonTextColor, buttonTextSize, dataset, buttonTextBold, quaternary } = toRefs( | |
33 | 34 | props.chartConfig.option |
34 | 35 | ) |
35 | 36 | ... | ... |
... | ... | @@ -19,7 +19,8 @@ export const option = { |
19 | 19 | buttonTextColor: 'white', |
20 | 20 | buttonTextSize: '16', |
21 | 21 | buttonTextBold: 500, |
22 | - selectTargetItems: [] | |
22 | + selectTargetItems: [], | |
23 | + quaternary: false | |
23 | 24 | } |
24 | 25 | |
25 | 26 | export default class Config extends PublicConfigClass implements CreateComponentType { | ... | ... |
... | ... | @@ -13,6 +13,11 @@ |
13 | 13 | <n-switch v-model:value="optionData.buttonGhost" /> |
14 | 14 | </setting-item> |
15 | 15 | </setting-item-box> |
16 | + <setting-item-box name="边框"> | |
17 | + <setting-item name="是否开启"> | |
18 | + <n-switch v-model:value="optionData.quaternary" /> | |
19 | + </setting-item> | |
20 | + </setting-item-box> | |
16 | 21 | <setting-item-box name="颜色"> |
17 | 22 | <setting-item name=""> |
18 | 23 | <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.buttonColor"></n-color-picker> | ... | ... |
1 | 1 | <template> |
2 | 2 | <n-button |
3 | + :quaternary="quaternary" | |
3 | 4 | :style="{ width: `${w}px`, height: `${h}px` }" |
4 | 5 | :type="buttonType" |
5 | 6 | :dashed="buttonDashed" |
... | ... | @@ -29,7 +30,7 @@ const props = defineProps({ |
29 | 30 | |
30 | 31 | const { w, h } = toRefs(props.chartConfig.attr) |
31 | 32 | |
32 | -const { buttonType, buttonDashed, buttonGhost, buttonColor, buttonTextColor, buttonTextSize, dataset, buttonTextBold } = toRefs( | |
33 | +const { buttonType, buttonDashed, buttonGhost, buttonColor, buttonTextColor, buttonTextSize, dataset, buttonTextBold, quaternary } = toRefs( | |
33 | 34 | props.chartConfig.option |
34 | 35 | ) |
35 | 36 | ... | ... |
... | ... | @@ -70,17 +70,20 @@ const transitionDuration = computed(() => { |
70 | 70 | |
71 | 71 | // 预览更新 |
72 | 72 | useChartDataFetch(props.chartConfig, useChartEditStore, (newData: string) => { |
73 | - const chartEditStore = useChartEditStore() | |
74 | - //判断只要是分组并且分组绑定了ws | |
75 | - let hasGroupWs = false | |
76 | - chartEditStore.getComponentList.some((item: CreateComponentType) => { | |
77 | - if (item.request.requestUrl?.includes('ws')) { | |
78 | - hasGroupWs = true | |
79 | - } | |
80 | - }) | |
81 | - if (!hasGroupWs) { | |
73 | + if(Object.prototype.toString.call(newData) !== '[object Object]'){ | |
82 | 74 | option.dataset = newData |
83 | 75 | } |
76 | + // const chartEditStore = useChartEditStore() | |
77 | + // //判断只要是分组并且分组绑定了ws | |
78 | + // let hasGroupWs = false | |
79 | + // chartEditStore.getComponentList.some((item: CreateComponentType) => { | |
80 | + // if (item.request.requestUrl?.includes('ws')) { | |
81 | + // hasGroupWs = true | |
82 | + // } | |
83 | + // }) | |
84 | + // if (!hasGroupWs) { | |
85 | + // option.dataset = newData | |
86 | + // } | |
84 | 87 | }) |
85 | 88 | </script> |
86 | 89 | ... | ... |
... | ... | @@ -57,17 +57,20 @@ watch( |
57 | 57 | |
58 | 58 | // 预览更新 |
59 | 59 | useChartDataFetch(props.chartConfig, useChartEditStore, (newData: string) => { |
60 | - const chartEditStore = useChartEditStore() | |
61 | - //判断只要是分组并且分组绑定了ws | |
62 | - let hasGroupWs = false | |
63 | - chartEditStore.getComponentList.some((item: CreateComponentType) => { | |
64 | - if (item.request.requestUrl?.includes('ws')) { | |
65 | - hasGroupWs = true | |
66 | - } | |
67 | - }) | |
68 | - if (!hasGroupWs) { | |
60 | + if(Object.prototype.toString.call(newData) !== '[object Object]'){ | |
69 | 61 | option.dataset = newData |
70 | 62 | } |
63 | + // const chartEditStore = useChartEditStore() | |
64 | + // //判断只要是分组并且分组绑定了ws | |
65 | + // let hasGroupWs = false | |
66 | + // chartEditStore.getComponentList.some((item: CreateComponentType) => { | |
67 | + // if (item.request.requestUrl?.includes('ws')) { | |
68 | + // hasGroupWs = true | |
69 | + // } | |
70 | + // }) | |
71 | + // if (!hasGroupWs) { | |
72 | + // option.dataset = newData | |
73 | + // } | |
71 | 74 | }) |
72 | 75 | |
73 | 76 | //打开链接 | ... | ... |
... | ... | @@ -38,17 +38,20 @@ watch( |
38 | 38 | ) |
39 | 39 | |
40 | 40 | useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => { |
41 | - const chartEditStore = useChartEditStore() | |
42 | - //判断只要是分组并且分组绑定了ws | |
43 | - let hasGroupWs = false | |
44 | - chartEditStore.getComponentList.some((item: CreateComponentType) => { | |
45 | - if (item.request.requestUrl?.includes('ws')) { | |
46 | - hasGroupWs = true | |
47 | - } | |
48 | - }) | |
49 | - if (!hasGroupWs) { | |
41 | + if(Object.prototype.toString.call(newData) !== '[object Object]'){ | |
50 | 42 | option.dataset = newData |
51 | 43 | } |
44 | + // const chartEditStore = useChartEditStore() | |
45 | + // //判断只要是分组并且分组绑定了ws | |
46 | + // let hasGroupWs = false | |
47 | + // chartEditStore.getComponentList.some((item: CreateComponentType) => { | |
48 | + // if (item.request.requestUrl?.includes('ws')) { | |
49 | + // hasGroupWs = true | |
50 | + // } | |
51 | + // }) | |
52 | + // if (!hasGroupWs) { | |
53 | + // option.dataset = newData | |
54 | + // } | |
52 | 55 | }) |
53 | 56 | </script> |
54 | 57 | ... | ... |
... | ... | @@ -23,14 +23,17 @@ import { OverrideBarCommonConfig } from '@/packages/components/external/Charts/B |
23 | 23 | import { OverrideLineCommonConfig } from '@/packages/components/external/Charts/Lines/OverrideLineCommon' |
24 | 24 | import { OverrideLineGradientsConfig } from '@/packages/components/external/Charts/Lines/OverrideLineGradients' |
25 | 25 | import { OverrideLineForDeviceHistoryQueryConfig } from '@/packages/components/external/Charts/Lines/OverrideLineForDeviceHistoryQuery' |
26 | +import { OverrideLineRealTimeConfig } from '@/packages/components/external/Charts/Lines/OverrideLineRealTime' | |
27 | +import { AddLinePlotConfig } from '@/packages/components/external/Charts/Lines/AddLinePlot' | |
26 | 28 | import { OverrideProcessConfig } from '@/packages/components/external/Charts/Mores/OverrideProcess' |
27 | 29 | import { OverridePieCircleConfig } from '@/packages/components/external/Charts/Pies/OverridePieCircle' |
28 | 30 | import { OverrideMapBaseConfig } from '@/packages/components/external/Charts/Maps/OverrideMapBase' |
29 | 31 | import { OverrideMapAmapConfig } from '@/packages/components/external/Charts/Maps/OverrideMapAmap' |
32 | +import { AddRealTimeTrajectoryAmapConfig } from '@/packages/components/external/Charts/Maps/AddRealTimeTrajectoryAmap' | |
33 | +import { AddHistoryTimeTrajectoryAmapConfig } from '@/packages/components/external/Charts/Maps/AddHistoryTimeTrajectoryAmap' | |
30 | 34 | import { AddThreeDimensionalMapConfig } from '@/packages/components/external/Charts/Maps/AddThreeDimensionalMap' |
31 | 35 | import { OverrideWaterPoloConfig } from '@/packages/components/external/Charts/Mores/OverrideWaterPolo' |
32 | 36 | import { OverrideDialConfig } from '@/packages/components/external/Charts/Mores/OverrideDial' |
33 | -import { OverrideLineRealTimeConfig } from '@/packages/components/external/Charts/Lines/OverrideLineRealTime' | |
34 | 37 | import { PickIconConfig } from '@/packages/components/external/Decorates/Mores/PickIcon' |
35 | 38 | import { WeatherConfig } from '@/packages/components/external/Decorates/Mores/Weather' |
36 | 39 | import { ThreeDimensionalConfig } from '@/packages/components/external/Decorates/Three/ThreeDimensional' |
... | ... | @@ -56,6 +59,8 @@ import { OverrideTableScrollBoardConfig } from '@/packages/components/external/T |
56 | 59 | |
57 | 60 | /** |
58 | 61 | * 重写动态注入 |
62 | + * Override是重写的 | |
63 | + * Add是新增的 | |
59 | 64 | */ |
60 | 65 | export function useInjectLib(packagesList: EPackagesType) { |
61 | 66 | // packagesList[EPackagesCategoryEnum.COMPOSES] = ComposesList //此项目扩展侧边栏功能,保留待用 |
... | ... | @@ -108,10 +113,13 @@ export function useInjectLib(packagesList: EPackagesType) { |
108 | 113 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideLineCommonConfig)//重写图表下的折线图 |
109 | 114 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideLineGradientsConfig)//重写图表下的渐变折线图 |
110 | 115 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideLineRealTimeConfig)//新增图表下的实时折线图 |
116 | + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, AddLinePlotConfig)//新增图表下的函数绘图 | |
111 | 117 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideProcessConfig)//重写图表下的native ui |
112 | 118 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverridePieCircleConfig)//重写图表下的饼图 |
113 | 119 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideMapBaseConfig)//重写图表下的地图 |
114 | 120 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideMapAmapConfig)//重写图表下的高德地图 |
121 | + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, AddRealTimeTrajectoryAmapConfig)//新增图表下的设备实时轨迹地图 | |
122 | + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, AddHistoryTimeTrajectoryAmapConfig)//新增图表下的设备历史轨迹地图 | |
115 | 123 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, AddThreeDimensionalMapConfig)//新增图表下的3d echarts地图 |
116 | 124 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideWaterPoloConfig)//重写图表下的水球图 |
117 | 125 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideDialConfig)//重写图表下的表盘 | ... | ... |
... | ... | @@ -280,7 +280,9 @@ export const useSocketStore = defineStore({ |
280 | 280 | targetItem.groupList?.forEach((item: CreateComponentType) => { |
281 | 281 | dumpSaveHistoryInput?.forEach((inputItem: { id: string, inputValue: string }) => { |
282 | 282 | if (item.id === inputItem.id) { |
283 | - item.option.dataset = Function('res', `return ${inputItem?.inputValue}`)(targetItem.option?.dataset) | |
283 | + // item.option.dataset = Function('res', `return ${inputItem?.inputValue}`)(targetItem.option?.dataset) | |
284 | + item.option.dataset = Function('res', `return ${targetItem.option?.dataset[inputItem?.inputValue]}`)(targetItem.option?.dataset) | |
285 | + item.option.selectKey = inputItem?.inputValue // 回显选择的下拉框 | |
284 | 286 | } |
285 | 287 | }) |
286 | 288 | }) | ... | ... |
... | ... | @@ -33,7 +33,7 @@ defineExpose({ |
33 | 33 | <template> |
34 | 34 | <NSpace> |
35 | 35 | <NText>背景选择</NText> |
36 | - <NSelect size="small" placeholder="请选择您要使用的背景" clearable style="width: 250px" :value="canvasConfig.backgroundImage" | |
36 | + <NSelect size="small" placeholder="请选择您要使用的背景" clearable style="width: 250px" :value="!canvasConfig.backgroundImage?null:canvasConfig.backgroundImage" | |
37 | 37 | :options="getAllBackgroundImageList" :render-label="renderOption" @update-value="handleUpdateValue" /> |
38 | 38 | </NSpace> |
39 | 39 | </template> | ... | ... |
... | ... | @@ -117,11 +117,14 @@ defineExpose({ |
117 | 117 | <ComponentConfiguration v-if="requestDataType === RequestDataTypeEnum.AJAX" ref="componentConfigurationEl" /> |
118 | 118 | <PublicInterfaceForm v-if="requestDataType === RequestDataTypeEnum.Pond" ref="publicInterfaceFormEl" /> |
119 | 119 | <template #action> |
120 | - <NSpace justify="space-between"> | |
121 | - <div style="display: flex; align-items: center; "> | |
122 | - <span>「 更多 」</span> | |
123 | - <span style="margin-right: 5px;">——</span> | |
124 | - <NTag type="info">{{ getRequestTypeName }}</NTag> | |
120 | + <NSpace justify="space-between"> | |
121 | + <div style="display: flex;flex-direction: column;"> | |
122 | + <NTag type="info">当公共接口选择“实时轨迹或者获取设备经纬度历史数据”时,属性选择则选择经纬度即可(longitude,latitude)</NTag> | |
123 | + <div style="display: flex; align-items: center; margin-top:10px"> | |
124 | + <span>「 更多 」</span> | |
125 | + <span style="margin-right: 5px;">——</span> | |
126 | + <NTag type="info">{{ getRequestTypeName }}</NTag> | |
127 | + </div> | |
125 | 128 | </div> |
126 | 129 | <NButton type="primary" @click="handleSaveAndRequest">保存 & 发送请求</NButton> |
127 | 130 | </NSpace> | ... | ... |
... | ... | @@ -5,11 +5,11 @@ |
5 | 5 | <n-card size="small"> |
6 | 6 | <n-code word-wrap :code="toString(targetData.option.dataset)" language="json"></n-code> |
7 | 7 | </n-card> |
8 | - <n-tag type="info"> 使用res点语法或者res中括号语法 </n-tag> | |
9 | - <n-tag type="warning">示例:res.xxxx</n-tag> | |
10 | - <span>测试一下</span> | |
11 | - <n-input type="textarea" @input="handleTestInput($event, option)" size="small" placeholder="请输入"></n-input> | |
12 | - <n-code word-wrap :code="toString(testInputValue)" language="json"></n-code> | |
8 | + <n-tag type="info"> 目前支持实时多属性 </n-tag> | |
9 | + <!-- <n-tag type="warning">示例:res.xxxx</n-tag> --> | |
10 | + <!-- <span>目前支持实时多属性</span> --> | |
11 | + <!-- <n-input type="textarea" @input="handleTestInput($event, option)" size="small" placeholder="请输入"></n-input> | |
12 | + <n-code word-wrap :code="toString(testInputValue)" language="json"></n-code> --> | |
13 | 13 | <n-divider /> |
14 | 14 | <div v-for="(item, index) in groupList" :key="item.key + index"> |
15 | 15 | <n-space justify="space-between"> |
... | ... | @@ -21,10 +21,16 @@ |
21 | 21 | <n-input size="small" v-model:value="item.chartConfig.title" :disabled="true"></n-input> |
22 | 22 | </n-space> |
23 | 23 | <n-space vertical justify="space-between"> |
24 | - <n-ellipsis> 设置数据 </n-ellipsis> | |
24 | + <n-ellipsis> 选择键值 </n-ellipsis> | |
25 | + <n-select size="small" :placeholder="String(!item.option.selectKey?'':item.option.selectKey)" | |
26 | + :options="dataCacheKeys" @update:value="(key:string,options:SelectOption[]) => handleSelectDataKey(key,options,item.id,groupList!)"> | |
27 | + </n-select> | |
28 | + </n-space> | |
29 | + <n-space vertical justify="space-between"> | |
30 | + <n-ellipsis>数据内容 </n-ellipsis> | |
31 | + <!-- @update:value="handleInput(groupList!, item.id, $event)" --> | |
25 | 32 | <n-input |
26 | 33 | type="textarea" |
27 | - @update:value="handleInput(groupList!, item.id, $event)" | |
28 | 34 | size="small" |
29 | 35 | :placeholder="String(item.option.dataset)" |
30 | 36 | ></n-input> |
... | ... | @@ -36,33 +42,39 @@ |
36 | 42 | </template> |
37 | 43 | |
38 | 44 | <script setup lang="ts"> |
39 | -import { toRefs, ref, watch } from 'vue' | |
45 | +import { toRefs, ref, watch,computed } from 'vue' | |
40 | 46 | import { useTargetData } from '../../hooks/useTargetData.hook' |
41 | 47 | import { toString } from '@/utils' |
42 | 48 | import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d' |
43 | 49 | import { datasetType, saveHistoryInputValueListType, historyInputValue } from './index.d' |
44 | -import { GlobalThemeJsonType } from '@/settings/chartThemes/index' | |
45 | 50 | import { uniqBy } from 'lodash' |
51 | +import { SelectOption } from 'naive-ui' | |
46 | 52 | |
47 | 53 | const { targetData } = useTargetData() |
48 | 54 | |
49 | 55 | const { groupList, option } = toRefs(targetData.value) |
50 | 56 | |
51 | -const testInputValue = ref('') | |
52 | - | |
53 | 57 | const saveHistoryInputValueList = ref<saveHistoryInputValueListType>([]) |
54 | 58 | |
55 | -const handleInput = (groupList: CreateComponentType[], id: string, inputValue: string) => { | |
59 | +const handleSelectDataKey = (key:string, options:SelectOption[], currentComponentId:string, groupList:CreateComponentType[]) => { | |
56 | 60 | saveHistoryInputValueList.value.unshift({ |
57 | - id, | |
58 | - inputValue | |
61 | + id:currentComponentId, | |
62 | + inputValue:key | |
59 | 63 | }) |
60 | - handleGroupListById(groupList, id, inputValue, option.value.dataset) | |
64 | + handleGroupListById(groupList, currentComponentId, key, option.value.dataset) | |
61 | 65 | } |
62 | 66 | |
63 | -const executeFn = (inputValue: string, dataset: datasetType) => { | |
67 | +// const handleInput = (groupList: CreateComponentType[], id: string, inputValue: string) => { | |
68 | +// saveHistoryInputValueList.value.unshift({ | |
69 | +// id, | |
70 | +// inputValue | |
71 | +// }) | |
72 | +// handleGroupListById(groupList, id, inputValue, option.value.dataset) | |
73 | +// } | |
74 | + | |
75 | +const executeFn = (inputValue: string, dataset: any) => { | |
64 | 76 | try { |
65 | - return Function('res', `return ${inputValue}`)(dataset) | |
77 | + return Function('res', `return ${dataset[inputValue]}`)(dataset) | |
66 | 78 | } catch (e) { |
67 | 79 | return inputValue |
68 | 80 | } |
... | ... | @@ -82,14 +94,26 @@ const handleGroupListById = ( |
82 | 94 | //原生for循环优化性能 |
83 | 95 | for (let index = 0, total = groupList.length; index < total; index++) { |
84 | 96 | const targetItem = groupList[index] |
85 | - if (id === targetItem.id) targetItem.option.dataset = setTargetDataset(inputValue, dataset) | |
97 | + if (id === targetItem.id) { | |
98 | + targetItem.option.selectKey = inputValue // 回显选择的下拉框 | |
99 | + targetItem.option.dataset = setTargetDataset(inputValue, dataset) | |
100 | + } | |
86 | 101 | } |
87 | 102 | } |
88 | 103 | |
89 | -//测试返回 | |
90 | -const handleTestInput = (inputValue: string, { dataset }: GlobalThemeJsonType) => { | |
91 | - testInputValue.value = setTargetDataset(inputValue, dataset) | |
92 | -} | |
104 | + | |
105 | +//键值 | |
106 | +const dataCacheKeys = computed(()=>{ | |
107 | + const datasetCache = targetData.value.option.dataset | |
108 | + if(!datasetCache) return [] | |
109 | + if(datasetCache.constructor === Object){ | |
110 | + //暂且不兼容嵌套对象 | |
111 | + if(Reflect.ownKeys(datasetCache).length !== 0){ | |
112 | + return Object.keys(datasetCache).map((dataItem:string)=>({label:dataItem,value:dataItem})) as SelectOption[] | |
113 | + } | |
114 | + } | |
115 | + return [] | |
116 | +}) | |
93 | 117 | |
94 | 118 | watch( |
95 | 119 | () => targetData.value, | ... | ... |
src/views/chart/ContentConfigurations/components/external/ThingsKitChartData/oldIndex.vue
0 → 100644
1 | +<template> | |
2 | + <div class="go-chart-configurations-data" v-if="targetData"> | |
3 | + <n-space vertical> | |
4 | + <n-tag type="success"> 分组返回的数据源 </n-tag> | |
5 | + <n-card size="small"> | |
6 | + <n-code word-wrap :code="toString(targetData.option.dataset)" language="json"></n-code> | |
7 | + </n-card> | |
8 | + <n-tag type="info"> 使用res点语法或者res中括号语法 </n-tag> | |
9 | + <n-tag type="warning">示例:res.xxxx</n-tag> | |
10 | + <span>测试一下</span> | |
11 | + <n-input type="textarea" @input="handleTestInput($event, option)" size="small" placeholder="请输入"></n-input> | |
12 | + <n-code word-wrap :code="toString(testInputValue)" language="json"></n-code> | |
13 | + <n-divider /> | |
14 | + <div v-for="(item, index) in groupList" :key="item.key + index"> | |
15 | + <n-space justify="space-between"> | |
16 | + <n-ellipsis> 组件id </n-ellipsis> | |
17 | + <n-input size="small" v-model:value="item.id" :disabled="true"></n-input> | |
18 | + </n-space> | |
19 | + <n-space justify="space-between"> | |
20 | + <n-ellipsis> 组件名称 </n-ellipsis> | |
21 | + <n-input size="small" v-model:value="item.chartConfig.title" :disabled="true"></n-input> | |
22 | + </n-space> | |
23 | + <n-space vertical justify="space-between"> | |
24 | + <n-ellipsis> 设置数据 </n-ellipsis> | |
25 | + <n-input | |
26 | + type="textarea" | |
27 | + @update:value="handleInput(groupList!, item.id, $event)" | |
28 | + size="small" | |
29 | + :placeholder="String(item.option.dataset)" | |
30 | + ></n-input> | |
31 | + </n-space> | |
32 | + <n-divider /> | |
33 | + </div> | |
34 | + </n-space> | |
35 | + </div> | |
36 | +</template> | |
37 | + | |
38 | +<script setup lang="ts"> | |
39 | +import { toRefs, ref, watch } from 'vue' | |
40 | +import { useTargetData } from '../../hooks/useTargetData.hook' | |
41 | +import { toString } from '@/utils' | |
42 | +import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d' | |
43 | +import { datasetType, saveHistoryInputValueListType, historyInputValue } from './index.d' | |
44 | +import { GlobalThemeJsonType } from '@/settings/chartThemes/index' | |
45 | +import { uniqBy } from 'lodash' | |
46 | + | |
47 | +const { targetData } = useTargetData() | |
48 | + | |
49 | +const { groupList, option } = toRefs(targetData.value) | |
50 | + | |
51 | +const testInputValue = ref('') | |
52 | + | |
53 | +const saveHistoryInputValueList = ref<saveHistoryInputValueListType>([]) | |
54 | + | |
55 | +const handleInput = (groupList: CreateComponentType[], id: string, inputValue: string) => { | |
56 | + saveHistoryInputValueList.value.unshift({ | |
57 | + id, | |
58 | + inputValue | |
59 | + }) | |
60 | + handleGroupListById(groupList, id, inputValue, option.value.dataset) | |
61 | +} | |
62 | + | |
63 | +const executeFn = (inputValue: string, dataset: datasetType) => { | |
64 | + try { | |
65 | + return Function('res', `return ${inputValue}`)(dataset) | |
66 | + } catch (e) { | |
67 | + return inputValue | |
68 | + } | |
69 | +} | |
70 | + | |
71 | +const setTargetDataset = (inputValue: string, dataset: datasetType) => { | |
72 | + //不能用!,会排除0,0也是需要显示的 | |
73 | + return executeFn(inputValue, dataset) === undefined ? '' : executeFn(inputValue, dataset) | |
74 | +} | |
75 | + | |
76 | +const handleGroupListById = ( | |
77 | + groupList: CreateComponentType[], | |
78 | + id: string, | |
79 | + inputValue: string, | |
80 | + dataset: datasetType | |
81 | +) => { | |
82 | + //原生for循环优化性能 | |
83 | + for (let index = 0, total = groupList.length; index < total; index++) { | |
84 | + const targetItem = groupList[index] | |
85 | + if (id === targetItem.id) targetItem.option.dataset = setTargetDataset(inputValue, dataset) | |
86 | + } | |
87 | +} | |
88 | + | |
89 | +//测试返回 | |
90 | +const handleTestInput = (inputValue: string, { dataset }: GlobalThemeJsonType) => { | |
91 | + testInputValue.value = setTargetDataset(inputValue, dataset) | |
92 | +} | |
93 | + | |
94 | +watch( | |
95 | + () => targetData.value, | |
96 | + (newValue: CreateComponentType | CreateComponentGroupType) => { | |
97 | + try { | |
98 | + const uniqHistoryInputValueList = uniqBy(saveHistoryInputValueList.value, 'id') | |
99 | + if (uniqHistoryInputValueList) { | |
100 | + newValue.saveHistoryInput = JSON.stringify(uniqHistoryInputValueList) | |
101 | + window.localStorage.setItem('CACHE_HISTORY_INPUT_VALUE', JSON.stringify(uniqHistoryInputValueList)) | |
102 | + } | |
103 | + newValue?.groupList?.forEach((item: CreateComponentType) => { | |
104 | + uniqHistoryInputValueList.forEach((uniqueItem: historyInputValue) => { | |
105 | + if (uniqueItem.id === item.id) { | |
106 | + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
107 | + handleGroupListById(newValue.groupList!, uniqueItem.id, uniqueItem.inputValue, newValue.option.dataset) | |
108 | + } | |
109 | + }) | |
110 | + }) | |
111 | + } catch (e) { | |
112 | + console.log(e) | |
113 | + } | |
114 | + }, | |
115 | + { | |
116 | + deep: true | |
117 | + } | |
118 | +) | |
119 | +</script> | ... | ... |
1 | 1 | import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' |
2 | -import { canvasCut, downloadTextFile, JSONStringify } from '@/utils' | |
2 | +import { downloadTextFile, JSONStringify } from '@/utils' | |
3 | 3 | const chartEditStore = useChartEditStore() |
4 | 4 | |
5 | 5 | // 导出 |
... | ... | @@ -24,9 +24,9 @@ export const exportHandle = () => { |
24 | 24 | } |
25 | 25 | |
26 | 26 | // 记录缩放比例 |
27 | - const scaleTemp = chartEditStore.getEditCanvas.scale | |
27 | + // const scaleTemp = chartEditStore.getEditCanvas.scale | |
28 | 28 | // 百分百展示页面 |
29 | - chartEditStore.setScale(1, true) | |
29 | + // chartEditStore.setScale(1, true) | |
30 | 30 | // 展示水印 |
31 | 31 | // THINGS_KIT 隐藏水印 |
32 | 32 | watermark.style.display = 'none' | ... | ... |