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,6 +24,7 @@ | ||
24 | class="rows" | 24 | class="rows" |
25 | :style="`height: ${h - (status.header.length ? status.mergedConfig.headerHeight : 0)}px;`" | 25 | :style="`height: ${h - (status.header.length ? status.mergedConfig.headerHeight : 0)}px;`" |
26 | > | 26 | > |
27 | + <div v-if="status.rows.length"> | ||
27 | <div | 28 | <div |
28 | class="row-item" | 29 | class="row-item" |
29 | v-for="(row, ri) in status.rows" | 30 | v-for="(row, ri) in status.rows" |
@@ -43,6 +44,10 @@ | @@ -43,6 +44,10 @@ | ||
43 | v-html="ceil" | 44 | v-html="ceil" |
44 | /> | 45 | /> |
45 | </div> | 46 | </div> |
47 | + </div> | ||
48 | + <div v-else class="nullData"> | ||
49 | + 暂无数据 | ||
50 | + </div> | ||
46 | </div> | 51 | </div> |
47 | </div> | 52 | </div> |
48 | </template> | 53 | </template> |
@@ -384,5 +389,14 @@ onUnmounted(() => { | @@ -384,5 +389,14 @@ onUnmounted(() => { | ||
384 | overflow: hidden; | 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 | </style> | 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 | +] |
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,22 +16,22 @@ | ||
16 | </template> | 16 | </template> |
17 | 17 | ||
18 | <script setup lang="ts"> | 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 | import VChart from 'vue-echarts' | 20 | import VChart from 'vue-echarts' |
21 | import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook' | 21 | import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook' |
22 | import { use } from 'echarts/core' | 22 | import { use } from 'echarts/core' |
23 | import { CanvasRenderer } from 'echarts/renderers' | 23 | import { CanvasRenderer } from 'echarts/renderers' |
24 | import { LineChart } from 'echarts/charts' | 24 | import { LineChart } from 'echarts/charts' |
25 | -import config, { includes,seriesItem } from './config' | 25 | +import config, { includes, seriesItem } from './config' |
26 | import { mergeTheme } from '@/packages/public/chart' | 26 | import { mergeTheme } from '@/packages/public/chart' |
27 | import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' | 27 | import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' |
28 | -import { useChartDataFetch } from '@/hooks' | 28 | +import { useChartDataFetch } from '@/hooks/external/useChartDataFetch.hook' |
29 | import { isPreview } from '@/utils' | 29 | import { isPreview } from '@/utils' |
30 | import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components' | 30 | import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components' |
31 | import dataJson from './data.json' | 31 | import dataJson from './data.json' |
32 | import { useFullScreen } from '../../utls/fullScreen' | 32 | import { useFullScreen } from '../../utls/fullScreen' |
33 | import { SocketReceiveMessageType } from '@/store/external/modules/socketStore.d' | 33 | import { SocketReceiveMessageType } from '@/store/external/modules/socketStore.d' |
34 | -import { useAssembleDataHooks } from '@/hooks/external/useAssembleData.hook' | 34 | +import { useAssembleDataHooks } from './useAssembleData.hook' |
35 | import isObject from 'lodash/isObject' | 35 | import isObject from 'lodash/isObject' |
36 | import cloneDeep from 'lodash/cloneDeep' | 36 | import cloneDeep from 'lodash/cloneDeep' |
37 | 37 | ||
@@ -205,25 +205,25 @@ watch( | @@ -205,25 +205,25 @@ watch( | ||
205 | //fix 修复v-chart图表绑定联动组件视图不更新问题 | 205 | //fix 修复v-chart图表绑定联动组件视图不更新问题 |
206 | const updateVChart = (newData: SocketReceiveMessageType) => { | 206 | const updateVChart = (newData: SocketReceiveMessageType) => { |
207 | if (!newData) return | 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 | const { data } = newData | 212 | const { data } = newData |
209 | - const {keys,record}= useAssembleDataHooks(data) | 213 | + const { keys, record } = useAssembleDataHooks(data,dimensions) |
210 | if (unref(realTimeList).length >= chartMaxDataPoint.value) { | 214 | if (unref(realTimeList).length >= chartMaxDataPoint.value) { |
211 | unref(realTimeList).splice(0, 1) | 215 | unref(realTimeList).splice(0, 1) |
212 | } | 216 | } |
213 | realTimeList.value.push(record) | 217 | realTimeList.value.push(record) |
214 | const dataset = { | 218 | const dataset = { |
215 | - dimensions: ['ts', ...keys], | ||
216 | - source: toRaw(unref(realTimeList)) | 219 | + dimensions: ['ts', ...keys], |
220 | + source: toRaw(unref(realTimeList)) | ||
217 | } | 221 | } |
218 | vChartRef.value?.setOption({ | 222 | vChartRef.value?.setOption({ |
219 | ...option.value, | 223 | ...option.value, |
220 | dataset | 224 | dataset |
221 | - }, | ||
222 | - { | ||
223 | - notMerge: true // 修复ws 属性对应属性值不匹配 | ||
224 | }) | 225 | }) |
225 | } | 226 | } |
226 | - | ||
227 | const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, newData => { | 227 | const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, newData => { |
228 | updateVChart(newData) | 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 | +] |
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,15 +11,40 @@ export const enum areaEnum { | ||
11 | CITY = 'CITY', //城市 | 11 | CITY = 'CITY', //城市 |
12 | COUNTY = 'COUNTY', //县 | 12 | COUNTY = 'COUNTY', //县 |
13 | COUNTRY = 'COUNTRY', //国家 | 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 | level: string | 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 | export interface dataPointI { | 49 | export interface dataPointI { |
25 | name: string | 50 | name: string |
@@ -50,7 +75,10 @@ export const option = { | @@ -50,7 +75,10 @@ export const option = { | ||
50 | adcode: 'china', | 75 | adcode: 'china', |
51 | showHainanIsLands: true, | 76 | showHainanIsLands: true, |
52 | saveSelect: { | 77 | saveSelect: { |
53 | - levelStr: areaEnum.COUNTRY | 78 | + levelStr: areaEnum.COUNTRY, |
79 | + cityValue:null, | ||
80 | + countyValue:null, | ||
81 | + provinceValue:'china' | ||
54 | } | 82 | } |
55 | }, | 83 | }, |
56 | tooltip: { | 84 | tooltip: { |
@@ -111,6 +139,9 @@ export const option = { | @@ -111,6 +139,9 @@ export const option = { | ||
111 | } | 139 | } |
112 | ] | 140 | ] |
113 | } | 141 | } |
142 | + | ||
143 | +export type optionType = typeof option | ||
144 | + | ||
114 | export const MapDefaultConfig = { ...option } | 145 | export const MapDefaultConfig = { ...option } |
115 | export default class Config extends PublicConfigClass implements CreateComponentType { | 146 | export default class Config extends PublicConfigClass implements CreateComponentType { |
116 | public key: string = AddThreeDimensionalMapConfig.key | 147 | public key: string = AddThreeDimensionalMapConfig.key |
@@ -139,6 +139,7 @@ import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/Ch | @@ -139,6 +139,7 @@ import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/Ch | ||
139 | import { GlobalThemeJsonType } from '@/settings/chartThemes/index' | 139 | import { GlobalThemeJsonType } from '@/settings/chartThemes/index' |
140 | import { GlobalSetting } from '@/components/Pages/ChartItemSetting' | 140 | import { GlobalSetting } from '@/components/Pages/ChartItemSetting' |
141 | import SelectCity from './components/SelectCity.vue' | 141 | import SelectCity from './components/SelectCity.vue' |
142 | +import { regionInfo } from './config' | ||
142 | 143 | ||
143 | const props = defineProps({ | 144 | const props = defineProps({ |
144 | optionData: { | 145 | optionData: { |
@@ -186,9 +187,9 @@ const symbolOption = ref([ | @@ -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 | const { cityValue, countyValue, provinceValue } = values | 193 | const { cityValue, countyValue, provinceValue } = values |
193 | props.optionData.mapRegion.saveSelect = values | 194 | props.optionData.mapRegion.saveSelect = values |
194 | props.optionData.mapRegion.adcode = countyValue | 195 | props.optionData.mapRegion.adcode = countyValue |
@@ -8,7 +8,7 @@ export const AddThreeDimensionalMapConfig: ConfigType = { | @@ -8,7 +8,7 @@ export const AddThreeDimensionalMapConfig: ConfigType = { | ||
8 | key, | 8 | key, |
9 | chartKey, | 9 | chartKey, |
10 | conKey, | 10 | conKey, |
11 | - title: '3d地图', | 11 | + title: '3D地图(支持下钻)', |
12 | category: ChatCategoryEnum.MAP, | 12 | category: ChatCategoryEnum.MAP, |
13 | categoryName: ChatCategoryEnumName.MAP, | 13 | categoryName: ChatCategoryEnumName.MAP, |
14 | package: PackagesCategoryEnum.CHARTS, | 14 | package: PackagesCategoryEnum.CHARTS, |
@@ -12,7 +12,7 @@ import { onMounted, ref, nextTick, PropType, toRefs, watch, reactive } from 'vue | @@ -12,7 +12,7 @@ import { onMounted, ref, nextTick, PropType, toRefs, watch, reactive } from 'vue | ||
12 | import * as echarts from 'echarts' | 12 | import * as echarts from 'echarts' |
13 | import { registerMap } from 'echarts/core' | 13 | import { registerMap } from 'echarts/core' |
14 | import 'echarts-gl' | 14 | import 'echarts-gl' |
15 | -import config, { areaEnum, dataPointI, HistoryParentType } from './config' | 15 | +import config, { areaEnum, dataPointI, optionType, historyParentType, backMapLevel } from './config' |
16 | import { getGeoJsonMap } from '@/api/external/common' | 16 | import { getGeoJsonMap } from '@/api/external/common' |
17 | import dataMaps from './data.json' | 17 | import dataMaps from './data.json' |
18 | 18 | ||
@@ -23,9 +23,7 @@ const props = defineProps({ | @@ -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 | const { w, h } = toRefs(props.chartConfig.attr) | 28 | const { w, h } = toRefs(props.chartConfig.attr) |
31 | 29 | ||
@@ -43,47 +41,51 @@ const toolBoxOption = ref({ | @@ -43,47 +41,51 @@ const toolBoxOption = ref({ | ||
43 | myFullButton: { | 41 | myFullButton: { |
44 | show: false, | 42 | show: false, |
45 | title: '返回', | 43 | title: '返回', |
46 | - icon: iconStr.value, | 44 | + icon: backIcon, |
47 | iconStyle: { | 45 | iconStyle: { |
48 | color: '' | 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 | watch( | 74 | watch( |
56 | () => props.chartConfig.option, | 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 | toolBoxOption.value.right = iconDistanceRight | 89 | toolBoxOption.value.right = iconDistanceRight |
88 | toolBoxOption.value.top = iconDistanceTop | 90 | toolBoxOption.value.top = iconDistanceTop |
89 | }, | 91 | }, |
@@ -92,26 +94,27 @@ watch( | @@ -92,26 +94,27 @@ watch( | ||
92 | } | 94 | } |
93 | ) | 95 | ) |
94 | 96 | ||
97 | +//追加echarts右上角自带toolbox | ||
95 | props.chartConfig.option = { | 98 | props.chartConfig.option = { |
96 | ...props.chartConfig.option, | 99 | ...props.chartConfig.option, |
97 | ...{ toolbox: toolBoxOption.value } | 100 | ...{ toolbox: toolBoxOption.value } |
98 | } | 101 | } |
99 | 102 | ||
100 | //地图点击返回 | 103 | //地图点击返回 |
101 | -const watchAdcode = async () => { | 104 | +const handleBack = async () => { |
102 | stopWatch() | 105 | stopWatch() |
103 | if (props.chartConfig.option.drillingIn) { | 106 | if (props.chartConfig.option.drillingIn) { |
104 | //如果是从右边配置里设置的,比如点击四川省,然后点击返回 | 107 | //如果是从右边配置里设置的,比如点击四川省,然后点击返回 |
105 | const savePopParent = saveHistoryParent.value.pop() | 108 | const savePopParent = saveHistoryParent.value.pop() |
106 | let saveAdcode = savePopParent?.adcode as string | number | 109 | let saveAdcode = savePopParent?.adcode as string | number |
107 | - saveLevelStr.level = savePopParent?.level | 110 | + saveLevelStr.level = savePopParent?.level as string |
108 | if (!savePopParent) { | 111 | if (!savePopParent) { |
109 | saveAdcode = getParentAdcode(props.chartConfig.option.mapRegion.adcode) | 112 | saveAdcode = getParentAdcode(props.chartConfig.option.mapRegion.adcode) |
110 | saveLevelStr.level = (regionMapParentArea as Recordable)[props.chartConfig.option.mapRegion.saveSelect.levelStr] | 113 | saveLevelStr.level = (regionMapParentArea as Recordable)[props.chartConfig.option.mapRegion.saveSelect.levelStr] |
111 | } | 114 | } |
112 | if (saveAdcode === 0) { | 115 | if (saveAdcode === 0) { |
113 | saveAdcode = 'china' | 116 | saveAdcode = 'china' |
114 | - saveLevelStr.level = 'COUNTRY' | 117 | + saveLevelStr.level = areaEnum.COUNTRY |
115 | } | 118 | } |
116 | const exist = await getGeojson(saveAdcode) | 119 | const exist = await getGeojson(saveAdcode) |
117 | const adcode = saveAdcode === 100000 ? 'china' : saveAdcode | 120 | const adcode = saveAdcode === 100000 ? 'china' : saveAdcode |
@@ -123,11 +126,12 @@ const watchAdcode = async () => { | @@ -123,11 +126,12 @@ const watchAdcode = async () => { | ||
123 | } | 126 | } |
124 | } | 127 | } |
125 | 128 | ||
129 | +//地区上级对应配置 | ||
126 | const regionMapParentArea = { | 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,8 +142,8 @@ const handleMap3DClick = async (params: Recordable) => { | ||
138 | if (item.properties.name === name) { | 142 | if (item.properties.name === name) { |
139 | const level = item.properties.level.toUpperCase() | 143 | const level = item.properties.level.toUpperCase() |
140 | const adcode = item.properties.adcode | 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 | props.chartConfig.option.mapRegion.adcode = adcode | 147 | props.chartConfig.option.mapRegion.adcode = adcode |
144 | props.chartConfig.option.saveClickRegion.level = level | 148 | props.chartConfig.option.saveClickRegion.level = level |
145 | saveLevelStr.level = level | 149 | saveLevelStr.level = level |
@@ -157,12 +161,12 @@ const saveGeojson: Recordable = ref({}) // 保存geojson | @@ -157,12 +161,12 @@ const saveGeojson: Recordable = ref({}) // 保存geojson | ||
157 | 161 | ||
158 | const chinaDefaultRegionId = ref(100000) //如果是china则adcode为100000 | 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 | level: '' | 166 | level: '' |
163 | }) | 167 | }) |
164 | 168 | ||
165 | -const saveHistoryParent = ref<HistoryParentType[]>([]) | 169 | +const saveHistoryParent = ref<historyParentType[]>([]) |
166 | 170 | ||
167 | //动态注册地图 | 171 | //动态注册地图 |
168 | const getGeojson = (regionId: number | string) => { | 172 | const getGeojson = (regionId: number | string) => { |
@@ -171,14 +175,14 @@ const getGeojson = (regionId: number | string) => { | @@ -171,14 +175,14 @@ const getGeojson = (regionId: number | string) => { | ||
171 | const { levelStr } = props.chartConfig.option.mapRegion.saveSelect //右侧配置项获取的行政级别 | 175 | const { levelStr } = props.chartConfig.option.mapRegion.saveSelect //右侧配置项获取的行政级别 |
172 | getGeoJsonMap( | 176 | getGeoJsonMap( |
173 | regionId === 'china' ? chinaDefaultRegionId.value : regionId, | 177 | regionId === 'china' ? chinaDefaultRegionId.value : regionId, |
174 | - !saveLevelStr.level ? levelStr : saveLevelStr.level | 178 | + !saveLevelStr.level ? levelStr : saveLevelStr.level //没有则获取右侧配置的行政级别 |
175 | ).then(res => { | 179 | ).then(res => { |
176 | const { geoJson, name, code, level } = res.data | 180 | const { geoJson, name, code, level } = res.data |
177 | const geoJsonFile = JSON.parse(geoJson) | 181 | const geoJsonFile = JSON.parse(geoJson) |
178 | if (!geoJsonFile) return | 182 | if (!geoJsonFile) return |
179 | - saveGeojson.value = geoJsonFile | 183 | + saveGeojson.value = geoJsonFile//保存一份服务端返回的geojson |
180 | const nameChina = name === '中国' ? 'china' : name | 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 | show.value = false | 186 | show.value = false |
183 | resolve(true) | 187 | resolve(true) |
184 | }) | 188 | }) |
@@ -191,6 +195,9 @@ const getGeojson = (regionId: number | string) => { | @@ -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 | //传adcode 获取上级 | 201 | //传adcode 获取上级 |
195 | const getParentAdcode = (adcode: number) => { | 202 | const getParentAdcode = (adcode: number) => { |
196 | let adcodeNum = 100000 | 203 | let adcodeNum = 100000 |
@@ -10,7 +10,14 @@ import AMapLoader from '@amap/amap-jsapi-loader' | @@ -10,7 +10,14 @@ import AMapLoader from '@amap/amap-jsapi-loader' | ||
10 | import { CreateComponentType } from '@/packages/index.d' | 10 | import { CreateComponentType } from '@/packages/index.d' |
11 | import { useChartDataFetch } from '@/hooks' | 11 | import { useChartDataFetch } from '@/hooks' |
12 | import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' | 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 | import { isArray } from '@/utils' | 21 | import { isArray } from '@/utils' |
15 | import inactive from './images/inactive.png' | 22 | import inactive from './images/inactive.png' |
16 | import online from './images/online.png' | 23 | import online from './images/online.png' |
@@ -29,7 +36,7 @@ const props = defineProps({ | @@ -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 | const { | 41 | const { |
35 | amapKey, | 42 | amapKey, |
@@ -76,7 +83,7 @@ const initMap = (newData: any) => { | @@ -76,7 +83,7 @@ const initMap = (newData: any) => { | ||
76 | viewMode: viewMode.value, // 地图模式 | 83 | viewMode: viewMode.value, // 地图模式 |
77 | willReadFrequently: true, | 84 | willReadFrequently: true, |
78 | WebGLParams: { | 85 | WebGLParams: { |
79 | - preserveDrawingBuffer: true, | 86 | + preserveDrawingBuffer: true |
80 | } | 87 | } |
81 | }) | 88 | }) |
82 | dataHandle(props.chartConfig.option.dataset) //处理地图标点 | 89 | dataHandle(props.chartConfig.option.dataset) //处理地图标点 |
@@ -99,19 +106,19 @@ const initMap = (newData: any) => { | @@ -99,19 +106,19 @@ const initMap = (newData: any) => { | ||
99 | }) | 106 | }) |
100 | } | 107 | } |
101 | 108 | ||
102 | - | ||
103 | //携带设备的tbDeviceId和别名或者名称 | 109 | //携带设备的tbDeviceId和别名或者名称 |
104 | const devicePartInfo = reactive<devicePartInfoInterface>({ | 110 | const devicePartInfo = reactive<devicePartInfoInterface>({ |
105 | tbDeviceId: '', | 111 | tbDeviceId: '', |
106 | name: '', | 112 | name: '', |
107 | alias: '', | 113 | alias: '', |
108 | - deviceProfileId: '', | 114 | + deviceProfileId: '' |
109 | }) | 115 | }) |
110 | 116 | ||
111 | //创建信息弹窗 | 117 | //创建信息弹窗 |
112 | const createInfoWindow = async (extraInfo: dataExtraInfoType) => { | 118 | const createInfoWindow = async (extraInfo: dataExtraInfoType) => { |
113 | try { | 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 | devicePartInfo.tbDeviceId = tbDeviceId | 122 | devicePartInfo.tbDeviceId = tbDeviceId |
116 | devicePartInfo.alias = alias | 123 | devicePartInfo.alias = alias |
117 | devicePartInfo.name = name | 124 | devicePartInfo.name = name |
@@ -140,30 +147,35 @@ const createInfoWindow = async (extraInfo: dataExtraInfoType) => { | @@ -140,30 +147,35 @@ const createInfoWindow = async (extraInfo: dataExtraInfoType) => { | ||
140 | <div style="display:flex;flex-direction:column;margin:3.5rem 5.5rem 2rem 6.5rem;position:relative;"> | 147 | <div style="display:flex;flex-direction:column;margin:3.5rem 5.5rem 2rem 6.5rem;position:relative;"> |
141 | <div style="display:flex;justify-content:space-between;align-items:center;color:white;"> | 148 | <div style="display:flex;justify-content:space-between;align-items:center;color:white;"> |
142 | <span style="${textOverflowFontBold}">${alias || name}</span> | 149 | <span style="${textOverflowFontBold}">${alias || name}</span> |
143 | - ${deviceState === 'INACTIVE' | ||
144 | - ? `<div style="${deviceStateContainer}"> | 150 | + ${ |
151 | + deviceState === 'INACTIVE' | ||
152 | + ? `<div style="${deviceStateContainer}"> | ||
145 | <img onclick="handleOpenDrawer()" title="最新数据" style="${deviceStateImg};cursor:pointer;" src="${listView}"/> | 153 | <img onclick="handleOpenDrawer()" title="最新数据" style="${deviceStateImg};cursor:pointer;" src="${listView}"/> |
146 | <img style="${deviceStateImg};margin-left:0.3rem" src="${inactive}"/> | 154 | <img style="${deviceStateImg};margin-left:0.3rem" src="${inactive}"/> |
147 | <span style="${deviceStateText}">待激活</span> | 155 | <span style="${deviceStateText}">待激活</span> |
148 | </div>` | 156 | </div>` |
149 | - : deviceState === 'ONLINE' | ||
150 | - ? `<div style="${deviceStateContainer}"> | 157 | + : deviceState === 'ONLINE' |
158 | + ? `<div style="${deviceStateContainer}"> | ||
151 | <img onclick="handleOpenDrawer()" title="最新数据" style="${deviceStateImg};cursor:pointer;" src="${listView}"/> | 159 | <img onclick="handleOpenDrawer()" title="最新数据" style="${deviceStateImg};cursor:pointer;" src="${listView}"/> |
152 | <img style="${deviceStateImg};margin-left:0.3rem" src="${online}"/> | 160 | <img style="${deviceStateImg};margin-left:0.3rem" src="${online}"/> |
153 | <span style="${deviceStateText}">在线</span> | 161 | <span style="${deviceStateText}">在线</span> |
154 | </div>` | 162 | </div>` |
155 | - : `<div style="${deviceStateContainer}"> | 163 | + : `<div style="${deviceStateContainer}"> |
156 | <img onclick="handleOpenDrawer()" title="最新数据" style="${deviceStateImg};cursor:pointer;" src="${listView}"/> | 164 | <img onclick="handleOpenDrawer()" title="最新数据" style="${deviceStateImg};cursor:pointer;" src="${listView}"/> |
157 | <img style="${deviceStateImg};margin-left:0.3rem" src="${offline}"/> | 165 | <img style="${deviceStateImg};margin-left:0.3rem" src="${offline}"/> |
158 | <span style="${deviceStateText}">离线</span> | 166 | <span style="${deviceStateText}">离线</span> |
159 | </div>` | 167 | </div>` |
160 | - } | 168 | + } |
161 | </div> | 169 | </div> |
162 | <div style="display:flex;flex-direction:column;justify-content:space-between;color:white;margin-top:1rem;gap:0.95rem;"> | 170 | <div style="display:flex;flex-direction:column;justify-content:space-between;color:white;margin-top:1rem;gap:0.95rem;"> |
163 | <div style="${textOverflow}">所属组织:${organizationDTO.name}</div> | 171 | <div style="${textOverflow}">所属组织:${organizationDTO.name}</div> |
164 | <div style="${textOverflow}">接入协议:${deviceProfile.transportType}</div> | 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 | </div> | 179 | </div> |
168 | </div> | 180 | </div> |
169 | </div> | 181 | </div> |
@@ -183,12 +195,12 @@ const handleOpenDrawer = async () => { | @@ -183,12 +195,12 @@ const handleOpenDrawer = async () => { | ||
183 | } | 195 | } |
184 | 196 | ||
185 | onMounted(() => { | 197 | onMounted(() => { |
186 | - (window as any).handleOpenDrawer = handleOpenDrawer; | ||
187 | -}); | 198 | + ;(window as any).handleOpenDrawer = handleOpenDrawer |
199 | +}) | ||
188 | 200 | ||
189 | onUnmounted(() => { | 201 | onUnmounted(() => { |
190 | - (window as any).handleOpenDrawer = null; | ||
191 | -}); | 202 | + ;(window as any).handleOpenDrawer = null |
203 | +}) | ||
192 | 204 | ||
193 | //地图鼠标hover | 205 | //地图鼠标hover |
194 | const mapClick = (markerInstance: any, markerItem: dataJsonMarkersType) => { | 206 | const mapClick = (markerInstance: any, markerItem: dataJsonMarkersType) => { |
@@ -225,7 +237,11 @@ const dataHandle = (newData: dataJsonType) => { | @@ -225,7 +237,11 @@ const dataHandle = (newData: dataJsonType) => { | ||
225 | const markerInstance = new AMapIns.Marker({ | 237 | const markerInstance = new AMapIns.Marker({ |
226 | position: [markerItem.position[0], markerItem.position[1]], | 238 | position: [markerItem.position[0], markerItem.position[1]], |
227 | offset: new AMapIns.Pixel(-13, 5), | 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 | // 原作者这种方式添加,属于JS API 1.4.8版本的 | 246 | // 原作者这种方式添加,属于JS API 1.4.8版本的 |
231 | markers.push(markerInstance) | 247 | markers.push(markerInstance) |
1 | <script lang="ts" setup name="SelectCity"> | 1 | <script lang="ts" setup name="SelectCity"> |
2 | import { onMounted, reactive } from 'vue' | 2 | import { onMounted, reactive } from 'vue' |
3 | import { getAreaList } from '@/api/external/common/index' | 3 | import { getAreaList } from '@/api/external/common/index' |
4 | -import { areaEnum } from '../config' | 4 | +import { areaEnum, regionInfo, regionOption } from '../config' |
5 | 5 | ||
6 | const props = defineProps({ | 6 | const props = defineProps({ |
7 | drillingIn: { | 7 | drillingIn: { |
@@ -15,35 +15,35 @@ const props = defineProps({ | @@ -15,35 +15,35 @@ const props = defineProps({ | ||
15 | 15 | ||
16 | const emits = defineEmits(['submit']) | 16 | const emits = defineEmits(['submit']) |
17 | 17 | ||
18 | -const selectOptions = reactive({ | 18 | +const selectOptions = reactive<regionOption>({ |
19 | provinceOptions: [], | 19 | provinceOptions: [], |
20 | cityOptions: [], | 20 | cityOptions: [], |
21 | countryOptions: [] | 21 | countryOptions: [] |
22 | }) | 22 | }) |
23 | 23 | ||
24 | -const selectValues = reactive({ | 24 | +const selectValues = reactive<regionInfo>({ |
25 | provinceValue: 'china', | 25 | provinceValue: 'china', |
26 | cityValue: null, | 26 | cityValue: null, |
27 | countyValue: null, | 27 | countyValue: null, |
28 | levelStr: areaEnum.COUNTRY | 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 | const resp = await getAreaList({ | 32 | const resp = await getAreaList({ |
33 | level, | 33 | level, |
34 | parentId | 34 | parentId |
35 | }) | 35 | }) |
36 | if (!resp) return [] | 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 | onMounted(async () => { | 40 | onMounted(async () => { |
41 | selectOptions.provinceOptions = await getAreaLists() | 41 | selectOptions.provinceOptions = await getAreaLists() |
42 | - ;(selectOptions.provinceOptions as never as any).unshift({ | 42 | + ;(selectOptions.provinceOptions).unshift({ |
43 | label: '中国', | 43 | label: '中国', |
44 | value: 'china' | 44 | value: 'china' |
45 | }) | 45 | }) |
46 | - onHandleSelectProvince(props.optionData?.mapRegion.saveSelect['provinceValue']) | 46 | + onHandleSelectProvince('china') |
47 | for (let i in selectValues) Reflect.set(selectValues, i, props.optionData?.mapRegion.saveSelect[i]) | 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,7 +51,7 @@ const onHandleSelectProvince = async (value: number | string) => { | ||
51 | selectValues.cityValue = null | 51 | selectValues.cityValue = null |
52 | selectValues.countyValue = null | 52 | selectValues.countyValue = null |
53 | if (value === 'china') return (selectValues.levelStr = areaEnum.COUNTRY) | 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 | selectValues.levelStr = areaEnum.PROVINCE | 55 | selectValues.levelStr = areaEnum.PROVINCE |
56 | } | 56 | } |
57 | 57 |
@@ -11,8 +11,40 @@ export const enum areaEnum { | @@ -11,8 +11,40 @@ export const enum areaEnum { | ||
11 | CITY = 'CITY', //城市 | 11 | CITY = 'CITY', //城市 |
12 | COUNTY = 'COUNTY', //县 | 12 | COUNTY = 'COUNTY', //县 |
13 | COUNTRY = 'COUNTRY', //国家 | 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 | export const includes = [] | 48 | export const includes = [] |
17 | 49 | ||
18 | export const option = { | 50 | export const option = { |
@@ -29,7 +61,10 @@ export const option = { | @@ -29,7 +61,10 @@ export const option = { | ||
29 | adcode: 'china', | 61 | adcode: 'china', |
30 | showHainanIsLands: true, | 62 | showHainanIsLands: true, |
31 | saveSelect: { | 63 | saveSelect: { |
32 | - levelStr: areaEnum.COUNTRY | 64 | + levelStr: areaEnum.COUNTRY, |
65 | + cityValue:null, | ||
66 | + countyValue:null, | ||
67 | + provinceValue:'china' | ||
33 | } | 68 | } |
34 | }, | 69 | }, |
35 | tooltip: { | 70 | tooltip: { |
@@ -187,6 +222,9 @@ export const option = { | @@ -187,6 +222,9 @@ export const option = { | ||
187 | } | 222 | } |
188 | ] | 223 | ] |
189 | } | 224 | } |
225 | + | ||
226 | +export type optionType = typeof option | ||
227 | + | ||
190 | export const MapDefaultConfig = { ...option } | 228 | export const MapDefaultConfig = { ...option } |
191 | export default class Config extends PublicConfigClass implements CreateComponentType { | 229 | export default class Config extends PublicConfigClass implements CreateComponentType { |
192 | public key: string = OverrideMapBaseConfig.key | 230 | public key: string = OverrideMapBaseConfig.key |
@@ -296,6 +296,14 @@ import { GlobalSetting } from '@/components/Pages/ChartItemSetting' | @@ -296,6 +296,14 @@ import { GlobalSetting } from '@/components/Pages/ChartItemSetting' | ||
296 | import { ref } from 'vue' | 296 | import { ref } from 'vue' |
297 | import mapChinaJson from './mapGeojson/china.json' | 297 | import mapChinaJson from './mapGeojson/china.json' |
298 | import SelectCity from './components/SelectCity.vue' | 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 | const mapRegionOptions = ref([ | 308 | const mapRegionOptions = ref([ |
301 | { | 309 | { |
@@ -315,17 +323,10 @@ const rippleEffectOptions = ref([ | @@ -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 | const initMapRegionOptions = () => { | 328 | const initMapRegionOptions = () => { |
328 | - mapChinaJson.features.forEach((element: any) => { | 329 | + mapChinaJson.features.forEach((element: Recordable) => { |
329 | if (element.properties.name) { | 330 | if (element.properties.name) { |
330 | mapRegionOptions.value.push({ ...element.properties }) | 331 | mapRegionOptions.value.push({ ...element.properties }) |
331 | } | 332 | } |
@@ -341,7 +342,7 @@ const mapRegion = computed(() => { | @@ -341,7 +342,7 @@ const mapRegion = computed(() => { | ||
341 | return props.optionData.mapRegion | 342 | return props.optionData.mapRegion |
342 | }) | 343 | }) |
343 | 344 | ||
344 | -const onHandleSelectValues = (values: any) => { | 345 | +const onHandleSelectValues = (values: regionInfo) => { |
345 | const { cityValue, countyValue, provinceValue } = values | 346 | const { cityValue, countyValue, provinceValue } = values |
346 | props.optionData.mapRegion.saveSelect = values | 347 | props.optionData.mapRegion.saveSelect = values |
347 | props.optionData.mapRegion.adcode = countyValue | 348 | props.optionData.mapRegion.adcode = countyValue |
@@ -354,6 +355,6 @@ const onHandleSelectValues = (values: any) => { | @@ -354,6 +355,6 @@ const onHandleSelectValues = (values: any) => { | ||
354 | } | 355 | } |
355 | 356 | ||
356 | const handleChangeDrillingIn = () => { | 357 | const handleChangeDrillingIn = () => { |
357 | - SelectCityRef.value?.resetValue() | 358 | + selectCityRef.value?.resetValue() |
358 | } | 359 | } |
359 | </script> | 360 | </script> |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | 15 | ||
16 | <script setup lang="ts"> | 16 | <script setup lang="ts"> |
17 | import { PropType, reactive, watch, ref, nextTick } from 'vue' | 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 | import VChart from 'vue-echarts' | 19 | import VChart from 'vue-echarts' |
20 | import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook' | 20 | import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook' |
21 | import { use, registerMap } from 'echarts/core' | 21 | import { use, registerMap } from 'echarts/core' |
@@ -60,9 +60,7 @@ use([ | @@ -60,9 +60,7 @@ use([ | ||
60 | 60 | ||
61 | const loading = ref(true) | 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 | const option = reactive({ | 65 | const option = reactive({ |
68 | value: mergeTheme(props.chartConfig.option, props.themeSetting, includes) | 66 | value: mergeTheme(props.chartConfig.option, props.themeSetting, includes) |
@@ -76,55 +74,60 @@ const toolBoxOption = ref({ | @@ -76,55 +74,60 @@ const toolBoxOption = ref({ | ||
76 | myFullButton: { | 74 | myFullButton: { |
77 | show: false, | 75 | show: false, |
78 | title: '返回', | 76 | title: '返回', |
79 | - icon: iconStr.value, | 77 | + icon: backIcon, |
80 | iconStyle: { | 78 | iconStyle: { |
81 | color: '' | 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 | watch( | 106 | watch( |
89 | () => props.chartConfig.option, | 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 | toolBoxOption.value.right = iconDistanceRight | 121 | toolBoxOption.value.right = iconDistanceRight |
121 | toolBoxOption.value.top = iconDistanceTop | 122 | toolBoxOption.value.top = iconDistanceTop |
122 | vEchartsSetOption() | 123 | vEchartsSetOption() |
123 | }, | 124 | }, |
124 | { | 125 | { |
125 | - deep: true, | 126 | + deep:true |
126 | } | 127 | } |
127 | ) | 128 | ) |
129 | + | ||
130 | +//追加echarts右上角自带toolbox | ||
128 | props.chartConfig.option = { | 131 | props.chartConfig.option = { |
129 | ...props.chartConfig.option, | 132 | ...props.chartConfig.option, |
130 | ...{ toolbox: toolBoxOption.value } | 133 | ...{ toolbox: toolBoxOption.value } |
@@ -132,25 +135,25 @@ props.chartConfig.option = { | @@ -132,25 +135,25 @@ props.chartConfig.option = { | ||
132 | 135 | ||
133 | 136 | ||
134 | //地图点击返回 adcode=100000,名字必须是china | 137 | //地图点击返回 adcode=100000,名字必须是china |
135 | -const watchAdcode = async () => { | 138 | +const handleBack = async () => { |
136 | if (props.chartConfig.option.drillingIn) { | 139 | if (props.chartConfig.option.drillingIn) { |
137 | //如果是从右边配置里设置的,比如点击四川省,然后点击返回 | 140 | //如果是从右边配置里设置的,比如点击四川省,然后点击返回 |
138 | const savePopParent = saveHistoryParent.value.pop() | 141 | const savePopParent = saveHistoryParent.value.pop() |
139 | - let saveAdcode: any = savePopParent?.adcode | 142 | + let saveAdcode = savePopParent?.adcode as string | number |
140 | saveLevelStr.level = savePopParent?.level as string | 143 | saveLevelStr.level = savePopParent?.level as string |
141 | if (!savePopParent) { | 144 | if (!savePopParent) { |
142 | saveAdcode = getParentAdcode(props.chartConfig.option.mapRegion.adcode) | 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 | if (saveAdcode === 0) { | 148 | if (saveAdcode === 0) { |
146 | saveAdcode = 'china' | 149 | saveAdcode = 'china' |
147 | - saveLevelStr.level = 'COUNTRY' | 150 | + saveLevelStr.level = areaEnum.COUNTRY |
148 | } | 151 | } |
149 | await getGeojson(saveAdcode) | 152 | await getGeojson(saveAdcode) |
150 | const adcode = saveAdcode === 100000 ? 'china' : saveAdcode | 153 | const adcode = saveAdcode === 100000 ? 'china' : saveAdcode |
151 | props.chartConfig.option.saveClickRegion.level = saveLevelStr.level | 154 | props.chartConfig.option.saveClickRegion.level = saveLevelStr.level |
152 | props.chartConfig.option.geo.map = adcode | 155 | props.chartConfig.option.geo.map = adcode |
153 | - props.chartConfig.option.series.forEach((item: any) => { | 156 | + props.chartConfig.option.series.forEach((item: Recordable) => { |
154 | if (item.type === 'map') item.map = adcode | 157 | if (item.type === 'map') item.map = adcode |
155 | }) | 158 | }) |
156 | vEchartsSetOption() | 159 | vEchartsSetOption() |
@@ -160,42 +163,36 @@ const watchAdcode = async () => { | @@ -160,42 +163,36 @@ const watchAdcode = async () => { | ||
160 | 163 | ||
161 | const vChartRef = ref<typeof VChart>() | 164 | const vChartRef = ref<typeof VChart>() |
162 | 165 | ||
163 | -const saveGeojson: any = ref({}) // 保存geojson | 166 | +const saveGeojson: Recordable = ref({}) //保存geojson |
164 | 167 | ||
165 | const chinaDefaultRegionId = ref(100000) //如果是china则adcode为100000 | 168 | const chinaDefaultRegionId = ref(100000) //如果是china则adcode为100000 |
166 | 169 | ||
167 | -const saveLevelStr = reactive({ | 170 | +const saveLevelStr = reactive<{level:historyParentType["level"]}>({ |
168 | // 地区级别 | 171 | // 地区级别 |
169 | level: '' | 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 | try { | 179 | try { |
183 | - const { levelStr } = props.chartConfig.option.mapRegion.saveSelect //右侧配置项获取的行政级别 | 180 | + const { levelStr } = props.chartConfig.option.mapRegion.saveSelect //从右侧配置项获取的行政级别 |
184 | const { data } = await getGeoJsonMap( | 181 | const { data } = await getGeoJsonMap( |
185 | regionId === 'china' ? chinaDefaultRegionId.value : regionId, | 182 | regionId === 'china' ? chinaDefaultRegionId.value : regionId, |
186 | - !saveLevelStr.level ? levelStr : saveLevelStr.level | 183 | + !saveLevelStr.level ? levelStr : saveLevelStr.level //没有则获取右侧配置的行政级别 |
187 | ) | 184 | ) |
188 | const { geoJson, name, code, level } = data | 185 | const { geoJson, name, code, level } = data |
189 | const geoJsonFile = JSON.parse(geoJson) | 186 | const geoJsonFile = JSON.parse(geoJson) |
190 | if (!geoJsonFile) return | 187 | if (!geoJsonFile) return |
191 | - saveGeojson.value = geoJsonFile | 188 | + saveGeojson.value = geoJsonFile//保存一份服务端返回的geojson |
192 | const nameChina = name === '中国' ? 'china' : name | 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 | loading.value = false | 191 | loading.value = false |
195 | } catch (error) { | 192 | } catch (error) { |
196 | loading.value = false | 193 | loading.value = false |
197 | console.error('动态注册地图出错', error) | 194 | console.error('动态注册地图出错', error) |
198 | - //注册出错则注册空的,不然在选择正确的adcode,则视图无法更新 | 195 | + //注册出错则注册空的,不然在选择正确的adcode,视图无法更新 |
199 | registerMap(props.chartConfig.option.mapRegion.adcode, { geoJSON: {} as any, specialAreas: {} }) | 196 | registerMap(props.chartConfig.option.mapRegion.adcode, { geoJSON: {} as any, specialAreas: {} }) |
200 | } | 197 | } |
201 | } | 198 | } |
@@ -227,11 +224,11 @@ const vEchartsSetOption = async () => { | @@ -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 | if (item.type === 'effectScatter' && dataset.point) item.data = dataset.point | 229 | if (item.type === 'effectScatter' && dataset.point) item.data = dataset.point |
233 | else if (item.type === 'lines' && dataset.line) { | 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 | return { | 232 | return { |
236 | ...it, | 233 | ...it, |
237 | lineStyle: { | 234 | lineStyle: { |
@@ -242,11 +239,10 @@ const dataSetHandle = async (dataset: any) => { | @@ -242,11 +239,10 @@ const dataSetHandle = async (dataset: any) => { | ||
242 | } else if (item.type === 'map' && dataset.map) item.data = dataset.map | 239 | } else if (item.type === 'map' && dataset.map) item.data = dataset.map |
243 | }) | 240 | }) |
244 | if (dataset.pieces) props.chartConfig.option.visualMap.pieces = dataset.pieces | 241 | if (dataset.pieces) props.chartConfig.option.visualMap.pieces = dataset.pieces |
245 | - | ||
246 | isPreview() && vEchartsSetOption() | 242 | isPreview() && vEchartsSetOption() |
247 | } | 243 | } |
248 | 244 | ||
249 | -// 处理海南群岛 | 245 | +// 单独处理海南群岛 |
250 | const hainanLandsHandle = async (newData: boolean) => { | 246 | const hainanLandsHandle = async (newData: boolean) => { |
251 | if (newData) { | 247 | if (newData) { |
252 | await getGeojson('china') | 248 | await getGeojson('china') |
@@ -275,7 +271,7 @@ watch( | @@ -275,7 +271,7 @@ watch( | ||
275 | await hainanLandsHandle(newData) | 271 | await hainanLandsHandle(newData) |
276 | vEchartsSetOption() | 272 | vEchartsSetOption() |
277 | } catch (error) { | 273 | } catch (error) { |
278 | - console.log(error) | 274 | + console.log('监听是否显示南海群岛出错',error) |
279 | } | 275 | } |
280 | }, | 276 | }, |
281 | { | 277 | { |
@@ -284,12 +280,11 @@ watch( | @@ -284,12 +280,11 @@ watch( | ||
284 | ) | 280 | ) |
285 | 281 | ||
286 | //处理数据标点包括飞线 | 282 | //处理数据标点包括飞线 |
287 | -const handleDataPoint = (newData: any) => { | 283 | +const handleDataPoint = (newData: string | number) => { |
288 | if (newData === 'china') { | 284 | if (newData === 'china') { |
289 | dataSetHandle(dataJson) | 285 | dataSetHandle(dataJson) |
290 | } else { | 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 | dataSetHandle({ | 288 | dataSetHandle({ |
294 | point: filterPoint, | 289 | point: filterPoint, |
295 | line: [] //由于点击单个地图模块,所以去除飞线 | 290 | line: [] //由于点击单个地图模块,所以去除飞线 |
@@ -304,13 +299,13 @@ watch( | @@ -304,13 +299,13 @@ watch( | ||
304 | try { | 299 | try { |
305 | await getGeojson(newData) | 300 | await getGeojson(newData) |
306 | props.chartConfig.option.geo.map = newData | 301 | props.chartConfig.option.geo.map = newData |
307 | - props.chartConfig.option.series.forEach((item: any) => { | 302 | + props.chartConfig.option.series.forEach((item: Recordable) => { |
308 | if (item.type === 'map') item.map = newData | 303 | if (item.type === 'map') item.map = newData |
309 | }) | 304 | }) |
310 | vEchartsSetOption() | 305 | vEchartsSetOption() |
311 | handleDataPoint(newData) | 306 | handleDataPoint(newData) |
312 | } catch (error) { | 307 | } catch (error) { |
313 | - console.log(error) | 308 | + console.log('监听地图展示区域发生变化出错',error) |
314 | } | 309 | } |
315 | }, | 310 | }, |
316 | { | 311 | { |
@@ -319,22 +314,23 @@ watch( | @@ -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 | dataSetHandle(newData) | 319 | dataSetHandle(newData) |
325 | }) | 320 | }) |
326 | 321 | ||
322 | +//地区上级对应配置 | ||
327 | const regionMapParentArea = { | 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 | const getParentAdcode = (adcode: number) => { | 331 | const getParentAdcode = (adcode: number) => { |
336 | let adcodeNum = 100000 | 332 | let adcodeNum = 100000 |
337 | - saveGeojson.value?.features.forEach((item: any) => { | 333 | + saveGeojson.value?.features.forEach((item: Recordable) => { |
338 | if (item.properties.adcode === adcode) { | 334 | if (item.properties.adcode === adcode) { |
339 | adcodeNum = item.properties.parent.adcode | 335 | adcodeNum = item.properties.parent.adcode |
340 | } | 336 | } |
@@ -343,22 +339,22 @@ const getParentAdcode = (adcode: number) => { | @@ -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 | if (props.chartConfig.option.drillingIn) { | 343 | if (props.chartConfig.option.drillingIn) { |
348 | const { name } = params | 344 | const { name } = params |
349 | - saveGeojson.value?.features.forEach((item: any) => { | 345 | + saveGeojson.value?.features.forEach((item: Recordable) => { |
350 | if (item.properties.name === name) { | 346 | if (item.properties.name === name) { |
351 | const level = item.properties.level.toUpperCase() | 347 | const level = item.properties.level.toUpperCase() |
352 | const adcode = item.properties.adcode | 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 | props.chartConfig.option.mapRegion.adcode = adcode | 351 | props.chartConfig.option.mapRegion.adcode = adcode |
356 | props.chartConfig.option.saveClickRegion.level = level | 352 | props.chartConfig.option.saveClickRegion.level = level |
357 | saveLevelStr.level = level | 353 | saveLevelStr.level = level |
358 | handleDataPoint(adcode) | 354 | handleDataPoint(adcode) |
359 | saveHistoryParent.value.push({ | 355 | saveHistoryParent.value.push({ |
360 | adcode: item.properties.parent.adcode, | 356 | adcode: item.properties.parent.adcode, |
361 | - level: (regionMapParentArea as any)[level] | 357 | + level: (regionMapParentArea as Recordable)[level] |
362 | }) | 358 | }) |
363 | } | 359 | } |
364 | }) | 360 | }) |
@@ -5,6 +5,9 @@ import cloneDeep from 'lodash/cloneDeep' | @@ -5,6 +5,9 @@ import cloneDeep from 'lodash/cloneDeep' | ||
5 | 5 | ||
6 | export const includes = [] | 6 | export const includes = [] |
7 | const option = { | 7 | const option = { |
8 | + tooltip: { | ||
9 | + formatter: '{c}' | ||
10 | + }, | ||
8 | backgroundColor: '#0E1327', | 11 | backgroundColor: '#0E1327', |
9 | dataset:70, | 12 | dataset:70, |
10 | series: [{ | 13 | series: [{ |
@@ -18,10 +18,12 @@ | @@ -18,10 +18,12 @@ | ||
18 | :dampingFactor="dampingFactor" | 18 | :dampingFactor="dampingFactor" |
19 | @process="onProcess" | 19 | @process="onProcess" |
20 | @load="onLoad" | 20 | @load="onLoad" |
21 | + @mousedown="handleMouseDown" | ||
21 | :position="position" | 22 | :position="position" |
22 | :rotation="rotation" | 23 | :rotation="rotation" |
23 | :lights="[lights[lightInput]]" | 24 | :lights="[lights[lightInput]]" |
24 | :showFps="showFps" | 25 | :showFps="showFps" |
26 | + :intersectRecursive="true" | ||
25 | :labels="labels" | 27 | :labels="labels" |
26 | /> | 28 | /> |
27 | <div v-show="show" class="process"> | 29 | <div v-show="show" class="process"> |
@@ -96,6 +98,11 @@ const { | @@ -96,6 +98,11 @@ const { | ||
96 | labels, | 98 | labels, |
97 | lightInput | 99 | lightInput |
98 | } = toRefs(props.chartConfig.option) as any | 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 | </script> | 106 | </script> |
100 | 107 | ||
101 | <style lang="scss" scoped> | 108 | <style lang="scss" scoped> |
@@ -19,7 +19,8 @@ export const option = { | @@ -19,7 +19,8 @@ export const option = { | ||
19 | buttonTextColor: 'white', | 19 | buttonTextColor: 'white', |
20 | buttonTextSize: '16', | 20 | buttonTextSize: '16', |
21 | buttonTextBold: 500, | 21 | buttonTextBold: 500, |
22 | - selectTargetItems: [] | 22 | + selectTargetItems: [], |
23 | + quaternary: false | ||
23 | } | 24 | } |
24 | 25 | ||
25 | export default class Config extends PublicConfigClass implements CreateComponentType { | 26 | export default class Config extends PublicConfigClass implements CreateComponentType { |
@@ -13,6 +13,11 @@ | @@ -13,6 +13,11 @@ | ||
13 | <n-switch v-model:value="optionData.buttonGhost" /> | 13 | <n-switch v-model:value="optionData.buttonGhost" /> |
14 | </setting-item> | 14 | </setting-item> |
15 | </setting-item-box> | 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 | <setting-item-box name="颜色"> | 21 | <setting-item-box name="颜色"> |
17 | <setting-item name=""> | 22 | <setting-item name=""> |
18 | <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.buttonColor"></n-color-picker> | 23 | <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.buttonColor"></n-color-picker> |
1 | <template> | 1 | <template> |
2 | <n-button | 2 | <n-button |
3 | + :quaternary="quaternary" | ||
3 | :style="{ width: `${w}px`, height: `${h}px` }" | 4 | :style="{ width: `${w}px`, height: `${h}px` }" |
4 | :type="buttonType" | 5 | :type="buttonType" |
5 | :dashed="buttonDashed" | 6 | :dashed="buttonDashed" |
@@ -29,7 +30,7 @@ const props = defineProps({ | @@ -29,7 +30,7 @@ const props = defineProps({ | ||
29 | 30 | ||
30 | const { w, h } = toRefs(props.chartConfig.attr) | 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 | props.chartConfig.option | 34 | props.chartConfig.option |
34 | ) | 35 | ) |
35 | 36 |
@@ -19,7 +19,8 @@ export const option = { | @@ -19,7 +19,8 @@ export const option = { | ||
19 | buttonTextColor: 'white', | 19 | buttonTextColor: 'white', |
20 | buttonTextSize: '16', | 20 | buttonTextSize: '16', |
21 | buttonTextBold: 500, | 21 | buttonTextBold: 500, |
22 | - selectTargetItems: [] | 22 | + selectTargetItems: [], |
23 | + quaternary: false | ||
23 | } | 24 | } |
24 | 25 | ||
25 | export default class Config extends PublicConfigClass implements CreateComponentType { | 26 | export default class Config extends PublicConfigClass implements CreateComponentType { |
@@ -13,6 +13,11 @@ | @@ -13,6 +13,11 @@ | ||
13 | <n-switch v-model:value="optionData.buttonGhost" /> | 13 | <n-switch v-model:value="optionData.buttonGhost" /> |
14 | </setting-item> | 14 | </setting-item> |
15 | </setting-item-box> | 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 | <setting-item-box name="颜色"> | 21 | <setting-item-box name="颜色"> |
17 | <setting-item name=""> | 22 | <setting-item name=""> |
18 | <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.buttonColor"></n-color-picker> | 23 | <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.buttonColor"></n-color-picker> |
1 | <template> | 1 | <template> |
2 | <n-button | 2 | <n-button |
3 | + :quaternary="quaternary" | ||
3 | :style="{ width: `${w}px`, height: `${h}px` }" | 4 | :style="{ width: `${w}px`, height: `${h}px` }" |
4 | :type="buttonType" | 5 | :type="buttonType" |
5 | :dashed="buttonDashed" | 6 | :dashed="buttonDashed" |
@@ -29,7 +30,7 @@ const props = defineProps({ | @@ -29,7 +30,7 @@ const props = defineProps({ | ||
29 | 30 | ||
30 | const { w, h } = toRefs(props.chartConfig.attr) | 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 | props.chartConfig.option | 34 | props.chartConfig.option |
34 | ) | 35 | ) |
35 | 36 |
@@ -70,17 +70,20 @@ const transitionDuration = computed(() => { | @@ -70,17 +70,20 @@ const transitionDuration = computed(() => { | ||
70 | 70 | ||
71 | // 预览更新 | 71 | // 预览更新 |
72 | useChartDataFetch(props.chartConfig, useChartEditStore, (newData: string) => { | 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 | option.dataset = newData | 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 | </script> | 88 | </script> |
86 | 89 |
@@ -57,17 +57,20 @@ watch( | @@ -57,17 +57,20 @@ watch( | ||
57 | 57 | ||
58 | // 预览更新 | 58 | // 预览更新 |
59 | useChartDataFetch(props.chartConfig, useChartEditStore, (newData: string) => { | 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 | option.dataset = newData | 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,17 +38,20 @@ watch( | ||
38 | ) | 38 | ) |
39 | 39 | ||
40 | useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => { | 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 | option.dataset = newData | 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 | </script> | 56 | </script> |
54 | 57 |
@@ -23,14 +23,17 @@ import { OverrideBarCommonConfig } from '@/packages/components/external/Charts/B | @@ -23,14 +23,17 @@ import { OverrideBarCommonConfig } from '@/packages/components/external/Charts/B | ||
23 | import { OverrideLineCommonConfig } from '@/packages/components/external/Charts/Lines/OverrideLineCommon' | 23 | import { OverrideLineCommonConfig } from '@/packages/components/external/Charts/Lines/OverrideLineCommon' |
24 | import { OverrideLineGradientsConfig } from '@/packages/components/external/Charts/Lines/OverrideLineGradients' | 24 | import { OverrideLineGradientsConfig } from '@/packages/components/external/Charts/Lines/OverrideLineGradients' |
25 | import { OverrideLineForDeviceHistoryQueryConfig } from '@/packages/components/external/Charts/Lines/OverrideLineForDeviceHistoryQuery' | 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 | import { OverrideProcessConfig } from '@/packages/components/external/Charts/Mores/OverrideProcess' | 28 | import { OverrideProcessConfig } from '@/packages/components/external/Charts/Mores/OverrideProcess' |
27 | import { OverridePieCircleConfig } from '@/packages/components/external/Charts/Pies/OverridePieCircle' | 29 | import { OverridePieCircleConfig } from '@/packages/components/external/Charts/Pies/OverridePieCircle' |
28 | import { OverrideMapBaseConfig } from '@/packages/components/external/Charts/Maps/OverrideMapBase' | 30 | import { OverrideMapBaseConfig } from '@/packages/components/external/Charts/Maps/OverrideMapBase' |
29 | import { OverrideMapAmapConfig } from '@/packages/components/external/Charts/Maps/OverrideMapAmap' | 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 | import { AddThreeDimensionalMapConfig } from '@/packages/components/external/Charts/Maps/AddThreeDimensionalMap' | 34 | import { AddThreeDimensionalMapConfig } from '@/packages/components/external/Charts/Maps/AddThreeDimensionalMap' |
31 | import { OverrideWaterPoloConfig } from '@/packages/components/external/Charts/Mores/OverrideWaterPolo' | 35 | import { OverrideWaterPoloConfig } from '@/packages/components/external/Charts/Mores/OverrideWaterPolo' |
32 | import { OverrideDialConfig } from '@/packages/components/external/Charts/Mores/OverrideDial' | 36 | import { OverrideDialConfig } from '@/packages/components/external/Charts/Mores/OverrideDial' |
33 | -import { OverrideLineRealTimeConfig } from '@/packages/components/external/Charts/Lines/OverrideLineRealTime' | ||
34 | import { PickIconConfig } from '@/packages/components/external/Decorates/Mores/PickIcon' | 37 | import { PickIconConfig } from '@/packages/components/external/Decorates/Mores/PickIcon' |
35 | import { WeatherConfig } from '@/packages/components/external/Decorates/Mores/Weather' | 38 | import { WeatherConfig } from '@/packages/components/external/Decorates/Mores/Weather' |
36 | import { ThreeDimensionalConfig } from '@/packages/components/external/Decorates/Three/ThreeDimensional' | 39 | import { ThreeDimensionalConfig } from '@/packages/components/external/Decorates/Three/ThreeDimensional' |
@@ -56,6 +59,8 @@ import { OverrideTableScrollBoardConfig } from '@/packages/components/external/T | @@ -56,6 +59,8 @@ import { OverrideTableScrollBoardConfig } from '@/packages/components/external/T | ||
56 | 59 | ||
57 | /** | 60 | /** |
58 | * 重写动态注入 | 61 | * 重写动态注入 |
62 | + * Override是重写的 | ||
63 | + * Add是新增的 | ||
59 | */ | 64 | */ |
60 | export function useInjectLib(packagesList: EPackagesType) { | 65 | export function useInjectLib(packagesList: EPackagesType) { |
61 | // packagesList[EPackagesCategoryEnum.COMPOSES] = ComposesList //此项目扩展侧边栏功能,保留待用 | 66 | // packagesList[EPackagesCategoryEnum.COMPOSES] = ComposesList //此项目扩展侧边栏功能,保留待用 |
@@ -108,10 +113,13 @@ export function useInjectLib(packagesList: EPackagesType) { | @@ -108,10 +113,13 @@ export function useInjectLib(packagesList: EPackagesType) { | ||
108 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideLineCommonConfig)//重写图表下的折线图 | 113 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideLineCommonConfig)//重写图表下的折线图 |
109 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideLineGradientsConfig)//重写图表下的渐变折线图 | 114 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideLineGradientsConfig)//重写图表下的渐变折线图 |
110 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideLineRealTimeConfig)//新增图表下的实时折线图 | 115 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideLineRealTimeConfig)//新增图表下的实时折线图 |
116 | + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, AddLinePlotConfig)//新增图表下的函数绘图 | ||
111 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideProcessConfig)//重写图表下的native ui | 117 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideProcessConfig)//重写图表下的native ui |
112 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverridePieCircleConfig)//重写图表下的饼图 | 118 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverridePieCircleConfig)//重写图表下的饼图 |
113 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideMapBaseConfig)//重写图表下的地图 | 119 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideMapBaseConfig)//重写图表下的地图 |
114 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideMapAmapConfig)//重写图表下的高德地图 | 120 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideMapAmapConfig)//重写图表下的高德地图 |
121 | + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, AddRealTimeTrajectoryAmapConfig)//新增图表下的设备实时轨迹地图 | ||
122 | + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, AddHistoryTimeTrajectoryAmapConfig)//新增图表下的设备历史轨迹地图 | ||
115 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, AddThreeDimensionalMapConfig)//新增图表下的3d echarts地图 | 123 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, AddThreeDimensionalMapConfig)//新增图表下的3d echarts地图 |
116 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideWaterPoloConfig)//重写图表下的水球图 | 124 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideWaterPoloConfig)//重写图表下的水球图 |
117 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideDialConfig)//重写图表下的表盘 | 125 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideDialConfig)//重写图表下的表盘 |
@@ -280,7 +280,9 @@ export const useSocketStore = defineStore({ | @@ -280,7 +280,9 @@ export const useSocketStore = defineStore({ | ||
280 | targetItem.groupList?.forEach((item: CreateComponentType) => { | 280 | targetItem.groupList?.forEach((item: CreateComponentType) => { |
281 | dumpSaveHistoryInput?.forEach((inputItem: { id: string, inputValue: string }) => { | 281 | dumpSaveHistoryInput?.forEach((inputItem: { id: string, inputValue: string }) => { |
282 | if (item.id === inputItem.id) { | 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,7 +33,7 @@ defineExpose({ | ||
33 | <template> | 33 | <template> |
34 | <NSpace> | 34 | <NSpace> |
35 | <NText>背景选择</NText> | 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 | :options="getAllBackgroundImageList" :render-label="renderOption" @update-value="handleUpdateValue" /> | 37 | :options="getAllBackgroundImageList" :render-label="renderOption" @update-value="handleUpdateValue" /> |
38 | </NSpace> | 38 | </NSpace> |
39 | </template> | 39 | </template> |
@@ -117,11 +117,14 @@ defineExpose({ | @@ -117,11 +117,14 @@ defineExpose({ | ||
117 | <ComponentConfiguration v-if="requestDataType === RequestDataTypeEnum.AJAX" ref="componentConfigurationEl" /> | 117 | <ComponentConfiguration v-if="requestDataType === RequestDataTypeEnum.AJAX" ref="componentConfigurationEl" /> |
118 | <PublicInterfaceForm v-if="requestDataType === RequestDataTypeEnum.Pond" ref="publicInterfaceFormEl" /> | 118 | <PublicInterfaceForm v-if="requestDataType === RequestDataTypeEnum.Pond" ref="publicInterfaceFormEl" /> |
119 | <template #action> | 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 | </div> | 128 | </div> |
126 | <NButton type="primary" @click="handleSaveAndRequest">保存 & 发送请求</NButton> | 129 | <NButton type="primary" @click="handleSaveAndRequest">保存 & 发送请求</NButton> |
127 | </NSpace> | 130 | </NSpace> |
@@ -5,11 +5,11 @@ | @@ -5,11 +5,11 @@ | ||
5 | <n-card size="small"> | 5 | <n-card size="small"> |
6 | <n-code word-wrap :code="toString(targetData.option.dataset)" language="json"></n-code> | 6 | <n-code word-wrap :code="toString(targetData.option.dataset)" language="json"></n-code> |
7 | </n-card> | 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 | <n-divider /> | 13 | <n-divider /> |
14 | <div v-for="(item, index) in groupList" :key="item.key + index"> | 14 | <div v-for="(item, index) in groupList" :key="item.key + index"> |
15 | <n-space justify="space-between"> | 15 | <n-space justify="space-between"> |
@@ -21,10 +21,16 @@ | @@ -21,10 +21,16 @@ | ||
21 | <n-input size="small" v-model:value="item.chartConfig.title" :disabled="true"></n-input> | 21 | <n-input size="small" v-model:value="item.chartConfig.title" :disabled="true"></n-input> |
22 | </n-space> | 22 | </n-space> |
23 | <n-space vertical justify="space-between"> | 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 | <n-input | 32 | <n-input |
26 | type="textarea" | 33 | type="textarea" |
27 | - @update:value="handleInput(groupList!, item.id, $event)" | ||
28 | size="small" | 34 | size="small" |
29 | :placeholder="String(item.option.dataset)" | 35 | :placeholder="String(item.option.dataset)" |
30 | ></n-input> | 36 | ></n-input> |
@@ -36,33 +42,39 @@ | @@ -36,33 +42,39 @@ | ||
36 | </template> | 42 | </template> |
37 | 43 | ||
38 | <script setup lang="ts"> | 44 | <script setup lang="ts"> |
39 | -import { toRefs, ref, watch } from 'vue' | 45 | +import { toRefs, ref, watch,computed } from 'vue' |
40 | import { useTargetData } from '../../hooks/useTargetData.hook' | 46 | import { useTargetData } from '../../hooks/useTargetData.hook' |
41 | import { toString } from '@/utils' | 47 | import { toString } from '@/utils' |
42 | import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d' | 48 | import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d' |
43 | import { datasetType, saveHistoryInputValueListType, historyInputValue } from './index.d' | 49 | import { datasetType, saveHistoryInputValueListType, historyInputValue } from './index.d' |
44 | -import { GlobalThemeJsonType } from '@/settings/chartThemes/index' | ||
45 | import { uniqBy } from 'lodash' | 50 | import { uniqBy } from 'lodash' |
51 | +import { SelectOption } from 'naive-ui' | ||
46 | 52 | ||
47 | const { targetData } = useTargetData() | 53 | const { targetData } = useTargetData() |
48 | 54 | ||
49 | const { groupList, option } = toRefs(targetData.value) | 55 | const { groupList, option } = toRefs(targetData.value) |
50 | 56 | ||
51 | -const testInputValue = ref('') | ||
52 | - | ||
53 | const saveHistoryInputValueList = ref<saveHistoryInputValueListType>([]) | 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 | saveHistoryInputValueList.value.unshift({ | 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 | try { | 76 | try { |
65 | - return Function('res', `return ${inputValue}`)(dataset) | 77 | + return Function('res', `return ${dataset[inputValue]}`)(dataset) |
66 | } catch (e) { | 78 | } catch (e) { |
67 | return inputValue | 79 | return inputValue |
68 | } | 80 | } |
@@ -82,14 +94,26 @@ const handleGroupListById = ( | @@ -82,14 +94,26 @@ const handleGroupListById = ( | ||
82 | //原生for循环优化性能 | 94 | //原生for循环优化性能 |
83 | for (let index = 0, total = groupList.length; index < total; index++) { | 95 | for (let index = 0, total = groupList.length; index < total; index++) { |
84 | const targetItem = groupList[index] | 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 | watch( | 118 | watch( |
95 | () => targetData.value, | 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 | import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' | 1 | import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' |
2 | -import { canvasCut, downloadTextFile, JSONStringify } from '@/utils' | 2 | +import { downloadTextFile, JSONStringify } from '@/utils' |
3 | const chartEditStore = useChartEditStore() | 3 | const chartEditStore = useChartEditStore() |
4 | 4 | ||
5 | // 导出 | 5 | // 导出 |
@@ -24,9 +24,9 @@ export const exportHandle = () => { | @@ -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 | // THINGS_KIT 隐藏水印 | 31 | // THINGS_KIT 隐藏水印 |
32 | watermark.style.display = 'none' | 32 | watermark.style.display = 'none' |