Merge remote-tracking branch 'origin/master'
8 files modified
3 files added
| | |
| | | <template> |
| | | <div class="deviceChart"> |
| | | <div class="titleBg"> |
| | | <span>设备类型分析</span> |
| | | <span>出库去向统计</span> |
| | | </div> |
| | | <div class="chartBox" ref="chartRef"></div> |
| | | </div> |
| | |
| | | </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"> |
| | |
| | | </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' |
| | | |
| | |
| | | } |
| | | }) |
| | | |
| | | const areaOptions = ref([]) |
| | | |
| | | const rules = { |
| | | charger: fieldRules(true), // 负责人 |
| | | contactPhone: fieldRules(true, 50), // 联系电话 |
| | |
| | | } |
| | | |
| | | 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 => { |
| | |
| | | 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') |
| | |
| | | |
| | | onMounted(() => { |
| | | getUserList() |
| | | getAreaOptions() |
| | | }) |
| | | |
| | | defineExpose({ open }) |
| | |
| | | let locationFlag = false |
| | | let watchId = null |
| | | let userLocationMarker = null |
| | | const setMapLocation = () => { |
| | | locationFlag = true |
| | | |
| | | // 初始化实时位置监听 |
| | | const initLocationWatch = () => { |
| | | // 在WebView加载的网页中 |
| | | // 检查浏览器是否支持 geolocation |
| | | if (navigator.geolocation) { |
| | |
| | | // 停止监听位置 |
| | | navigator.geolocation.clearWatch(watchId) |
| | | } |
| | | |
| | | // 开始持续获取用户的位置 |
| | | watchId = navigator.geolocation.watchPosition( |
| | | function (position) { |
| | |
| | | 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) { |
| | |
| | | ) |
| | | } 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 |
| | |
| | | initMap() |
| | | map.addLayer(layers[0].map) |
| | | |
| | | // 初始化实时位置监听 |
| | | initLocationWatch() |
| | | |
| | | EventBus.on('mapSetView', mapSetView) |
| | | EventBus.on('mapAddMarker', mapAddMarker) |
| | | EventBus.on('mapAddAirMarker', mapAddAirMarker) |
| | |
| | | }) |
| | | |
| | | 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) |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 位置标记闪烁效果 |
| | | .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> |
| New file |
| | |
| | | <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> |
| | |
| | | <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(() => { |
| | |
| | | export function useGlobalWS() { |
| | | const userStore = useUserStore(); |
| | | const appStore = useAppStore(); |
| | | const callStatus = ref(null) |
| | | |
| | | // 设置默认用户ID为3 |
| | | const defaultUserId = '3'; |
| | |
| | | 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) |
| | |
| | | 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('无法跳转到语音通话页面:当前环境不支持导航'); |
| | |
| | | } |
| | | |
| | | // 然后处理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 } |
| | | ) |
| | | } |
| | | |
| | | |
| | |
| | | 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"; |
| | |
| | | const agreed = ref(true); |
| | | |
| | | const userStore = useUserStore(); |
| | | const locationStore = useLocationStore(); |
| | | const loginForm = ref({ |
| | | username: "", |
| | | password: "", |
| | |
| | | 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", |
| | | }); |
| | |
| | | import useAppStore from "./modules/app" |
| | | import useUserStore from "./modules/user" |
| | | import useMapStore from "./modules/map" |
| | | import useLocationStore from "./modules/location" |
| | | |
| | | // 安装pinia状态管理插件 |
| | | function setupStore (app) { |
| | |
| | | } |
| | | |
| | | // 导出模块 |
| | | export { useAppStore, useUserStore, useMapStore } |
| | | export { useAppStore, useUserStore, useMapStore, useLocationStore } |
| | | export default setupStore |
| New file |
| | |
| | | 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 |
| New file |
| | |
| | | /** |
| | | * 位置管理工具类 |
| | | * 用于处理位置权限请求和实时位置获取 |
| | | */ |
| | | 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 |
| | | }; |
| | |
| | | // WebSocket服务管理 |
| | | import { useGlobalWS } from "@/hooks/useGlobalWS.js";// WebSocket服务管理 |
| | | class WebSocketService { |
| | | constructor() { |
| | | this.socketTask = null |
| | |
| | | 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) |
| | | } |
| | |
| | | // 监听连接打开 |
| | | 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() |
| | |
| | | // 监听连接关闭 |
| | | this.socketTask.onClose(() => { |
| | | this.connected = false |
| | | console.log('WebSocket已关闭') |
| | | // console.log('WebSocket已关闭') |
| | | this.stopPing() |
| | | if (this.onCloseCallback) { |
| | | this.onCloseCallback() |
| | |
| | | |
| | | // 监听连接错误 |
| | | this.socketTask.onError((error) => { |
| | | console.error('WebSocket错误:', error) |
| | | // console.error('WebSocket错误:', error) |
| | | this.connected = false |
| | | this.stopPing() |
| | | if (this.onErrorCallback) { |
| | |
| | | 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) |
| | | } |
| | |
| | | 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) |
| | | } |
| | | }) |
| | | } |
| | |
| | | code: 1000, |
| | | reason: '正常关闭', |
| | | success: () => { |
| | | console.log('WebSocket已关闭') |
| | | // console.log('WebSocket已关闭') |
| | | }, |
| | | fail: (error) => { |
| | | console.error('WebSocket关闭失败:', error) |
| | | // console.error('WebSocket关闭失败:', error) |
| | | } |
| | | }) |
| | | this.socketTask = null |
| | |
| | | |
| | | // 设置消息回调 |
| | | setOnMessageCallback(callback) { |
| | | console.log('🔔 WebSocket消息回调已设置') |
| | | this.onMessageCallback = callback |
| | | } |
| | | |
| | | |
| | | // 获取当前消息回调(用于检查是否已设置) |
| | | getOnMessageCallback() { |
| | | return this.onMessageCallback |