吉安感知网项目-前端
shuishen
2026-01-16 ceeb5fdbe689d68c4f795690ea56a27ed515757c
Merge branch 'master' of http://139.196.74.78:10010/r/jagzwxm/ja_web
4 files modified
527 ■■■■ changed files
uniapps/work-app/src/hooks/useGlobalWS.js 102 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/pages/login/index.vue 53 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/workDetail/index.vue 366 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/utils/websocket.js 6 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/hooks/useGlobalWS.js
@@ -1,5 +1,4 @@
import { useUserStore } from "@/store/index.js";
import { ref, computed, watch } from "vue";
import useAppStore from "../store/modules/app/index.js";
import websocketService from "@/utils/websocket.js";
@@ -34,81 +33,24 @@
      // 转义参数以便在URL中传递
      const encodedParams = encodeURIComponent(JSON.stringify(callParams));
      // 跳转到mobile-web-view应用中的语音通话页面
      const voiceCallUrl = `http://localhost:9527/work-app/#/subPackages/voiceCallDetail/index?voiceparams=${encodedParams}`;
      console.log('🔗 准备导航到:', voiceCallUrl)
      try {
        // 优先使用uni-app的导航API(适用于uni-app环境)
        // 优先使用uni-app的导航API(适用于同应用内跳转)
        if (typeof uni !== 'undefined' && uni.navigateTo) {
          console.log('使用uni-app导航API跳转到语音通话页面');
          // 检查是否是跳转到应用内页面
          if (voiceCallUrl.includes('/subPackages/voiceCallDetail/')) {
            // 应用内页面跳转
            uni.navigateTo({
              url: voiceCallUrl.replace('http://localhost:9527/work-app/#', ''),
              success: () => {
                console.log('✅ 应用内导航成功!');
              },
              fail: (err) => {
                console.error('❌ 应用内导航失败:', err);
                // 如果应用内导航失败,尝试使用外部跳转
                fallbackToExternalNavigation();
              }
            });
          } else {
            // 外部页面跳转
            fallbackToExternalNavigation();
          }
        }
        // 否则尝试使用window.location跳转(适用于Web环境)
        else if (typeof window !== 'undefined' && window.location) {
          console.log('使用window.location跳转到语音通话页面');
          window.location.href = voiceCallUrl;
          uni.navigateTo({
            url: `/subPackages/voiceCallDetail/index?voiceparams=${encodedParams}`,
            success: () => {
              console.log('✅ 应用内导航成功!');
            },
            fail: (err) => {
              console.error('❌ 应用内导航失败:', err);
            }
          });
        } else {
          console.error('无法跳转到语音通话页面:当前环境不支持导航');
        }
      } catch (error) {
        console.error('跳转到语音通话页面失败:', error);
        // 尝试使用外部跳转作为最后的补救措施
        fallbackToExternalNavigation();
      }
      // 外部页面跳转的回调函数
      function fallbackToExternalNavigation() {
        console.log('尝试使用外部导航跳转到语音通话页面');
        try {
          // 使用uni-app的外部链接跳转API
          if (typeof uni !== 'undefined' && uni.navigateToWebview) {
            // 这是一个示例,具体API可能需要根据uni-app版本和配置调整
            uni.navigateToWebview({
              url: voiceCallUrl,
              success: () => {
                console.log('✅ 外部Webview导航成功!');
              },
              fail: (err) => {
                console.error('❌ 外部Webview导航失败:', err);
                // 如果还是失败,尝试使用浏览器打开
                if (typeof plus !== 'undefined' && plus.runtime) {
                  plus.runtime.openURL(voiceCallUrl);
                } else {
                  console.error('❌ 所有导航方式都失败了');
                }
              }
            });
          }
          // 或者使用plus.runtime.openURL(适用于App环境)
          else if (typeof plus !== 'undefined' && plus.runtime) {
            plus.runtime.openURL(voiceCallUrl);
            console.log('尝试使用plus.runtime.openURL打开外部链接');
          } else {
            console.error('无法跳转到外部页面:当前环境不支持外部导航');
          }
        } catch (fallbackError) {
          console.error('外部导航尝试失败:', fallbackError);
        }
      }
      return
    }
