吉安感知网项目-前端
chenyao
2026-01-17 99597fb1590fdec42300de4802dda103956ee582
Merge remote-tracking branch 'origin/master'
8 files modified
3 files added
710 ■■■■ changed files
applications/drone-command/src/views/basicManage/deviceStock/DeviceChart2.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceTrackDiaLog.vue 33 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appComponents/LeafletMap/index.vue 88 ●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appDataSource/leafletMapIcon/search-normal.svg 14 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/App.vue 17 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/hooks/useGlobalWS.js 104 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/pages/login/index.vue 22 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/store/index.js 3 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/store/modules/location.js 137 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/utils/location.js 260 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/utils/websocket.js 30 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceChart2.vue
@@ -1,7 +1,7 @@
<template>
    <div class="deviceChart">
        <div class="titleBg">
            <span>设备类型分析</span>
            <span>出库去向统计</span>
        </div>
        <div class="chartBox" ref="chartRef"></div>
    </div>
applications/drone-command/src/views/basicManage/deviceStock/DeviceTrackDiaLog.vue
@@ -16,7 +16,15 @@
                </el-col>
                <el-col :span="12">
                    <el-form-item label="出库去向" prop="outTarget">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.outTarget" maxlength="50" placeholder="请输入" clearable />
                        <el-select
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-select-popper"
                            v-model="formData.outTarget"
                            placeholder="请选择"
                            clearable
                        >
                            <el-option v-for="item in areaOptions" :key="item.value" :label="item.label" :value="item.label" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
