From 899574ba3eaa89edfc8db7a452722f98f589a8e0 Mon Sep 17 00:00:00 2001 From: TongTongStudio Date: Wed, 5 Aug 2026 23:15:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(device):=20=E6=B7=BB=E5=8A=A0=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E5=AE=9A=E4=BD=8D=E4=BF=A1=E6=81=AF=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=E4=B8=8E=E7=99=BE=E5=BA=A6=E5=9C=B0=E5=9B=BE=E9=9B=86=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 VITE_BAIDU_MAP_AK 环境变量配置百度地图密钥 - 新增 DeviceLocationInfo 类型定义与 API 调用 - 在设备详情页新增"定位信息"标签页,支持定位概览展示与百度地图可视化 - 支持刷新定位指令下发并拉取最新坐标 - 修复推送 ID 列文本溢出显示省略号 - 优化操作按钮布局,支持换行与统一宽度 - 优化折叠面板卡片样式,增加边框与圆角 --- .env.development | 5 + src/api/system/device/index.ts | 14 ++ src/api/system/device/types.ts | 42 ++++ src/components/DeviceSn/DeviceSnDetail.vue | 261 ++++++++++++++++++++- src/views/devices/sn/config/content.ts | 2 +- types/env.d.ts | 1 + 6 files changed, 319 insertions(+), 6 deletions(-) diff --git a/.env.development b/.env.development index 5fc3077a..b4d8f138 100644 --- a/.env.development +++ b/.env.development @@ -16,3 +16,8 @@ VITE_MOCK_DEV_SERVER=false # 多租户开关(true:开启 false:关闭) VITE_APP_TENANT_ENABLED=false + +# 百度地图 JavaScript API v4.0 密钥(AK) +# 申请地址:https://lbsyun.baidu.com/apiconsole/key +# 注意:需在「应用管理-我的应用」中为该 AK 配置「浏览器端」白名单(Referer 域名/IP) +VITE_BAIDU_MAP_AK=DaKEjkzMRHg87GZgEJqAmxUxcBbJaYvS diff --git a/src/api/system/device/index.ts b/src/api/system/device/index.ts index c268204c..d4103e30 100644 --- a/src/api/system/device/index.ts +++ b/src/api/system/device/index.ts @@ -16,6 +16,7 @@ import type { DeviceOtherInfo, DeviceSecurityInfo, ApkInstallInfo, + DeviceLocationInfo, } from "./types"; import type { ExcelResult, PageResult, OptionItem } from "@/api/common"; @@ -365,6 +366,19 @@ const DeviceAPI = { }); }, + /** + * 获取设备最新定位信息 + * 对应后端 GET /{sn}/location (getLocation) + * + * @param sn 设备SN号 + */ + getLocationInfo(sn: string) { + return request({ + url: `${DEVICE_BASE_URL}/${sn}/location`, + method: "get", + }); + }, + /** 打开应用 */ launchApp(sn: string, packageName: string) { return request({ diff --git a/src/api/system/device/types.ts b/src/api/system/device/types.ts index 574425a6..11fe59b5 100644 --- a/src/api/system/device/types.ts +++ b/src/api/system/device/types.ts @@ -359,3 +359,45 @@ export interface ApkInstallInfo { /** 应用图标访问地址 */ iconUrl?: string; } + +// ===================== 设备定位信息 ===================== + +/** 设备定位信息 */ +export interface DeviceLocationInfo { + /** 设备序列号 */ + sn?: string; + /** 国家 */ + country?: string; + /** 国家代码 */ + countryCode?: string; + /** 省 */ + province?: string; + /** 市 */ + city?: string; + /** 城市代码 */ + cityCode?: string; + /** 区/县 */ + district?: string; + /** 街道 */ + street?: string; + /** 门牌号 */ + streetNumber?: string; + /** 详细地址 */ + address?: string; + /** 区域编码 */ + adCode?: string; + /** 镇 */ + town?: string; + /** 镇级行政区划编码 */ + townCode?: string; + /** 位置描述 */ + locationDescribe?: string; + /** 经度 */ + longitude?: string; + /** 纬度 */ + latitude?: string; + /** 定位失败原因,成功时为 '-' */ + mapError?: string; + /** 上次定位成功时间 */ + lastSuccessfulTime?: string; +} diff --git a/src/components/DeviceSn/DeviceSnDetail.vue b/src/components/DeviceSn/DeviceSnDetail.vue index 56ce2c0c..3551df74 100644 --- a/src/components/DeviceSn/DeviceSnDetail.vue +++ b/src/components/DeviceSn/DeviceSnDetail.vue @@ -564,8 +564,76 @@ -
- +
+ + + + + + {{ formatText(locationInfo.lastSuccessfulTime) }} + + + + 失败 + + 成功 + + + {{ formatText(locationInfo.province) }} / {{ formatText(locationInfo.city) }} + + + {{ formatText(locationInfo.district) }} + + + {{ formatText(locationInfo.address) }} + + + {{ formatText(locationInfo.locationDescribe) }} + + + {{ formatText(locationInfo.longitude) }} + + + {{ formatText(locationInfo.latitude) }} + + + {{ locationInfo.mapError }} + + + + + + + +
+ +
@@ -599,8 +667,9 @@ import type { DeviceNetworkInfo, DeviceOtherInfo, DeviceSecurityInfo, + DeviceLocationInfo, } from "@/api/system/device/types"; -import { reactive, ref, watch } from "vue"; +import { nextTick, reactive, ref, watch } from "vue"; import { Back, Edit, @@ -678,6 +747,16 @@ const networkInfo = reactive>({}); const securityInfo = reactive>({}); const otherInfo = reactive>({}); +/** 设备定位信息 */ +const locationInfo = reactive>({}); +const locationLoading = ref(false); +/** 百度地图容器 DOM 引用 */ +const mapContainer = ref(null); +/** 百度地图实例 */ +let baiduMap: any = null; +/** 百度地图 JS 是否已加载 */ +let baiduScriptLoaded = false; + const loading = reactive({ basic: false, hardware: false, @@ -685,6 +764,7 @@ const loading = reactive({ security: false, other: false, apk: false, + location: false, }); /** 设备已安装应用列表 */ @@ -847,6 +927,95 @@ async function loadOtherInfo(serialno: string) { } } +/** 定位信息 */ +async function loadLocationInfo(serialno: string) { + locationLoading.value = true; + try { + const data = (await DeviceAPI.getLocationInfo(serialno)) || {}; + assignTo(locationInfo, data); + // 坐标齐全则初始化/刷新地图(地图需等容器渲染完成后执行) + if (locationInfo.latitude && locationInfo.longitude) { + await nextTick(); + initBaiduMap(Number(locationInfo.longitude), Number(locationInfo.latitude)); + } + } catch (e) { + console.error("加载定位信息失败", e); + } finally { + locationLoading.value = false; + } +} + +/** 刷新定位:先下发定位指令,再拉取最新定位信息 */ +async function handleRefreshLocation() { + if (!props.serialno) return; + try { + await DeviceAPI.deviceLocate(props.serialno); + ElMessage.success("已下发定位指令,正在获取最新位置"); + // 定位上报有网络延迟,稍作等待后拉取 + setTimeout(() => loadLocationInfo(props.serialno), 1500); + } catch (e) { + ElMessage.error("下发定位指令失败"); + console.error("触发定位失败", e); + } +} + +/** + * 初始化百度地图并在指定坐标打点 + * 百度地图 JS 通过动态