Forráskód Böngészése

feat: 设备地图增加产品的筛选功能

yanglzh 9 hónapja
szülő
commit
09dcec0d81

+ 0 - 105
src/views/iot/property/deviceMap/edit.vue

@@ -1,105 +0,0 @@
-<template>
-  <el-dialog class="api-edit" v-model="showDialog" :title="`${formData.id ? '编辑资产关系' : '新增资产关系'}`" width="800px" :close-on-click-modal="false" :close-on-press-escape="false">
-    <div class="dialog-wrapper" v-if="showDialog">
-      <el-form class="inline-form" ref="formRef" :model="formData" :rules="ruleForm" label-width="auto">
-        <el-form-item label="选择角色" prop="roleId">
-          <el-cascader :options="roleData" :props="{
-            checkStrictly: true, multiple: false, emitPath: false, value: 'id', label: 'name'
-          }" placeholder="请选择角色" clearable class="w100" disabled v-model="formData.roleId">
-            <template #default="{ node, data }">
-              <span>{{ data.name }}</span>
-              <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
-            </template>
-          </el-cascader>
-        </el-form-item>
-        <el-form-item label="已选设备" prop="assetIds">
-          <el-tag v-for="name in assetName" class="mr-2">{{ name }} </el-tag>
-        </el-form-item>
-      </el-form>
-      <el-table :data="tableData" style="width: 100%" row-key="id" @selection-change="handleSelectionChange" v-loading="loading">
-        <el-table-column type="selection" reserve-selection width="55" align="center" />
-        <el-table-column prop="deviceName" label="设备名称" min-width="100" show-overflow-tooltip></el-table-column>
-        <el-table-column prop="deviceKey" label="设备KEY" show-overflow-tooltip></el-table-column>
-        <el-table-column prop="deviceNumber" label="设备编码" show-overflow-tooltip></el-table-column>
-        <el-table-column prop="deviceCategory" label="设备类型" show-overflow-tooltip></el-table-column>
-        <el-table-column prop="installTime" label="安装时间" width="160" align="center"></el-table-column>
-      </el-table>
-      <pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize" @pagination="getList()" />
-    </div>
-    <template #footer>
-      <div class="dialog-footer">
-        <el-button @click="showDialog = false">取消</el-button>
-        <el-button type="primary" @click="onSubmit">确定</el-button>
-      </div>
-    </template>
-  </el-dialog>
-</template>
-
-<script lang="ts" setup>
-import { ref, reactive, nextTick } from 'vue';
-import api from '/@/api/device'
-import system from '/@/api/system';
-import { useSearch } from '/@/hooks/useCommon'
-import { ruleRequired } from '/@/utils/validator';
-import { ElMessage } from 'element-plus';
-import { deepClone } from '/@/utils/other';
-
-const emit = defineEmits(['getList']);
-
-const showDialog = ref(false);
-const formRef = ref();
-const roleData = ref([]);
-const assetName = ref<string[]>([]);
-const baseForm = {
-  id: undefined,
-  roleId: '',
-  assetIds: [],
-};
-
-const formData = reactive<any>(deepClone(baseForm));
-
-const ruleForm = {
-  roleId: [ruleRequired('角色不能为空')],
-  assetIds: [ruleRequired('设备不能为空')],
-};
-
-const { params, tableData, getList, loading } = useSearch<any[]>(api.dev_asset.getList, 'Data', { pageSize: 10 })
-getList()
-
-const handleSelectionChange = (selection: any[]) => {
-  formData.assetIds = selection.map((item) => item.id);
-  assetName.value = selection.map((item) => item.deviceName);
-};
-
-const onSubmit = async () => {
-  await formRef.value.validate();
-  await api.assetRelationship.bind(formData);
-  ElMessage.success('操作成功');
-  resetForm();
-  showDialog.value = false;
-  emit('getList');
-};
-
-const resetForm = async () => {
-  Object.assign(formData, deepClone(baseForm));
-  assetName.value = []
-  formRef.value && formRef.value.resetFields();
-};
-
-const open = async (roleId: string) => {
-  resetForm();
-  showDialog.value = true;
-  formData.roleId = roleId;
-  //获取所有的角色
-  system.role.getList({ status: 1 }).then((resp: any) => {
-    roleData.value = resp;
-  })
-};
-
-defineExpose({ open });
-</script>
-<style scoped lang="scss">
-.demo-form-inline .el-input {
-  --el-input-width: 320px;
-}
-</style>