@@ -142,14 +84,24 @@
  // 初始化WebSocket连接
  function initWS() {
    try {
      // 使用默认用户ID初始化WebSocket连接
      websocketService.init(defaultUserId, WS_BASE);
      console.log('🌐 全局WebSocket初始化成功,默认用户ID:', defaultUserId);
      // 检查是否已经有活跃的WebSocket连接
      if (!websocketService.getConnected()) {
        // 使用默认用户ID初始化WebSocket连接
        websocketService.init(defaultUserId, WS_BASE);
        console.log('🌐 全局WebSocket初始化成功,默认用户ID:', defaultUserId);
      } else {
        console.log('🌐 WebSocket已存在活跃连接,无需重新初始化');
      }
      // 设置全局消息处理回调
      websocketService.setOnMessageCallback(messageHandler);
      console.log('✅ 全局WebSocket消息处理器已设置');
      // 只设置一次全局消息处理回调
      // 检查是否已经设置了回调
      if (typeof websocketService.getOnMessageCallback() !== 'function') {
        // 设置全局消息处理回调
        websocketService.setOnMessageCallback(messageHandler);
        console.log('✅ 全局WebSocket消息处理器已设置');
      } else {
        console.log('✅ WebSocket消息处理器已存在,无需重新设置');
      }
    } catch (error) {
      console.error('❌ 全局WebSocket初始化失败:', error);
    }
uniapps/work-app/src/pages/login/index.vue
@@ -135,59 +135,8 @@
      userInfo.key,
      userInfo.code
    );
    console.log('登录成功,用户信息:', res.data.data);
    userStore.setUserInfo(res.data.data);
    // 初始化WebSocket连接
    try {
      const wsUrl = 'wss://wrj.shuixiongit.com/ws/chat?userId=';
      // 设置默认用户ID为3
      const defaultUserId = '3';
      // 使用默认用户ID初始化WebSocket连接
      websocketService.init(defaultUserId, wsUrl);
      console.log('登录成功,WebSocket已初始化,默认用户ID:', defaultUserId);
      // 设置来电消息回调
      websocketService.setOnMessageCallback((message) => {
        if (message.type === 'call') {
          console.log('收到来电,准备跳转到语音通话页面');
          // 构建来电参数
          const callParams = {
            peerUid: message.from,
            from: message.from,
            type: 'incoming'
          };
          // 转义参数以便在URL中传递
          const encodedParams = encodeURIComponent(JSON.stringify(callParams));
          try {
            // 优先使用uni-app的导航API(适用于同应用内跳转)
            if (typeof uni !== 'undefined' && uni.navigateTo) {
              console.log('使用uni-app导航API跳转到语音通话页面');
              uni.navigateTo({
                url: `/subPackages/voiceCallDetail/index?params=${encodedParams}`
              });
            }
            // 否则使用window.location跳转(适用于Web环境)
            else if (typeof window !== 'undefined' && window.location) {
              console.log('使用window.location跳转到语音通话页面');
              // 注意:实际部署时需要使用正确的URL
              const voiceCallUrl = `http://localhost:8080/appPages/voiceCallDetail/index.html?params=${encodedParams}`;
              window.location.href = voiceCallUrl;
            } else {
              console.error('无法跳转到语音通话页面:当前环境不支持导航');
            }
          } catch (error) {
            console.error('跳转到语音通话页面失败:', error);
          }
        }
      });
    } catch (error) {
      console.error('WebSocket初始化失败:', error);
    }
    console.log('登录成功,WebSocket连接将由全局钩子统一管理');
    uni.reLaunch({
      url: "/pages/work/index",
    });
