Commit c8eb67f749aa9fdf67cd86d01a20c01be91ef8c3

Authored by fengwotao
1 parent 88906aca

feat(src/packages): 图表高德地图新增右上角弹窗筛选

... ... @@ -10,7 +10,9 @@ enum Api {
10 10 CONFIGURATION = '/configuration/center',
11 11 CONFIGURATION_SHARE = '/configuration/center/share/',
12 12 BASEORIGINATION = '/organization/me/list/',
13   - VIDEO = '/video/list'
  13 + VIDEO = '/video/list',
  14 + DEVICE_PROFILE = '/device_profile/me/list',
  15 + DEVICE = '/device'
14 16 }
15 17
16 18 export const getDictItemByCode = (value: string) => {
... ... @@ -77,8 +79,23 @@ export const getOrganizationList = (params?: OrganizationListItem) =>
77 79 })
78 80
79 81 //获取视频列表
80   -export const getVideoList = (params?:object) =>
  82 +export const getVideoList = (params?: object) =>
81 83 defHttp.get({
82 84 url: Api.VIDEO,
83 85 params
84 86 })
  87 +
  88 +//获取产品列表
  89 +export const getProfileList = (params?: object) =>
  90 + defHttp.get({
  91 + url: Api.DEVICE_PROFILE,
  92 + params
  93 + })
  94 +
  95 +//获取设备列表
  96 +export const getDeviceList = (params: any, data?: object) =>
  97 + defHttp.post({
  98 + url: Api.DEVICE,
  99 + params,
  100 + data
  101 + })