+ 0 - 118
src/views/iot/property/deviceMap/filter.vue

@@ -1,118 +0,0 @@
-<template>
-  <el-dialog class="api-edit" v-model="showDialog" title="设置筛选条件" width="800px" :close-on-click-modal="false" :close-on-press-escape="false">
-    <el-form class="inline-form" ref="formRef" label-width="auto">
-      <el-form-item v-for="(item, i) in ruleList" :key="item.id" :label="item.title" :prop="item.name">
-        <template #label>
-          <el-checkbox v-model="item.isCheck">{{ item.title }}</el-checkbox>
-        </template>
-        <el-input v-if="item.types === 'input'" v-model="item.value" :disabled="!item.isCheck" placeholder="请输入" />
-        <el-input v-else-if="item.types === 'textarea'" v-model="item.value" :disabled="!item.isCheck" type="textarea" placeholder="请输入" />
-        <el-date-picker v-else-if="item.types === 'date'" v-model="item.value" :disabled="!item.isCheck" type="date" value-format="YYYY-MM-DD" placeholder="请选择时间" class="w100" clearable />
-      </el-form-item>
-      <el-form-item v-if="ruleList.length % 2 === 1"> </el-form-item>
-    </el-form>
-    <template #footer>
-      <div class="dialog-footer">
-        <el-button @click="showDialog = false">取消</el-button>
-        <el-button type="primary" @click="onSubmit">确定</el-button>
-      </div>
-    </template>
-  </el-dialog>
-</template>
-
-<script lang="ts" setup>
-import { ref, reactive, nextTick } from 'vue';
-import api from '/@/api/device'
-
-import { ElMessage } from 'element-plus';
-
-const emit = defineEmits(['getList']);
-const showDialog = ref(false);
-const formRef = ref();
-const ruleList = ref([]);
-const baseForm = {
-  id: undefined,
-};
-
-const formData = reactive({
-  ...baseForm,
-});
-
-const onSubmit = async () => {
-  const hasNull = ruleList.value.find((item: any) => {
-    const isNull = item.isCheck && !item.value
-    if (isNull) {
-      ElMessage.closeAll()
-      ElMessage.warning(`${item.title}不能为空`)
-    }
-    return isNull
-  })
-
-  if (hasNull) return
-
-  const ruleInfo = ruleList.value.map((item: any) => {
-    item.isCheck = item.isCheck ? 1 : 0
-    delete item.title
-    return item
-  })
-
-  await api.assetRelationship.editRuleInfo({ id: formData.id, ruleInfo });
-  ElMessage.success('操作成功');
-  resetForm();
-  showDialog.value = false;
-  emit('getList');
-};
-
-const resetForm = async () => {
-  Object.assign(formData, { ...baseForm });
-  ruleList.value = []
-  formRef.value && formRef.value.resetFields();
-};
-
-const open = async (row: any) => {
-  resetForm();
-  showDialog.value = true;
-  formData.id = row.id
-  const productKey = row.assetInfo.productKey
-
-  const ruleInfo = JSON.parse(row.ruleInfo || '[]')
-
-  //获取属性列表
-  api.dev_asset_metadata.detail({ productKey }).then((res: any) => {
-    const list = res.map((row: any) => {
-
-      const hasFindItem = ruleInfo.find((item2: any) => item2.id === row.id)
-      const item = {
-        id: row.id,
-        name: row.name,
-        title: row.title,
-        types: row.types,
-        value: hasFindItem ? hasFindItem.value : null,
-        isCheck: hasFindItem?.isCheck ? true : false,
-      }
-      return item
-    })
-    ruleList.value = list
-  })
-};
-
-defineExpose({ open });
-</script>
-<style scoped lang="scss">
-.demo-form-inline .el-input {
-  --el-input-width: 320px;
-}
-
-.el-form {
-  display: flex;
-  flex-flow: row wrap;
-  gap: 20px;
-  align-items: flex-start;
-
-  .el-form-item {
-    flex: 1;
-    min-width: 40%;
-    margin-bottom: 0;
-  }
-}
-</style>