uniapps/work-app/src/subPackages/workDetail/index.vue
@@ -1,40 +1,58 @@
<!-- 工单详情 -->
<template>
  <div class="workDetailContainer">
    <!-- 自定义导航栏 -->
    <u-navbar
      title="工单详情"
      :is-back="true"
      back-text=""
      :back-icon-size="40"
      @left-click="onBackClick"
    >
      <template #right>
        <div class="share-btn" @click="onShareClick">分享</div>
      </template>
    </u-navbar>
    <div class="detailTop">
      <div class="image-container">
        <u-swiper
          class="detailImage"
          :list="getImageList"
          mode="round"
          indicator
          indicatorMode="dot"
          indicatorActiveColor="#4C85FF"
          indicatorInactiveColor="#fff"
          indicatorPosition="center"
          height="410rpx"
          @click="previewImage"
        ></u-swiper>
        <!--                <div class="detailTitle">-->
        <!--                    <div class="titleText">-->
        <!--                        <div class="itemName">{{workDetailData.event_name}}</div>-->
        <!--                        <div class="itemTime">{{formatDate(workDetailData.create_time)}}</div>-->
        <!--                    </div>-->
        <!--                </div>-->
      </div>
    </div>
    <div class="workDetailContainer">
        <!-- 自定义导航栏 -->
        <u-navbar title="工单详情" :is-back="true" back-text="" :back-icon-size="40" @left-click="onBackClick">
            <template #right>
                <div class="share-btn" @click="onShareClick">分享</div>
            </template>
        </u-navbar>
        <!-- 分享弹出层 -->
        <div v-if="showShareModal" class="share-modal-overlay" @click="onShareModalClose">
            <div class="share-modal-content" @click.stop>
                <div class="share-modal-header">
                    <div class="share-modal-title">分享到</div>
                    <div class="share-modal-close" @click="onShareModalClose">×</div>
                </div>
                <div class="share-modal-body">
                    <div class="share-item" @click="shareToWechat">
                        <div class="share-icon wechat"></div>
                        <div class="share-name">微信好友</div>
                    </div>
                    <div class="share-item" @click="shareToMoments">
                        <div class="share-icon moments"></div>
                        <div class="share-name">朋友圈</div>
                    </div>
                    <div class="share-item" @click="copyLink">
                        <div class="share-icon link"></div>
                        <div class="share-name">复制链接</div>
                    </div>
                </div>
            </div>
        </div>
        <div class="detailTop">
            <div class="image-container">
                <u-swiper
                    class="detailImage"
                    :list="getImageList"
                    mode="round"
                    indicator
                    indicatorMode="dot"
                    indicatorActiveColor="#4C85FF"
                    indicatorInactiveColor="#fff"
                    indicatorPosition="center"
                    height="410rpx"
                    @click="previewImage"
                ></u-swiper>
                <!--                <div class="detailTitle">-->
                <!--                    <div class="titleText">-->
                <!--                        <div class="itemName">{{workDetailData.event_name}}</div>-->
                <!--                        <div class="itemTime">{{formatDate(workDetailData.create_time)}}</div>-->
                <!--                    </div>-->
                <!--                </div>-->
            </div>
        </div>
    <!-- 工单内容 -->
    <div class="worderContainer">
@@ -178,88 +196,111 @@
  })
}
// 分享按钮点击事件
// 分享模态框状态
const showShareModal = ref(false)
// 分享按钮点击事件 - 显示分享模态框
const onShareClick = () => {
  // 检查当前平台是否支持分享功能
  const platform = uni.getSystemInfoSync().platform
    showShareModal.value = true
}
  // H5平台不支持uni.share API
  if (platform === 'web') {
    uni.showToast({
      title: 'H5平台暂不支持分享功能',
      icon: 'none'
    })
    return
  }
// 关闭分享模态框
const onShareModalClose = () => {
    showShareModal.value = false
}
  uni.showActionSheet({
    itemList: ['分享给微信好友', '分享到朋友圈'],
    success: res => {
      // 检查uni.share是否存在
      if (typeof uni.share !== 'function') {
        uni.showToast({
          title: '当前环境不支持分享功能',
          icon: 'none'
        })
        return
      }
// 微信好友分享
const shareToWechat = () => {
    shareToPlatform('weixin', 'WXSceneSession')
}
      try {
        switch (res.tapIndex) {
          case 0:
            // 分享给微信好友
            uni.share({
              provider: 'weixin',
              scene: 'WXSceneSession',
              type: 0,
              title: '工单详情',
              summary: workDetailData.value.event_name,
              href: 'https://example.com/workDetail?eventNum=' + workDetailData.value.event_num,
              imageUrl: workDetailData.value.photo_url,
              success: () => {
                uni.showToast({ title: '分享成功', icon: 'success' })
              },
              fail: (err) => {
                console.log('分享失败', err.errMsg)
                uni.showToast({
                  title: err.errMsg,
                  icon: 'none'
                })
              }
            })
            break
          case 1:
            // 分享到朋友圈
            uni.share({
              provider: 'weixin',
              scene: 'WXSceneTimeline',
              type: 0,
              title: workDetailData.value.event_name,
              summary: '查看工单详情',
              href: 'https://example.com/workDetail?eventNum=' + workDetailData.value.event_num,
              imageUrl: workDetailData.value.photo_url,
              success: () => {
                uni.showToast({ title: '分享成功', icon: 'success' })
              },
              fail: (err) => {
                console.log('分享失败', err)
                uni.showToast({
                  title: '分享失败,请检查配置',
                  icon: 'none'
                })
              }
            })
            break
        }
      } catch (error) {
        console.error('分享出错:', error)
        uni.showToast({
          title: '分享功能异常',
          icon: 'none'
        })
      }
    }
  })
