Commit 1efc9c2fecc6681028fc88537b953e7aaabf43ea

Authored by xp.Huang
1 parent d7c8f39d

feat:函数绘图、历史轨迹、实时轨迹组件及相关代码优化bug修复

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