+ 113 - 72
src/views/iot/property/deviceMap/index.vue

@@ -1,17 +1,37 @@
 <template>
 	<div class="page page-full">
+		<el-select
+			v-model="productKey"
+			style="position: absolute; top: 20px; left: 20px; width: 200px; z-index: 1000"
+			filterable
+			clearable
+			:loading="loading"
+			placeholder="选择产品"
+			@change="handleProductChange"
+		>
+			<el-option v-for="item in productData" :key="item.key" :label="item.name" :value="item.key" value-key="id"> </el-option>
+		</el-select>
 		<div class="map" id="device-map"></div>
 	</div>
 </template>
 
 <script lang="ts" setup>
 import { initMap, MapStyleJson } from '/@/utils/map'
-import { onMounted } from 'vue'
+import { onMounted, ref } from 'vue'
 import api from '/@/api/device'
+import { ElMessage } from 'element-plus'
 
 let BMapGL: any = null
 let map: any = null
 
+const productKey = ref('')
+const productData = ref<any[]>([])
+const loading = ref(false)
+
+api.product.getLists().then((res: any) => {
+	productData.value = res.product
+})
+
 onMounted(async () => {
 	const { BMapGL: theBMapGL } = await initMap()
 
@@ -32,85 +52,106 @@ onMounted(async () => {
 	})
 })
 