// 朋友圈分享
const shareToMoments = () => {
    shareToPlatform('weixin', 'WXSceneTimeline')
}
// 复制链接
const copyLink = async () => {
    const shareUrl = 'https://example.com/workDetail?eventNum=' + workDetailData.value.event_num
    try {
        await uni.setClipboardData({
            data: shareUrl,
        })
        uni.showToast({
            title: '链接已复制',
            icon: 'success',
        })
    } catch (error) {
        console.error('复制失败:', error)
        uni.showToast({
            title: '复制失败,请重试',
            icon: 'none',
        })
    }
    onShareModalClose()
}
// 通用分享平台函数
const shareToPlatform = (provider, scene = '') => {
    // 检查当前平台是否支持分享功能
    const platform = uni.getSystemInfoSync().platform
    // H5平台不支持uni.share API
    if (platform === 'web') {
        uni.showToast({
            title: 'H5平台暂不支持分享功能',
            icon: 'none',
        })
        onShareModalClose()
        return
    }
    // 检查uni.share是否存在
    if (typeof uni.share !== 'function') {
        uni.showToast({
            title: '当前环境不支持分享功能',
            icon: 'none',
        })
        onShareModalClose()
        return
    }
    try {
        const shareConfig = {
            provider,
            type: 0,
            title: workDetailData.value.event_name || '工单详情',
            summary: '查看工单详情',
            href: 'https://example.com/workDetail?eventNum=' + workDetailData.value.event_num,
            imageUrl: workDetailData.value.photo_url,
            success: () => {
                uni.showToast({ title: '分享成功', icon: 'success' })
                onShareModalClose()
            },
            fail: err => {
                console.log('分享失败', err)
                uni.showToast({
                    title: '分享失败,请重试',
                    icon: 'none',
                })
                onShareModalClose()
            },
        }
        // 添加场景参数(如果有)
        if (scene) {
            shareConfig.scene = scene
        }
        uni.share(shareConfig)
    } catch (error) {
        console.error('分享出错:', error)
        uni.showToast({
            title: '分享功能异常',
            icon: 'none',
        })
        onShareModalClose()
    }
}
// 返回按钮点击事件
@@ -422,4 +463,103 @@
    }
  }
}
/* 分享弹出层样式 */
.share-modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.6);
    display: flex;
    justify-content: flex-end;
    align-items: flex-end;
    z-index: 9999;
}
.share-modal-content {
    background-color: #fff;
    border-radius: 20rpx 20rpx 0 0;
    padding: 30rpx;
    width: 100%;
    max-height: 80vh;
    overflow-y: auto;
}
.share-modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 30rpx;
}
.share-modal-title {
    font-size: 36rpx;
    font-weight: bold;
    color: #222324;
    text-align: center;
    flex: 1;
}
.share-modal-close {
    font-size: 60rpx;
    color: #999;
    line-height: 40rpx;
}
.share-modal-body {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
    padding-bottom: 30rpx;
}
.share-modal-footer {
    display: flex;
    justify-content: space-around;
    padding-top: 30rpx;
    border-top: 1px solid #f0f0f0;
}
.share-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin: 20rpx;
    width: 120rpx;
}
.share-icon {
    width: 90rpx;
    height: 90rpx;
    border-radius: 50%;
    margin-bottom: 15rpx;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
}
.share-icon.wechat {
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="%2307C160" d="M18.66 6.86l-1.41-1.42a1 1 0 00-1.42 0l-12 12a1 1 0 000 1.42l1.42 1.42a1 1 0 001.42 0l12-12a1 1 0 000-1.42z"/><path fill="%2307C160" d="M8.5 14.5c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm0-6a2 2 0 100 4 2 2 0 000-4zm7 6c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm0-6a2 2 0 100 4 2 2 0 000-4z"/></svg>');
}
.share-icon.moments {
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="%2307C160" d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 18H6V4h12v16z"/><path fill="%2307C160" d="M12 6c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm0 10c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"/><path fill="%2307C160" d="M10 13.5h4v3h-4z"/></svg>');
}
.share-icon.link {
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="%23666" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>');
}
.share-icon.poster {
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="%23FFA500" d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"/><path fill="%23FFA500" d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></svg>');
}
.share-name {
    font-size: 24rpx;
    color: #666;
    margin-top: 10rpx;
}
</style>
uniapps/work-app/src/utils/websocket.js
@@ -160,8 +160,14 @@
  // 设置消息回调
  setOnMessageCallback(callback) {
    console.log('🔔 WebSocket消息回调已设置')
    this.onMessageCallback = callback
  }
  // 获取当前消息回调(用于检查是否已设置)
  getOnMessageCallback() {
    return this.onMessageCallback
  }
  // 设置连接打开回调
  setOnOpenCallback(callback) {