@@ -54,9 +62,10 @@
</template>
<script setup>
import { computed, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { getUserListApi } from '@/api/system/user'
import { getLazyTree } from '@/api/base/region'
import { fieldRules } from '@ztzf/utils'
import { fwDeviceTrackSubmitApi } from '@/views/basicManage/deviceStock/fwDeviceTrackApi'
@@ -84,6 +93,8 @@
    }
})
const areaOptions = ref([])
const rules = {
    charger: fieldRules(true), // 负责人
    contactPhone: fieldRules(true, 50), // 联系电话
@@ -93,6 +104,16 @@
}
const userList = ref([])
function getAreaOptions() {
    getLazyTree('360800000000').then(res => {
        const list = res?.data?.data ?? []
        areaOptions.value = list.map(item => ({
            label: item.title,
            value: item.value,
        }))
    })
}
function getUserList() {
    formData.value.charger = ''
    getUserListApi().then(res => {
@@ -111,7 +132,12 @@
    if (!isValid) return
    submitting.value = true
    try {
        await fwDeviceTrackSubmitApi(formData.value)
        const selected = areaOptions.value.find(item => item.value === formData.value.outTarget)
        const payload = {
            ...formData.value,
            outTarget: selected?.label ?? formData.value.outTarget,
        }
        await fwDeviceTrackSubmitApi(payload)
        ElMessage.success('出库成功')
        visible.value = false
        emit('success')
@@ -134,6 +160,7 @@
onMounted(() => {
    getUserList()
    getAreaOptions()
})
defineExpose({ open })
applications/mobile-web-view/src/appComponents/LeafletMap/index.vue
@@ -156,9 +156,9 @@
let locationFlag = false
let watchId = null
let userLocationMarker = null
const setMapLocation = () => {
    locationFlag = true
// 初始化实时位置监听
const initLocationWatch = () => {
    // 在WebView加载的网页中
    // 检查浏览器是否支持 geolocation
    if (navigator.geolocation) {
@@ -166,7 +166,6 @@
            // 停止监听位置
            navigator.geolocation.clearWatch(watchId)
        }
        // 开始持续获取用户的位置
        watchId = navigator.geolocation.watchPosition(
            function (position) {
@@ -174,31 +173,26 @@
                const lat = position.coords.latitude // 纬度
                const lng = position.coords.longitude // 经度
                if (locationFlag) {
                    if (userLocationMarker) {
                        userLocationMarker.setLatLng([lat, lng])
                    } else {
                        userLocationMarker = L.marker([lat, lng], {
                            icon: L.icon({
                // 确保位置标记存在并更新位置
                if (userLocationMarker) {
                    userLocationMarker.setLatLng([lat, lng])
                } else {
                    userLocationMarker = L.marker([lat, lng], {
                        icon: L.icon({
                                iconUrl: userLocationIcon, // 图片路径
                                iconSize: [24, 24], // 图标尺寸
                                iconAnchor: [12, 12], // 锚点位置
                            }),
                        }).addTo(map)
                    }
                                    iconSize: [24, 24], // 图标尺寸
                                    iconAnchor: [12, 12], // 锚点位置
                                }),
                    }).addTo(map)
                }
                // 如果是点击定位按钮触发的,将地图视图定位到当前位置
                if (locationFlag) {
                    mapSetView({
                        lat,
                        lng,
                    })
                    locationFlag = false
                    return
                }
                if (userLocationMarker) {
                    userLocationMarker.setLatLng([lat, lng])
                }
            },
            function (error) {
@@ -213,6 +207,24 @@
        )
    } else {
        console.log('该浏览器不支持地理定位功能。')
    }
}
const setMapLocation = () => {
    locationFlag = true
    // 如果还没有开始监听位置,先初始化监听
    if (!watchId) {
        initLocationWatch()
    } else {
        // 已经在监听位置,会自动定位到当前位置
        // 可以添加一个视觉反馈,比如位置标记闪烁效果
        if (userLocationMarker) {
            // 添加闪烁效果
            userLocationMarker.getElement().classList.add('location-blink')
            setTimeout(() => {
                userLocationMarker.getElement().classList.remove('location-blink')
            }, 1000)
        }
    }
}
let lastLocationMarker = null
@@ -450,6 +462,9 @@
    initMap()
    map.addLayer(layers[0].map)
    // 初始化实时位置监听
    initLocationWatch()
    EventBus.on('mapSetView', mapSetView)
    EventBus.on('mapAddMarker', mapAddMarker)
    EventBus.on('mapAddAirMarker', mapAddAirMarker)
@@ -466,6 +481,17 @@
})
onUnmounted(() => {
    // 清理位置监听
    if (watchId) {
        navigator.geolocation.clearWatch(watchId)
        watchId = null
    }
    // 移除位置标记
    if (userLocationMarker) {
        map.removeLayer(userLocationMarker)
        userLocationMarker = null
    }
    EventBus.off('mapSetView', mapSetView)
    EventBus.off('mapAddMarker', mapAddMarker)
    EventBus.off('mapAddAirMarker', mapAddAirMarker)
@@ -606,4 +632,24 @@
        }
    }
}
// 位置标记闪烁效果
.location-blink {
    animation: blink 1s ease-in-out;
}
@keyframes blink {
    0% {
        transform: scale(1);
        opacity: 1;
    }
    50% {
        transform: scale(1.2);
        opacity: 0.8;
    }
    100% {
        transform: scale(1);
        opacity: 1;
    }
}
</style>
applications/mobile-web-view/src/appDataSource/leafletMapIcon/search-normal.svg
New file
@@ -0,0 +1,14 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="search-normal">
<g id="search-normal_2">
<g id="search-normal_3">
<g id="vuesax/linear/search-normal">
<g id="search-normal_4">
<path id="Vector" d="M7.66659 14C11.1644 14 13.9999 11.1644 13.9999 7.66665C13.9999 4.16884 11.1644 1.33331 7.66659 1.33331C4.16878 1.33331 1.33325 4.16884 1.33325 7.66665C1.33325 11.1644 4.16878 14 7.66659 14Z" stroke="#191919" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_2" d="M14.6666 14.6666L13.3333 13.3333" stroke="#191919" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</g>
</g>
</g>
</g>
</svg>
uniapps/work-app/src/App.vue
@@ -1,19 +1,32 @@
<script setup>
import { onHide, onLaunch, onShow } from "@dcloudio/uni-app";
import { useAppStore, useUserStore } from "@/store";
import { useAppStore, useUserStore, useLocationStore } from "@/store";
import { useGlobalWS } from "@/hooks/useGlobalWS.js";
const appStore = useAppStore();
const userStore = useUserStore();
const locationStore = useLocationStore();
useGlobalWS();
onShow(() => {
onShow(async () => {
  console.log("App Show");
  // 应用从后台返回前台时,重新初始化位置服务
  if (userStore.userInfo && locationStore) {
    try {
      await locationStore.initLocationService();
    } catch (error) {
      console.error('重新初始化位置服务失败:', error);
    }
  }
});
onHide(() => {
  console.log("App Hide");
  // 应用进入后台时,停止位置监听以节省资源
  if (locationStore) {
    locationStore.stopLocationWatch();
  }
});
onLaunch(() => {
uniapps/work-app/src/hooks/useGlobalWS.js
@@ -5,6 +5,7 @@
export function useGlobalWS() {
    const userStore = useUserStore();
  const appStore = useAppStore();
  const callStatus = ref(null)
  // 设置默认用户ID为3
  const defaultUserId = '3';
@@ -16,10 +17,9 @@
    console.log('🌐 全局WebSocket收到消息111111111111111111111:', payload)
    // 先尝试直接处理消息(适用于mobile-web-view的voiceCallDetail页面的消息格式)
    const t = (payload.type || '').toString()
    const bizCode = payload.biz_code || ''
    console.log('📋 消息分析:type=' + t + ', biz_code=' + bizCode)
    // const bizCode = payload.biz_code || ''
    callStatus.value = t
    // console.log('📋 消息分析:type=' + t )
    // 处理语音通话请求
    if (t === 'call') {
      console.log('📞 全局收到来电 call,来自', payload.from)
@@ -36,15 +36,8 @@
      try {
        // 优先使用uni-app的导航API(适用于同应用内跳转)
        if (typeof uni !== 'undefined' && uni.navigateTo) {
          console.log('使用uni-app导航API跳转到语音通话页面');
          uni.navigateTo({
            url: `/subPackages/voiceCallDetail/index?voiceparams=${encodedParams}`,
            success: () => {
              console.log('✅ 应用内导航成功!');
            },
            fail: (err) => {
              console.error('❌ 应用内导航失败:', err);
            }
          });
        } else {
          console.error('无法跳转到语音通话页面:当前环境不支持导航');
@@ -56,64 +49,63 @@
    }
    // 然后处理biz_code格式的消息
    switch (bizCode) {
      case 'JOB_ISREFRESH':
        appStore.setJobUpdateKeyAdd()
        break
      case 'DEVICE_ISREFRESH':
        appStore.setDeviceUpdateKeyAdd()
        break
      case 'DOWNLOAD_PROGRESS':
        break
      case 'LOGOUT_USER':
        userStore.setUserInfo(null)
        uni.reLaunch({
          url: '/pages/login/index'
        })
        break
      case 'VoiceCall':
        // enterRoom(payload, userId.value)
        break
      default:
        // 记录未处理的消息
        console.log('未处理的WebSocket消息:', payload)
        break;
    }
    // switch (bizCode) {
    //   case 'JOB_ISREFRESH':
    //     appStore.setJobUpdateKeyAdd()
    //     break
    //   case 'DEVICE_ISREFRESH':
    //     appStore.setDeviceUpdateKeyAdd()
    //     break
    //   case 'DOWNLOAD_PROGRESS':
    //     break
    //   case 'LOGOUT_USER':
    //     userStore.setUserInfo(null)
    //     uni.reLaunch({
    //       url: '/pages/login/index'
    //     })
    //     break
    //   case 'VoiceCall':
    //     // enterRoom(payload, userId.value)
    //     break
    //   default:
    //     // 记录未处理的消息
    //     console.log('未处理的WebSocket消息:', payload)
    //     break;
    // }
  }
  // 初始化WebSocket连接
  function initWS() {
    try {
      // 检查是否已经有活跃的WebSocket连接
      if (!websocketService.getConnected()) {
        // 使用默认用户ID初始化WebSocket连接
        websocketService.init(defaultUserId, WS_BASE);
        console.log('🌐 全局WebSocket初始化成功,默认用户ID:', defaultUserId);
      } else {
        console.log('🌐 WebSocket已存在活跃连接,无需重新初始化');
      }
      // 只设置一次全局消息处理回调
      // 检查是否已经设置了回调
      if (typeof websocketService.getOnMessageCallback() !== 'function') {
        // 设置全局消息处理回调
        websocketService.setOnMessageCallback(messageHandler);
        console.log('✅ 全局WebSocket消息处理器已设置');
      websocketService.setOnMessageCallback(messageHandler);
      // 获取当前用户ID,优先使用store中的用户信息
      const userId = userStore.userInfo?.id || defaultUserId;
      // 检查是否已经有活跃的WebSocket连接
      if (!websocketService.getConnected() || websocketService.userId !== userId) {
        // 使用用户ID初始化WebSocket连接
        websocketService.init(userId, WS_BASE);
        // console.log('🌐 全局WebSocket初始化成功,用户ID:', userId);
      } else {
        console.log('✅ WebSocket消息处理器已存在,无需重新设置');
        websocketService.connect();
      }
    } catch (error) {
      console.error('❌ 全局WebSocket初始化失败:', error);
    }
  }
  // 监听用户信息变化,初始化WebSocket
  watch(
    () => userStore.userInfo,
    (newUserInfo) => {
      console.log('👤 用户信息变化,尝试初始化WebSocket');
    () => callStatus.value,
    (newValue) => {
      if (newValue === 'accept') {
        console.log('📞 通话中,跳过WebSocket初始化');
        return;
      }
      initWS();
    },
    { immediate: true }
    { immediate: true, deep: true }
  )
}
uniapps/work-app/src/pages/login/index.vue
@@ -44,7 +44,8 @@
import { getAssetsImage } from "@/utils/index.js";
import md5 from "js-md5";
import { loginByUsername } from "@/api/user/index.js";
import { useUserStore } from "@/store/index.js";
import { useUserStore, useLocationStore } from "@/store/index.js";
import locationUtil from "@/utils/location.js";
import websocketService from "@/utils/websocket.js";
import { HOME_PATH, LOGIN_PATH, removeQueryString } from "@/router";
@@ -56,6 +57,7 @@
const agreed = ref(true);
const userStore = useUserStore();
const locationStore = useLocationStore();
const loginForm = ref({
  username: "",
  password: "",
@@ -135,8 +137,22 @@
      userInfo.key,
      userInfo.code
    );
    userStore.setUserInfo(res.data.data);
    console.log('登录成功,WebSocket连接将由全局钩子统一管理');
    userStore.setUserInfo(res.data.data);
    // 请求位置权限并初始化位置服务
    try {
      // 请求位置权限
      const hasPermission = await locationUtil.requestLocationPermission();
      if (hasPermission) {
        console.log('获取位置权限成功');
        // 初始化位置服务
        await locationStore.initLocationService();
      } else {
        console.log('获取位置权限失败');
      }
    } catch (error) {
      console.error('处理位置服务失败:', error);
    }
    uni.reLaunch({
      url: "/pages/work/index",
    });
uniapps/work-app/src/store/index.js
@@ -6,6 +6,7 @@
import useAppStore from "./modules/app"
import useUserStore from "./modules/user"
import useMapStore from "./modules/map"
import useLocationStore from "./modules/location"
// 安装pinia状态管理插件
function setupStore (app) {
@@ -23,5 +24,5 @@
}
// 导出模块
export { useAppStore, useUserStore, useMapStore }
export { useAppStore, useUserStore, useMapStore, useLocationStore }
export default setupStore
uniapps/work-app/src/store/modules/location.js
New file
@@ -0,0 +1,137 @@
import { defineStore } from 'pinia'
import locationUtil from '@/utils/location.js'
const useLocationStore = defineStore('location', {
  state: () => ({
    // 当前位置信息
    currentLocation: null,
    // 位置权限状态
    hasPermission: false,
    // 系统定位服务是否开启
    systemLocationEnabled: true,
    // 位置更新间隔(毫秒)
    updateInterval: 5000,
    // 位置监听器ID
    watcherId: null
  }),
  getters: {
    /**
     * 获取当前位置
     * @returns {Object|null} 当前位置信息
     */
    getCurrentLocation: (state) => state.currentLocation,
    /**
     * 获取位置权限状态
     * @returns {boolean} 是否有位置权限
     */
    getPermissionStatus: (state) => state.hasPermission,
    /**
     * 获取系统定位服务状态
     * @returns {boolean} 系统定位服务是否开启
     */
    getSystemLocationStatus: (state) => state.systemLocationEnabled
  },
  actions: {
    /**
     * 初始化位置服务
     */
    async initLocationService() {
      try {
        // 检查系统定位服务是否开启
        this.systemLocationEnabled = await locationUtil.checkSystemLocationEnabled();
        if (!this.systemLocationEnabled) {
          console.warn('系统定位服务未开启');
        }
        // 请求位置权限
        this.hasPermission = await locationUtil.requestLocationPermission();
        if (this.hasPermission) {
          // 获取当前位置
          await this.updateCurrentLocation();
          // 开始位置监听
          this.startLocationWatch();
        }
      } catch (error) {
        console.error('初始化位置服务失败:', error);
      }
    },
    /**
     * 更新当前位置
     */
    async updateCurrentLocation() {
      try {
        const location = await locationUtil.getCurrentLocation();
        this.currentLocation = location;
        console.log('位置更新:', location);
        return location;
      } catch (error) {
        console.error('更新位置失败:', error);
        return null;
      }
    },
    /**
     * 开始位置监听
     */
    startLocationWatch() {
      if (this.watcherId) {
        this.stopLocationWatch();
      }
      this.watcherId = locationUtil.startLocationWatcher((location) => {
        this.currentLocation = location;
        // 可以在这里添加位置信息上报逻辑
        this.reportLocation(location);
      }, {
        interval: this.updateInterval
      });
    },
    /**
     * 停止位置监听
     */
    stopLocationWatch() {
      if (this.watcherId) {
        locationUtil.stopLocationWatcher();
        this.watcherId = null;
      }
    },
    /**
     * 重新请求位置权限
     */
    async reRequestPermission() {
      this.hasPermission = await locationUtil.requestLocationPermission();
      if (this.hasPermission) {
        await this.updateCurrentLocation();
        this.startLocationWatch();
      }
      return this.hasPermission;
    },
    /**
     * 位置信息上报(可根据实际需求实现)
     * @param {Object} location 位置信息
     */
    reportLocation(location) {
      // 这里可以添加位置信息上报到服务器的逻辑
      // 例如:api.uploadLocation(location)
      // console.log('位置上报:', location);
    },
    /**
     * 清理位置服务
     */
    cleanupLocationService() {
      this.stopLocationWatch();
      this.currentLocation = null;
    }
  }
})
export default useLocationStore
uniapps/work-app/src/utils/location.js
New file
@@ -0,0 +1,260 @@
/**
 * 位置管理工具类
 * 用于处理位置权限请求和实时位置获取
 */
import permission from './voiceCallByTX/TrtcCloud/permission.js'
// 平台判断
const isIos = () => {
  // #ifdef APP-PLUS
  return plus.os.name === "iOS";
  // #endif
  return false;
};
// 位置更新回调函数
let locationUpdateCallback = null;
// 位置监听器ID
let locationWatcherId = null;
/**
 * 请求位置权限
 * @returns {Promise<boolean>} 是否获取到权限
 */
export const requestLocationPermission = async () => {
  return new Promise(async (resolve) => {
    // #ifdef APP-PLUS
    if (isIos()) {
      // iOS平台
      const hasPermission = permission.judgeIosPermission('location');
      if (hasPermission) {
        resolve(true);
      } else {
        // iOS需要提示用户手动开启权限
        uni.showModal({
          title: '提示',
          content: '应用需要获取位置信息权限,请在设置中开启',
          confirmText: '去设置',
          cancelText: '取消',
          success: (res) => {
            if (res.confirm) {
              permission.gotoAppPermissionSetting();
              resolve(false);
            } else {
              resolve(false);
            }
          }
        });
      }
    } else {
      // Android平台
      const result = await permission.requestAndroidPermission('android.permission.ACCESS_FINE_LOCATION');
      if (result === 1) {
        resolve(true);
      } else if (result === 0) {
        // 拒绝本次申请
        resolve(false);
      } else if (result === -1) {
        // 永久拒绝
        uni.showModal({
          title: '提示',
          content: '应用需要获取位置信息权限,请在设置中开启',
          confirmText: '去设置',
          cancelText: '取消',
          success: (res) => {
            if (res.confirm) {
              permission.gotoAppPermissionSetting();
              resolve(false);
            } else {
              resolve(false);
            }
          }
        });
      }
    }
    // #endif
    // #ifdef H5
    // H5平台使用浏览器原生API请求权限
    if (navigator.geolocation) {
      navigator.permissions.query({ name: 'geolocation' }).then((result) => {
        if (result.state === 'granted') {
          resolve(true);
        } else if (result.state === 'prompt') {
          // 还未请求过权限,会在getLocation时提示
          resolve(true);
        } else {
          resolve(false);
        }
      }).catch(() => {
        resolve(false);
      });
    } else {
      resolve(false);
    }
    // #endif
    // #ifdef MP
    // 小程序平台
    uni.getSetting({
      success: (res) => {
        if (res.authSetting['scope.userLocation']) {
          resolve(true);
        } else {
          uni.authorize({
            scope: 'scope.userLocation',
            success: () => {
              resolve(true);
            },
            fail: () => {
              uni.showModal({
                title: '提示',
                content: '应用需要获取位置信息权限,请在设置中开启',
                confirmText: '去设置',
                cancelText: '取消',
                success: (res) => {
                  if (res.confirm) {
                    uni.openSetting();
                    resolve(false);
                  } else {
                    resolve(false);
                  }
                }
              });
            }
          });
        }
      }
    });
    // #endif
  });
};
/**
 * 获取当前位置信息
 * @returns {Promise<Object>} 位置信息对象
 */
export const getCurrentLocation = () => {
  return new Promise((resolve, reject) => {
    uni.getLocation({
      type: 'gcj02',
      altitude: true,
      success: (res) => {
        resolve({
          latitude: res.latitude,
          longitude: res.longitude,
          altitude: res.altitude,
          accuracy: res.accuracy,
          speed: res.speed,
          verticalAccuracy: res.verticalAccuracy,
          horizontalAccuracy: res.horizontalAccuracy
        });
      },
      fail: (err) => {
        console.error('获取位置失败:', err);
        reject(err);
      }
    });
  });
};
/**
 * 开始实时位置监听
 * @param {Function} callback 位置更新回调函数
 * @param {Object} options 监听选项
 * @returns {number} 监听器ID
 */
export const startLocationWatcher = (callback, options = {}) => {
  if (locationWatcherId) {
    stopLocationWatcher();
  }
  locationUpdateCallback = callback;
  locationWatcherId = uni.startLocationUpdate({
    success: () => {
      console.log('开始位置监听成功');
    },
    fail: (err) => {
      console.error('开始位置监听失败:', err);
      // 如果是因为权限问题,重新请求权限
      if (err.errCode === 12002) {
        requestLocationPermission();
      }
    }
  });
  // 监听位置更新
  uni.onLocationChange((res) => {
    const locationInfo = {
      latitude: res.latitude,
      longitude: res.longitude,
      altitude: res.altitude,
      accuracy: res.accuracy,
      speed: res.speed,
      verticalAccuracy: res.verticalAccuracy,
      horizontalAccuracy: res.horizontalAccuracy,
      timestamp: res.timestamp
    };
    if (locationUpdateCallback) {
      locationUpdateCallback(locationInfo);
    }
  });
  return locationWatcherId;
};
/**
 * 停止位置监听
 */
export const stopLocationWatcher = () => {
  if (locationWatcherId) {
    uni.stopLocationUpdate({
      success: () => {
        console.log('停止位置监听成功');
      },
      fail: (err) => {
        console.error('停止位置监听失败:', err);
      }
    });
    locationWatcherId = null;
  }
  // 移除位置更新监听
  uni.offLocationChange();
  locationUpdateCallback = null;
};
/**
 * 检查系统定位服务是否开启
 * @returns {Promise<boolean>} 是否开启
 */
export const checkSystemLocationEnabled = async () => {
  return new Promise((resolve) => {
    // #ifdef APP-PLUS
    const enabled = permission.checkSystemEnableLocation();
    resolve(enabled);
    // #endif
    // #ifdef H5
    resolve(navigator.geolocation ? true : false);
    // #endif
    // #ifdef MP
    uni.getSystemInfo({
      success: (res) => {
        // 小程序无法直接检查系统定位服务是否开启
        resolve(true);
      },
      fail: () => {
        resolve(false);
      }
    });
    // #endif
  });
};
export default {
  requestLocationPermission,
  getCurrentLocation,
  startLocationWatcher,
  stopLocationWatcher,
  checkSystemLocationEnabled
};
uniapps/work-app/src/utils/websocket.js
@@ -1,4 +1,4 @@
// WebSocket服务管理
import { useGlobalWS } from "@/hooks/useGlobalWS.js";// WebSocket服务管理
class WebSocketService {
  constructor() {
    this.socketTask = null
@@ -34,10 +34,10 @@
      this.socketTask = uni.connectSocket({
        url: this.url,
        success: () => {
          console.log('WebSocket连接已请求')
          // console.log('WebSocket连接已请求')
        },
        fail: (error) => {
          console.error('WebSocket连接请求失败:', error)
          // console.error('WebSocket连接请求失败:', error)
          if (this.onErrorCallback) {
            this.onErrorCallback(error)
          }
@@ -47,7 +47,7 @@
      // 监听连接打开
      this.socketTask.onOpen(() => {
        this.connected = true
        console.log('WebSocket已连接,userId:', this.userId)
        // console.log('WebSocket已连接,userId:', this.userId)
        this.startPing()
        if (this.onOpenCallback) {
          this.onOpenCallback()
@@ -57,7 +57,7 @@
      // 监听连接关闭
      this.socketTask.onClose(() => {
        this.connected = false
        console.log('WebSocket已关闭')
        // console.log('WebSocket已关闭')
        this.stopPing()
        if (this.onCloseCallback) {
          this.onCloseCallback()
@@ -66,7 +66,7 @@
      // 监听连接错误
      this.socketTask.onError((error) => {
        console.error('WebSocket错误:', error)
        // console.error('WebSocket错误:', error)
        this.connected = false
        this.stopPing()
        if (this.onErrorCallback) {
@@ -78,16 +78,16 @@
      this.socketTask.onMessage((res) => {
        try {
          const message = JSON.parse(res.data)
          console.log('收到WebSocket消息:', message)
          // console.log('收到WebSocket消息:', message)
          if (this.onMessageCallback) {
            this.onMessageCallback(message)
          }
        } catch (error) {
          console.error('解析WebSocket消息失败:', error)
          // console.error('解析WebSocket消息失败:', error)
        }
      })
    } catch (error) {
      console.error('WebSocket连接失败:', error)
      // console.error('WebSocket连接失败:', error)
      if (this.onErrorCallback) {
        this.onErrorCallback(error)
      }
@@ -112,10 +112,11 @@
    this.socketTask.send({
      data: JSON.stringify(message),
      success: () => {
        console.log('WebSocket消息发送成功:', message)
        // console.log('WebSocket消息发送成功:', message)
        useGlobalWS();
      },
      fail: (error) => {
        console.error('WebSocket消息发送失败:', error)
        // console.error('WebSocket消息发送失败:', error)
      }
    })
  }
@@ -146,10 +147,10 @@
        code: 1000,
        reason: '正常关闭',
        success: () => {
          console.log('WebSocket已关闭')
          // console.log('WebSocket已关闭')
        },
        fail: (error) => {
          console.error('WebSocket关闭失败:', error)
          // console.error('WebSocket关闭失败:', error)
        }
      })
      this.socketTask = null
@@ -160,10 +161,9 @@
  // 设置消息回调
  setOnMessageCallback(callback) {
    console.log('🔔 WebSocket消息回调已设置')
    this.onMessageCallback = callback
  }
  // 获取当前消息回调(用于检查是否已设置)
  getOnMessageCallback() {
    return this.onMessageCallback