+// 监听产品变化
+const handleProductChange = () => {
+	setMarker()
+}
+
 async function setMarker() {
-	const { device: list } = await api.device.allList({})
+	console.log('setMarker')
+	loading.value = true
+	try {
+		const { device } = await api.device.allList({ productKey: productKey.value })
 
-	const points: any[] = []
-	const markers: any[] = []
+		// 清除现有标记
+		map.clearOverlays()
 
-	list.map((row: any) => {
-		if (!row.lat || !row.lng) return
+		const list = (device || []).filter((row: any) => row.lat && row.lng)
 
-		const point = new BMapGL.Point(row.lng, row.lat)
-		const marker = new BMapGL.Marker(point, {
-			icon: new BMapGL.Icon('/imgs/device.png', new BMapGL.Size(146, 85)),
-			// offset: new BMapGL.Size(-73, -42.5),
-		})
-		points.push(point)
-		markers.push(marker)
-		map.addOverlay(marker)
-
-		marker.addEventListener('click', function () {
-			const deviceType = row.product.deviceType === '网关' ? '网关' : '设备'
-			// api.screen.alarmCount(row.key).then(({ handled, number, unhandled }: any) => {
-			const number = 0
-			const unhandled = 0
-			const handled = 0
-			const infoWindow = new BMapGL.InfoWindow(
-				`
-      <div class="map-hover-box">
-        <div class="map-hover-title">${row.name}</div>
-				${
-					false
-						? `
-        <div class="map-hover-nums">
-          <div class="map-hover-nums-item">
-            <div class="map-hover-nums-label">今日报警总数</div>
-            <div class="map-hover-nums-value">${number}</div>
-          </div>
-          <div class="map-hover-nums-item">
-            <div class="map-hover-nums-label">今日未处理报警</div>
-            <div class="map-hover-nums-value">${unhandled}</div>
-          </div>
-          <div class="map-hover-nums-item">
-            <div class="map-hover-nums-label">今日已处理报警</div>
-            <div class="map-hover-nums-value">${handled}</div>
+		if (!list.length) {
+			ElMessage.warning('改产品在不存在有坐标的设备!')
+			return map.centerAndZoom('四川', 5)
+		}
+
+		const points: any[] = []
+		const markers: any[] = []
+
+		list?.map((row: any) => {
+			if (!row.lat || !row.lng) return
+
+			const point = new BMapGL.Point(row.lng, row.lat)
+			const marker = new BMapGL.Marker(point, {
+				icon: new BMapGL.Icon('/imgs/device.png', new BMapGL.Size(146, 85)),
+				// offset: new BMapGL.Size(-73, -42.5),
+			})
+			points.push(point)
+			markers.push(marker)
+			map.addOverlay(marker)
+
+			marker.addEventListener('click', function () {
+				const deviceType = row.product.deviceType === '网关' ? '网关' : '设备'
+				// api.screen.alarmCount(row.key).then(({ handled, number, unhandled }: any) => {
+				const number = 0
+				const unhandled = 0
+				const handled = 0
+				const infoWindow = new BMapGL.InfoWindow(
+					`
+          <div class="map-hover-box">
+            <div class="map-hover-title">${row.name}</div>
+					${
+						false
+							? `
+          <div class="map-hover-nums">
+            <div class="map-hover-nums-item">
+              <div class="map-hover-nums-label">今日报警总数</div>
+              <div class="map-hover-nums-value">${number}</div>
+            </div>
+            <div class="map-hover-nums-item">
+              <div class="map-hover-nums-label">今日未处理报警</div>
+              <div class="map-hover-nums-value">${unhandled}</div>
+            </div>
+            <div class="map-hover-nums-item">
+              <div class="map-hover-nums-label">今日已处理报警</div>
+              <div class="map-hover-nums-value">${handled}</div>
+            </div>
+          </div>`
+							: ''
+					}
+            <div class="map-hover-row-item">
+              <div class="map-hover-label">编号:</div>
+              <div class="map-hover-value">${row.key}</div>
+            </div>
+            <div class="map-hover-row-item">
+              <div class="map-hover-label">最后在线时间:</div>
+              <div class="map-hover-value">${row.lastOnlineTime || '-'}</div>
+            </div>
+            <div class="map-hover-row-item">
+              <div class="map-hover-label">${deviceType}地址:</div>
+              <div class="map-hover-value">${row.address || '-'}</div>
+            </div>
           </div>
-        </div>`
-						: ''
-				}
-        <div class="map-hover-row-item">
-          <div class="map-hover-label">编号:</div>
-          <div class="map-hover-value">${row.key}</div>
-        </div>
-        <div class="map-hover-row-item">
-          <div class="map-hover-label">最后在线时间:</div>
-          <div class="map-hover-value">${row.lastOnlineTime || '-'}</div>
-        </div>
-        <div class="map-hover-row-item">
-          <div class="map-hover-label">${deviceType}地址:</div>
-          <div class="map-hover-value">${row.address || '-'}</div>
-        </div>
-      </div>
-        `,
-				{
-					width: 268 * 1.2, // 信息窗口宽度  268 * 193
-					// height: 120 * 1.2, // 信息窗口高度
-					title: '',
-				}
-			)
-			map.openInfoWindow(infoWindow, point) //开启信息窗口
-			// })
+            `,
+					{
+						width: 268 * 1.2, // 信息窗口宽度  268 * 193
+						// height: 120 * 1.2, // 信息窗口高度
+						title: '',
+					}
+				)
+				map.openInfoWindow(infoWindow, point) //开启信息窗口
+				// })
+			})
 		})
-	})
 
-	if (!markers.length) return
+		if (!markers.length) return
 
-	// 设置地图显示范围
-	map.setViewport(points, {
-		enableAnimation: true, // 启用动画过渡
-		margins: [30, 30, 30, 30], // 设置四个边界的边距
-	})
+		// 设置地图显示范围
+		map.setViewport(points, {
+			enableAnimation: true, // 启用动画过渡
+			margins: [30, 30, 30, 30], // 设置四个边界的边距
+		})
+	} finally {
+		loading.value = false
+	}
 }
 </script>
 <style scoped lang="scss">