... ...
  1 +<template>
  2 + <n-drawer display-directive="if" :show="modelShow" :width="502" :placement="placement">
  3 + <n-drawer-content title="设备筛选">
  4 + <n-space vertical>
  5 + <span>组织</span>
  6 + <n-tree-select
  7 + placement="top-start"
  8 + label-field="name"
  9 + v-model:value="searchParams.organizationId"
  10 + key-field="id"
  11 + children-field="children"
  12 + :options="originationOption"
  13 + />
  14 + <span>产品</span>
  15 + <n-select v-model:value="searchParams.deviceProfileIds" :options="deviceProfileOption" />
  16 + <span>设备</span>
  17 + <n-input v-model:value="searchParams.name" type="text" placeholder="请输入设备名称" />
  18 + <span>设备状态</span>
  19 + <n-radio-group v-model:value="searchParams.deviceState" name="radiogroup">
  20 + <n-space>
  21 + <n-radio v-for="(item, index) in deviceStateGroup" :key="index" :value="item.value">
  22 + {{ item.label }}
  23 + </n-radio>
  24 + </n-space>
  25 + </n-radio-group>
  26 + <span>是否告警</span>
  27 + <n-radio-group v-model:value="searchParams.alarmStatus" name="radiogroup">
  28 + <n-space>
  29 + <n-radio v-for="(item, index) in alarmStatusGroup" :key="index" :value="item.value">
  30 + {{ item.label }}
  31 + </n-radio>
  32 + </n-space>
  33 + </n-radio-group>
  34 + <span>配置查询分页</span>
  35 + <n-space justify="space-between">
  36 + <n-input-number :min="1" v-model:value="searchPage.page" clearable />
  37 + <n-input-number :step="10" :min="10" v-model:value="searchPage.pageSize" clearable />
  38 + </n-space>
  39 + </n-space>
  40 + <template #footer>
  41 + <n-button @click="handleCancel" type="tertiary">取消</n-button>
  42 + <div style="visibility: hidden; width: 10px">占位</div>
  43 + <n-button @click="handleSubmit" type="success">确定</n-button>
  44 + </template>
  45 + </n-drawer-content>
  46 + </n-drawer>
  47 +</template>
  48 +
  49 +<script lang="ts" setup>
  50 +import { ref, onMounted, reactive } from 'vue'
  51 +import { NTreeSelect } from 'naive-ui'
  52 +import { getOrganizationList, getProfileList } from '@/api/external/common/index'
  53 +
  54 +interface searchParamsInterface {
  55 + organizationId: string | null
  56 + deviceProfileIds: null
  57 + name: string
  58 + deviceState: string | null
  59 + alarmStatus: number
  60 +}
  61 +
  62 +defineProps({
  63 + modelShow: Boolean
  64 +})
  65 +
  66 +const emit = defineEmits(['searchParams', 'closeDrawer'])
  67 +
  68 +const placement = ref('right')
  69 +
  70 +//设备状态
  71 +const deviceStateGroup = [
  72 + {
  73 + label: '全部',
  74 + value: null
  75 + },
  76 + {
  77 + label: '待激活',
  78 + value: 'INACTIVE'
  79 + },
  80 + {
  81 + label: '在线',
  82 + value: 'ONLINE'
  83 + },
  84 + {
  85 + label: '离线',
  86 + value: 'OFFLINE'
  87 + }
  88 +]
  89 +
  90 +//告警状态
  91 +const alarmStatusGroup = [
  92 + {
  93 + label: '是',
  94 + value: 1
  95 + },
  96 + {
  97 + label: '否',
  98 + value: 0
  99 + }
  100 +]
  101 +
  102 +const searchPage = reactive<{ page: number; pageSize: number }>({
  103 + page: 1,
  104 + pageSize: 10
  105 +})
  106 +
  107 +const searchParams = reactive<searchParamsInterface>({
  108 + organizationId: null,
  109 + deviceProfileIds: null,
  110 + name: '',
  111 + deviceState: null,
  112 + alarmStatus: 0
  113 +})
  114 +
  115 +const originationOption = ref([])
  116 +
  117 +const deviceProfileOption = ref([])
  118 +
  119 +const loadList = async () => {
  120 + const resOrganization = await getOrganizationList()
  121 + const resProfileList = await getProfileList()
  122 + Promise.all([resOrganization, resProfileList]).then(res => {
  123 + originationOption.value = res[0]
  124 + deviceProfileOption.value = res[1].map((item: { name: string; tbProfileId: string }) => ({
  125 + label: item.name,
  126 + value: item.tbProfileId
  127 + }))
  128 + })
  129 +}
  130 +
  131 +const handleSubmit = () => {
  132 + searchParams.deviceProfileIds = [searchParams.deviceProfileIds] as any
  133 + emit('searchParams', searchPage, searchParams)
  134 + handleCancel()
  135 +}
  136 +
  137 +const handleCancel = () => {
  138 + for (let i in searchParams) Reflect.set(searchParams, i, null)
  139 + searchParams.name = ''
  140 + searchParams.alarmStatus = 0
  141 + searchPage.page = 1
  142 + searchPage.pageSize = 10
  143 + emit('closeDrawer')
  144 +}
  145 +
  146 +onMounted(() => {
  147 + loadList()
  148 +})
  149 +</script>
  150 +
  151 +<style lang="scss" scoped></style>
... ...
... ... @@ -11,6 +11,30 @@ export type dataJsonType = typeof dataJson //data.json类型
11 11
12 12 export type dataJsonMarkersType = typeof dataJson.markers[number] //data.json markers类型
13 13
  14 +//标注数据格式
  15 +export const fileterDevice = (items: any) => {
  16 + const values = items.reduce((acc: any, curr: any) => {
  17 + acc.push({
  18 + name: curr.alias,
  19 + value: 10,
  20 + position: [curr.deviceInfo.longitude, curr.deviceInfo.latitude],
  21 + extraInfo: {
  22 + tbDeviceId: curr.tbDeviceId,
  23 + name: curr.name,
  24 + alias: curr.alias,
  25 + organizationDTO: curr.organizationDTO,
  26 + deviceState: curr.deviceState,
  27 + deviceProfile: curr.deviceProfile,
  28 + deviceInfo: curr.deviceInfo
  29 + }
  30 + })
  31 + return [...acc]
  32 + }, [])
  33 + return {
  34 + markers: values
  35 + }
  36 +}
  37 +
14 38 export enum ThemeEnum {
15 39 NORMAL = 'normal',
16 40 DARK = 'dark',
... ...
1 1 <template>
2   - <div ref="vChartRef"></div>
  2 + <div @mouseenter="handleMouseenter" @mouseleave="handleMouseleave" class="chart-amap" ref="vChartRef">
  3 + <div v-if="showSearchBox" @click.stop="handleOpenSearchBox" class="search-box"></div>
  4 + <search-box :modelShow="modelShow" @searchParams="handleSearchParams" @closeDrawer="handleCloseDrawer"></search-box>
  5 + </div>
3 6 </template>
4 7
5 8 <script setup lang="ts">
... ... @@ -8,13 +11,14 @@ import AMapLoader from '@amap/amap-jsapi-loader'
8 11 import { CreateComponentType } from '@/packages/index.d'
9 12 import { useChartDataFetch } from '@/hooks'
10 13 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
11   -import { MarkerEnum, ThemeEnum, dataExtraInfoType, dataJsonType, dataJsonMarkersType } from './config'
  14 +import { MarkerEnum, ThemeEnum, dataExtraInfoType, dataJsonType, dataJsonMarkersType, fileterDevice } from './config'
12 15 import { isArray } from '@/utils'
13 16 import djh from './images/djh.png'
14 17 import online from './images/online.png'
15 18 import lx1 from './images/lx1.png'
16   -import { getDeviceActiveTime } from '@/api/external/common/index'
  19 +import { getDeviceActiveTime, getDeviceList } from '@/api/external/common/index'
17 20 import dayjs from 'dayjs'
  21 +import SearchBox from './components/SearchBox.vue'
18 22
19 23 const props = defineProps({
20 24 chartConfig: {
... ... @@ -23,6 +27,10 @@ const props = defineProps({
23 27 }
24 28 })
25 29
  30 +const modelShow = ref(false)
  31 +
  32 +const showSearchBox = ref(false)
  33 +
26 34 let {
27 35 amapKey,
28 36 amapStyleKey,
... ... @@ -126,6 +134,25 @@ const createInfoWindow = async (extraInfo: dataExtraInfoType) => {
126 134 }
127 135 }
128 136
  137 +const handleMouseenter = () => (showSearchBox.value = true)
  138 +
  139 +const handleMouseleave = () => (showSearchBox.value = false)
  140 +
  141 +const handleOpenSearchBox = () => (modelShow.value = true)
  142 +
  143 +const handleCloseDrawer = () => (modelShow.value = false)
  144 +
  145 +const handleSearchParams = async (searchPage: any, params: any) => {
  146 + try {
  147 + const { items } = await getDeviceList(searchPage, params)
  148 + const values = fileterDevice(items)
  149 + if (!values) return
  150 + dataHandle(values)
  151 + } finally {
  152 + handleCloseDrawer()
  153 + }
  154 +}
  155 +
129 156 //地图点击
130 157 const mapClick = (markerInstance: any, markerItem: dataJsonMarkersType) => {
131 158 markerInstance.setExtData({
... ... @@ -178,7 +205,7 @@ const dataHandle = (newData: dataJsonType) => {
178 205 // markers.push(markerInstance) //原作者这种方式添加,属于JS API 1.4.8版本的
179 206 // markerInstance.setMap(mapIns)
180 207 mapIns.add(markerInstance)
181   - mapClick(markerInstance, markerItem)
  208 + mapClick(markerInstance, markerItem) // circle点击无效
182 209 })
183 210 }
184 211 }
... ... @@ -215,3 +242,20 @@ useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
215 242 dataHandle(newData)
216 243 })
217 244 </script>
  245 +
  246 +<style lang="scss" scoped>
  247 +.chart-amap {
  248 + position: relative;
  249 + .search-box {
  250 + cursor: pointer;
  251 + position: absolute;
  252 + right: -25px;
  253 + top: 15px;
  254 + z-index: 9999;
  255 + width: 50px;
  256 + height: 50px;
  257 + border-radius: 50%;
  258 + background-color: rgba(255, 255, 255, 0.2);
  259 + }
  260 +}
  261 +</style>
... ...