tea 3 months ago
parent
commit
994262a380

+ 3 - 5
kxmall-admin-ui/src/views/rider/rider/index.vue

@@ -148,11 +148,9 @@
 
         <el-form-item label="等级" prop="level">
           <el-select v-model="form.level" placeholder="请选择骑手等级" clearable>
-            <el-option label="初级骑手" :value="1" />
-            <el-option label="中级骑手" :value="2" />
-            <el-option label="高级骑手" :value="3" />
-            <el-option label="资深骑手" :value="4" />
-            <el-option label="专家骑手" :value="5" />
+            <el-option label="初级" :value="1" />
+            <el-option label="中级" :value="2" />
+            <el-option label="高级" :value="3" />
           </el-select>
         </el-form-item>
 

+ 92 - 0
kxmall-rider-ui/config/websocket.js

@@ -0,0 +1,92 @@
+/**
+ * WebSocket配置文件
+ * 
+ * @author kxmall
+ * @date 2025-01-08
+ */
+
+export default {
+  // WebSocket连接配置
+  connection: {
+    // 最大重连次数
+    maxReconnectAttempts: 5,
+    // 重连间隔(毫秒)
+    reconnectInterval: 5000,
+    // 心跳间隔(毫秒)
+    heartbeatInterval: 30000,
+    // 连接超时时间(毫秒)
+    connectionTimeout: 10000
+  },
+
+  // 消息通知配置
+  notification: {
+    // 是否启用声音提醒
+    enableSound: true,
+    // 是否启用震动提醒
+    enableVibration: true,
+    // 是否启用Toast通知
+    enableToast: true,
+    // 紧急订单是否使用模态框
+    urgentOrderModal: true,
+    // Toast显示时长(毫秒)
+    toastDuration: 3000,
+    // 震动时长(毫秒)
+    vibrationDuration: 500
+  },
+
+  // 消息类型配置
+  messageTypes: {
+    // 新订单消息
+    newOrder: {
+      type: 'NEW_ORDER',
+      title: '新订单通知',
+      sound: true,
+      vibration: true,
+      autoRefresh: true
+    },
+    // 订单取消消息
+    orderCancel: {
+      type: 'ORDER_CANCEL',
+      title: '订单取消',
+      sound: false,
+      vibration: false,
+      autoRefresh: true
+    },
+    // 订单状态更新
+    orderStatusUpdate: {
+      type: 'ORDER_STATUS_UPDATE',
+      title: '订单状态更新',
+      sound: false,
+      vibration: false,
+      autoRefresh: true
+    },
+    // 系统消息
+    systemMessage: {
+      type: 'SYSTEM_MESSAGE',
+      title: '系统消息',
+      sound: false,
+      vibration: false,
+      autoRefresh: false
+    }
+  },
+
+  // 开发环境配置
+  development: {
+    // 是否启用调试日志
+    enableDebugLog: true,
+    // 是否启用性能监控
+    enablePerformanceMonitor: false,
+    // 测试模式
+    testMode: false
+  },
+
+  // 生产环境配置
+  production: {
+    // 是否启用调试日志
+    enableDebugLog: false,
+    // 是否启用性能监控
+    enablePerformanceMonitor: true,
+    // 测试模式
+    testMode: false
+  }
+};

+ 147 - 0
kxmall-rider-ui/mixins/websocketMixin.js

@@ -0,0 +1,147 @@
+/**
+ * WebSocket功能混入
+ * 提供WebSocket连接管理的通用方法和生命周期处理
+ * 
+ * @author kxmall
+ * @date 2025-01-08
+ */
+
+import riderWebSocketManager from '@/utils/websocket.js';
+import { registerMessageHandlers, unregisterMessageHandlers } from '@/utils/messageHandlers.js';
+import { mapState } from 'vuex';
+
+export default {
+  computed: {
+    ...mapState(['token', 'userInfo'])
+  },
+
+  data() {
+    return {
+      // WebSocket连接状态
+      wsConnectionStatus: 'disconnected', // disconnected, connecting, connected, error
+      // 是否启用WebSocket(可以通过配置控制)
+      wsEnabled: true
+    };
+  },
+
+  methods: {
+    /**
+     * 初始化WebSocket连接
+     */
+    initWebSocket() {
+      if (!this.wsEnabled) {
+        console.log('WebSocket功能已禁用');
+        return;
+      }
+
+      if (!this.userInfo || !this.userInfo.state) {
+        console.log('用户未通过审核,不连接WebSocket');
+        return;
+      }
+
+      // 注册消息处理器
+      registerMessageHandlers(riderWebSocketManager, this);
+
+      // 连接WebSocket
+      riderWebSocketManager.connect(this.userInfo, this.$baseUrl);
+      
+      console.log('WebSocket初始化完成');
+    },
+
+    /**
+     * 断开WebSocket连接
+     */
+    disconnectWebSocket() {
+      if (riderWebSocketManager.isConnected()) {
+        unregisterMessageHandlers(riderWebSocketManager);
+        riderWebSocketManager.disconnect();
+        console.log('WebSocket连接已断开');
+      }
+    },
+
+    /**
+     * 重连WebSocket
+     */
+    reconnectWebSocket() {
+      console.log('手动重连WebSocket');
+      riderWebSocketManager.reset();
+      this.disconnectWebSocket();
+      
+      // 延迟一秒后重连
+      setTimeout(() => {
+        this.initWebSocket();
+      }, 1000);
+    },
+
+    /**
+     * 发送WebSocket消息
+     * @param {String} message 消息内容
+     */
+    sendWebSocketMessage(message) {
+      return riderWebSocketManager.send(message);
+    },
+
+    /**
+     * 获取WebSocket连接状态
+     */
+    getWebSocketStatus() {
+      return riderWebSocketManager.isConnected() ? 'connected' : 'disconnected';
+    },
+
+    /**
+     * 设置WebSocket启用状态
+     * @param {Boolean} enabled 是否启用
+     */
+    setWebSocketEnabled(enabled) {
+      this.wsEnabled = enabled;
+      
+      if (enabled) {
+        this.initWebSocket();
+      } else {
+        this.disconnectWebSocket();
+      }
+    }
+  },
+
+  /**
+   * 页面显示时的处理
+   */
+  onShow() {
+    // 如果用户状态发生变化,重新初始化WebSocket
+    if (this.userInfo && this.userInfo.state && this.wsEnabled) {
+      if (!riderWebSocketManager.isConnected()) {
+        console.log('页面显示,重新连接WebSocket');
+        this.initWebSocket();
+      }
+    }
+  },
+
+  /**
+   * 页面隐藏时的处理
+   */
+  onHide() {
+    // 页面隐藏时不断开连接,保持后台接收消息
+    console.log('页面隐藏,WebSocket保持连接');
+  },
+
+  /**
+   * 组件销毁前的清理
+   */
+  beforeDestroy() {
+    this.disconnectWebSocket();
+  },
+
+  /**
+   * 组件销毁后的清理(兼容性)
+   */
+  destroyed() {
+    this.disconnectWebSocket();
+  },
+
+  /**
+   * 页面卸载时的清理
+   */
+  onUnload() {
+    this.disconnectWebSocket();
+  }
+};

+ 4 - 0
kxmall-rider-ui/pages/task/task.vue

@@ -122,6 +122,7 @@
 
 <script>
   import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
+  import websocketMixin from '@/mixins/websocketMixin.js';
   import {
     mapState,
     mapMutations
@@ -131,6 +132,7 @@
     components: {
       uniLoadMore
     },
+    mixins: [websocketMixin],
     computed: {
       ...mapState(['token', 'userInfo'])
     },
@@ -195,6 +197,8 @@
       let that = this;
       if (that.userInfo.state) {
         that.resetData();
+        // 初始化WebSocket连接
+        // that.initWebSocket();
       }
       // 如果已有定时任务,先清除
       if (this.reportInterval) {

+ 186 - 0
kxmall-rider-ui/store/modules/websocket.js

@@ -0,0 +1,186 @@
+/**
+ * WebSocket状态管理模块
+ * 
+ * @author kxmall
+ * @date 2025-01-08
+ */
+
+import riderWebSocketManager from '@/utils/websocket.js';
+
+const state = {
+  // 连接状态
+  connectionStatus: 'disconnected', // disconnected, connecting, connected, error
+  // 在线状态
+  isOnline: false,
+  // 最后连接时间
+  lastConnectedTime: null,
+  // 最后断开时间
+  lastDisconnectedTime: null,
+  // 重连次数
+  reconnectAttempts: 0,
+  // 接收到的消息数量
+  receivedMessageCount: 0,
+  // 最后接收消息时间
+  lastMessageTime: null,
+  // WebSocket配置
+  config: {
+    enabled: true,
+    autoReconnect: true,
+    enableNotifications: true
+  }
+};
+
+const mutations = {
+  // 设置连接状态
+  SET_CONNECTION_STATUS(state, status) {
+    state.connectionStatus = status;
+    state.isOnline = status === 'connected';
+    
+    if (status === 'connected') {
+      state.lastConnectedTime = new Date();
+      state.reconnectAttempts = 0;
+    } else if (status === 'disconnected') {
+      state.lastDisconnectedTime = new Date();
+    }
+  },
+
+  // 增加重连次数
+  INCREMENT_RECONNECT_ATTEMPTS(state) {
+    state.reconnectAttempts++;
+  },
+
+  // 重置重连次数
+  RESET_RECONNECT_ATTEMPTS(state) {
+    state.reconnectAttempts = 0;
+  },
+
+  // 增加接收消息数量
+  INCREMENT_MESSAGE_COUNT(state) {
+    state.receivedMessageCount++;
+    state.lastMessageTime = new Date();
+  },
+
+  // 更新WebSocket配置
+  UPDATE_CONFIG(state, config) {
+    state.config = { ...state.config, ...config };
+  },
+
+  // 重置状态
+  RESET_STATE(state) {
+    state.connectionStatus = 'disconnected';
+    state.isOnline = false;
+    state.reconnectAttempts = 0;
+    state.receivedMessageCount = 0;
+    state.lastConnectedTime = null;
+    state.lastDisconnectedTime = null;
+    state.lastMessageTime = null;
+  }
+};
+
+const actions = {
+  // 连接WebSocket
+  connect({ commit, rootState }) {
+    if (!rootState.userInfo || !rootState.userInfo.state) {
+      console.log('用户未通过审核,无法连接WebSocket');
+      return false;
+    }
+
+    commit('SET_CONNECTION_STATUS', 'connecting');
+    
+    try {
+      riderWebSocketManager.connect(rootState.userInfo, rootState.baseUrl);
+      return true;
+    } catch (error) {
+      console.error('连接WebSocket失败:', error);
+      commit('SET_CONNECTION_STATUS', 'error');
+      return false;
+    }
+  },
+
+  // 断开WebSocket
+  disconnect({ commit }) {
+    riderWebSocketManager.disconnect();
+    commit('SET_CONNECTION_STATUS', 'disconnected');
+  },
+
+  // 重连WebSocket
+  reconnect({ dispatch, commit }) {
+    commit('INCREMENT_RECONNECT_ATTEMPTS');
+    return dispatch('connect');
+  },
+
+  // 发送消息
+  sendMessage({ state }, message) {
+    if (state.connectionStatus !== 'connected') {
+      console.warn('WebSocket未连接,无法发送消息');
+      return false;
+    }
+
+    return riderWebSocketManager.send(message);
+  },
+
+  // 更新配置
+  updateConfig({ commit }, config) {
+    commit('UPDATE_CONFIG', config);
+  },
+
+  // 处理连接状态变化
+  handleStatusChange({ commit }, status) {
+    commit('SET_CONNECTION_STATUS', status);
+  },
+
+  // 处理接收到消息
+  handleMessage({ commit }, message) {
+    commit('INCREMENT_MESSAGE_COUNT');
+  },
+
+  // 重置所有状态
+  reset({ commit }) {
+    commit('RESET_STATE');
+  }
+};
+
+const getters = {
+  // 是否已连接
+  isConnected: state => state.connectionStatus === 'connected',
+  
+  // 是否正在连接
+  isConnecting: state => state.connectionStatus === 'connecting',
+  
+  // 是否有错误
+  hasError: state => state.connectionStatus === 'error',
+  
+  // 连接状态文本
+  statusText: state => {
+    switch (state.connectionStatus) {
+      case 'connected': return '已连接';
+      case 'connecting': return '连接中';
+      case 'disconnected': return '已断开';
+      case 'error': return '连接错误';
+      default: return '未知状态';
+    }
+  },
+
+  // 连接时长(毫秒)
+  connectionDuration: state => {
+    if (state.connectionStatus === 'connected' && state.lastConnectedTime) {
+      return Date.now() - state.lastConnectedTime.getTime();
+    }
+    return 0;
+  },
+
+  // 是否需要重连
+  shouldReconnect: state => {
+    return state.config.autoReconnect && 
+           state.connectionStatus === 'disconnected' && 
+           state.reconnectAttempts < 5;
+  }
+};
+
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+  getters
+};

+ 322 - 0
kxmall-rider-ui/utils/README.md

@@ -0,0 +1,322 @@
+# 骑手端WebSocket工具说明
+
+## 📁 文件结构
+
+```
+utils/
+├── websocket.js          # WebSocket连接管理器
+├── messageHandlers.js    # 消息处理器
+└── README.md            # 说明文档
+
+mixins/
+└── websocketMixin.js    # WebSocket功能混入
+
+config/
+└── websocket.js         # WebSocket配置文件
+
+store/modules/
+└── websocket.js         # WebSocket状态管理
+```
+
+## 🚀 快速开始
+
+### 1. 在页面中使用WebSocket功能
+
+```javascript
+// 在页面组件中引入mixin
+import websocketMixin from '@/mixins/websocketMixin.js';
+
+export default {
+  mixins: [websocketMixin],
+  
+  onLoad() {
+    // mixin会自动处理WebSocket连接
+    // 如果用户已通过审核,会自动连接WebSocket
+  }
+}
+```
+
+### 2. 自定义消息处理
+
+```javascript
+// 在页面中重写消息处理方法
+export default {
+  mixins: [websocketMixin],
+  
+  methods: {
+    // 重写新订单通知处理
+    handleNewOrderNotification(message) {
+      console.log('自定义处理新订单:', message);
+      // 自定义逻辑
+      this.showCustomOrderDialog(message);
+    }
+  }
+}
+```
+
+### 3. 手动控制WebSocket
+
+```javascript
+export default {
+  mixins: [websocketMixin],
+  
+  methods: {
+    // 手动连接
+    connectManually() {
+      this.initWebSocket();
+    },
+    
+    // 手动断开
+    disconnectManually() {
+      this.disconnectWebSocket();
+    },
+    
+    // 重连
+    reconnectManually() {
+      this.reconnectWebSocket();
+    },
+    
+    // 发送消息
+    sendTestMessage() {
+      this.sendWebSocketMessage('test message');
+    },
+    
+    // 检查连接状态
+    checkStatus() {
+      const status = this.getWebSocketStatus();
+      console.log('WebSocket状态:', status);
+    }
+  }
+}
+```
+
+## 🔧 配置说明
+
+### WebSocket配置 (config/websocket.js)
+
+```javascript
+export default {
+  connection: {
+    maxReconnectAttempts: 5,    // 最大重连次数
+    reconnectInterval: 5000,    // 重连间隔(ms)
+    heartbeatInterval: 30000,   // 心跳间隔(ms)
+    connectionTimeout: 10000    // 连接超时(ms)
+  },
+  
+  notification: {
+    enableSound: true,          // 启用声音提醒
+    enableVibration: true,      // 启用震动提醒
+    enableToast: true,          // 启用Toast通知
+    urgentOrderModal: true      // 紧急订单使用模态框
+  }
+}
+```
+
+### 消息类型常量
+
+```javascript
+import { MESSAGE_TYPES } from '@/utils/websocket.js';
+
+// 可用的消息类型
+MESSAGE_TYPES.NEW_ORDER           // 新订单
+MESSAGE_TYPES.ORDER_CANCEL        // 订单取消
+MESSAGE_TYPES.ORDER_STATUS_UPDATE // 订单状态更新
+MESSAGE_TYPES.SYSTEM_MESSAGE      // 系统消息
+```
+
+## 📡 API参考
+
+### WebSocket管理器 (utils/websocket.js)
+
+#### 主要方法
+
+```javascript
+import riderWebSocketManager from '@/utils/websocket.js';
+
+// 连接WebSocket
+riderWebSocketManager.connect(userInfo, baseUrl);
+
+// 断开连接
+riderWebSocketManager.disconnect();
+
+// 发送消息
+riderWebSocketManager.send('message');
+
+// 检查连接状态
+riderWebSocketManager.isConnected();
+
+// 注册消息处理器
+riderWebSocketManager.onMessage('NEW_ORDER', (message) => {
+  console.log('收到新订单:', message);
+});
+
+// 注册状态变化处理器
+riderWebSocketManager.onStatusChange((status, data) => {
+  console.log('状态变化:', status);
+});
+```
+
+### 消息处理器 (utils/messageHandlers.js)
+
+#### 处理器函数
+
+```javascript
+import { 
+  handleNewOrderNotification,
+  handleOrderCancelNotification,
+  handleSystemMessage,
+  registerMessageHandlers,
+  unregisterMessageHandlers
+} from '@/utils/messageHandlers.js';
+
+// 注册所有处理器
+registerMessageHandlers(wsManager, this);
+
+// 注销所有处理器
+unregisterMessageHandlers(wsManager);
+```
+
+### WebSocket混入 (mixins/websocketMixin.js)
+
+#### 提供的方法
+
+```javascript
+// 混入提供的方法
+this.initWebSocket()          // 初始化连接
+this.disconnectWebSocket()    // 断开连接
+this.reconnectWebSocket()     // 重连
+this.sendWebSocketMessage()   // 发送消息
+this.getWebSocketStatus()     // 获取状态
+this.setWebSocketEnabled()    // 启用/禁用
+```
+
+#### 生命周期处理
+
+- `onShow`: 页面显示时自动检查并重连
+- `onHide`: 页面隐藏时保持连接
+- `beforeDestroy`: 组件销毁前清理资源
+- `onUnload`: 页面卸载时断开连接
+
+## 🎯 使用场景
+
+### 场景1: 基础使用
+
+```javascript
+// 只需要基本的WebSocket功能
+export default {
+  mixins: [websocketMixin]
+  // 就这么简单!
+}
+```
+
+### 场景2: 自定义通知
+
+```javascript
+export default {
+  mixins: [websocketMixin],
+  
+  methods: {
+    handleNewOrderNotification(message) {
+      // 自定义新订单处理逻辑
+      if (message.urgencyLevel >= 3) {
+        // 特急订单特殊处理
+        this.playUrgentSound();
+        this.showFullScreenAlert(message);
+      } else {
+        // 普通订单
+        this.showToast(message.orderNo);
+      }
+    }
+  }
+}
+```
+
+### 场景3: 状态监控
+
+```javascript
+export default {
+  mixins: [websocketMixin],
+  
+  data() {
+    return {
+      wsStatus: '未连接'
+    }
+  },
+  
+  methods: {
+    handleConnectionStatusChange(status) {
+      this.wsStatus = status;
+      // 更新UI状态显示
+    }
+  }
+}
+```
+
+## 🐛 调试和故障排除
+
+### 1. 启用调试日志
+
+在开发环境中,WebSocket工具会自动输出详细的调试信息到控制台。
+
+### 2. 常见问题
+
+**Q: WebSocket连接失败**
+```javascript
+// 检查用户状态
+if (!this.userInfo || !this.userInfo.state) {
+  console.log('用户未通过审核');
+}
+
+// 检查网络连接
+// 检查服务器地址配置
+```
+
+**Q: 消息处理器不工作**
+```javascript
+// 确保正确注册了处理器
+registerMessageHandlers(riderWebSocketManager, this);
+
+// 检查消息类型是否匹配
+console.log('消息类型:', message.type);
+```
+
+**Q: 自动重连不工作**
+```javascript
+// 检查重连配置
+riderWebSocketManager.reset(); // 重置重连状态
+this.reconnectWebSocket();      // 手动重连
+```
+
+### 3. 性能监控
+
+```javascript
+// 检查连接状态
+console.log('连接状态:', this.getWebSocketStatus());
+
+// 查看重连次数
+console.log('重连次数:', riderWebSocketManager.reconnectAttempts);
+
+// 监控消息处理性能
+performance.mark('message-start');
+// 处理消息...
+performance.mark('message-end');
+performance.measure('message-processing', 'message-start', 'message-end');
+```
+
+## 📈 最佳实践
+
+1. **使用mixin**: 推荐使用websocketMixin,它处理了大部分生命周期管理
+2. **自定义处理**: 根据业务需求重写消息处理方法
+3. **错误处理**: 总是处理连接错误和消息解析错误
+4. **性能优化**: 避免在消息处理中执行耗时操作
+5. **用户体验**: 合理使用声音、震动等提醒方式
+6. **状态管理**: 使用Vuex模块管理WebSocket状态(可选)
+
+## 🔄 版本更新
+
+### v1.0.0
+- 基础WebSocket连接管理
+- 消息处理器系统
+- Vue混入支持
+- 自动重连机制
+- 心跳保活功能

+ 238 - 0
kxmall-rider-ui/utils/messageHandlers.js

@@ -0,0 +1,238 @@
+/**
+ * WebSocket消息处理器
+ * 统一管理不同类型消息的处理逻辑
+ * 
+ * @author kxmall
+ * @date 2025-01-08
+ */
+
+import { MESSAGE_TYPES } from './websocket.js';
+
+/**
+ * 新订单通知处理器
+ * @param {Object} message 消息对象
+ * @param {Object} context 上下文对象(通常是Vue组件实例)
+ */
+export function handleNewOrderNotification(message, context) {
+  console.log('收到新订单通知:', message);
+  
+  // 显示通知
+  uni.showToast({
+    title: `新订单:${message.orderNo}`,
+    icon: 'none',
+    duration: 3000
+  });
+
+  // 播放提示音
+  // #ifdef APP-PLUS
+  if (plus && plus.device) {
+    plus.device.beep();
+  }
+  // #endif
+
+  // 尝试震动提醒
+  // #ifdef APP-PLUS
+  if (plus && plus.device) {
+    plus.device.vibrate(500);
+  }
+  // #endif
+  
+  // #ifndef APP-PLUS
+  // H5和小程序使用uni-app的API
+  try {
+    uni.vibrateShort({
+      success: () => {
+        console.log('震动成功');
+      },
+      fail: (err) => {
+        console.log('震动失败:', err);
+      }
+    });
+  } catch (error) {
+    console.log('震动API不支持:', error);
+  }
+  // #endif
+
+  // 如果上下文存在且有刷新方法,则刷新订单列表
+  if (context && typeof context.resetData === 'function') {
+    context.resetData();
+  }
+
+  // 显示详细信息弹窗(可选)
+  if (message.urgencyLevel >= 2) { // 紧急或特急订单
+    showOrderDetailModal(message, context);
+  }
+}
+
+/**
+ * 订单取消通知处理器
+ * @param {Object} message 消息对象
+ * @param {Object} context 上下文对象
+ */
+export function handleOrderCancelNotification(message, context) {
+  console.log('收到订单取消通知:', message);
+  
+  uni.showToast({
+    title: `订单${message.orderNo}已取消`,
+    icon: 'none',
+    duration: 2000
+  });
+
+  // 刷新订单列表
+  if (context && typeof context.resetData === 'function') {
+    context.resetData();
+  }
+}
+
+/**
+ * 订单状态更新处理器
+ * @param {Object} message 消息对象
+ * @param {Object} context 上下文对象
+ */
+export function handleOrderStatusUpdate(message, context) {
+  console.log('收到订单状态更新:', message);
+  
+  uni.showToast({
+    title: `订单${message.orderNo}状态更新`,
+    icon: 'none',
+    duration: 2000
+  });
+
+  // 刷新订单列表
+  if (context && typeof context.resetData === 'function') {
+    context.resetData();
+  }
+}
+
+/**
+ * 系统消息处理器
+ * @param {Object} message 消息对象
+ * @param {Object} context 上下文对象
+ */
+export function handleSystemMessage(message, context) {
+  console.log('收到系统消息:', message);
+  
+  const title = message.remark || '系统消息';
+  
+  // 重要系统消息使用模态框显示
+  if (message.urgencyLevel >= 2) {
+    uni.showModal({
+      title: '系统通知',
+      content: title,
+      showCancel: false,
+      confirmText: '知道了'
+    });
+  } else {
+    // 普通消息使用Toast显示
+    uni.showToast({
+      title: title,
+      icon: 'none',
+      duration: 3000
+    });
+  }
+}
+
+/**
+ * 显示订单详情弹窗
+ * @param {Object} orderData 订单数据
+ * @param {Object} context 上下文对象
+ */
+function showOrderDetailModal(orderData, context) {
+  const content = `
+订单号:${orderData.orderNo || ''}
+配送费:¥${orderData.freightPrice || '0'}
+收货人:${orderData.consignee || ''}
+地址:${orderData.address || ''}
+匹配度:${orderData.matchScore || 0}分
+${orderData.remark ? '备注:' + orderData.remark : ''}
+  `.trim();
+
+  uni.showModal({
+    title: '紧急订单通知',
+    content: content,
+    showCancel: true,
+    cancelText: '稍后处理',
+    confirmText: '立即查看',
+    success: (res) => {
+      if (res.confirm && context) {
+        // 跳转到订单详情或刷新页面
+        if (typeof context.resetData === 'function') {
+          context.resetData();
+        }
+        // 可以在这里添加跳转到订单详情的逻辑
+        // uni.navigateTo({ url: `/pages/order/detail?id=${orderData.orderId}` });
+      }
+    }
+  });
+}
+
+/**
+ * WebSocket连接状态变化处理器
+ * @param {String} status 连接状态
+ * @param {Object} data 附加数据
+ * @param {Object} context 上下文对象
+ */
+export function handleConnectionStatusChange(status, data, context) {
+  console.log('WebSocket连接状态变化:', status, data);
+  
+  switch (status) {
+    case 'connected':
+      console.log('WebSocket连接成功');
+      // 可以在这里更新UI状态
+      break;
+      
+    case 'disconnected':
+      console.log('WebSocket连接断开');
+      // 可以显示离线状态
+      break;
+      
+    case 'error':
+      console.error('WebSocket连接错误:', data);
+      // 可以显示错误提示
+      break;
+      
+    default:
+      console.log('未知连接状态:', status);
+  }
+}
+
+/**
+ * 注册所有消息处理器到WebSocket管理器
+ * @param {Object} wsManager WebSocket管理器实例
+ * @param {Object} context 上下文对象
+ */
+export function registerMessageHandlers(wsManager, context) {
+  // 注册各种消息类型的处理器
+  wsManager.onMessage(MESSAGE_TYPES.NEW_ORDER, (message) => {
+    handleNewOrderNotification(message, context);
+  });
+
+  wsManager.onMessage(MESSAGE_TYPES.ORDER_CANCEL, (message) => {
+    handleOrderCancelNotification(message, context);
+  });
+
+  wsManager.onMessage(MESSAGE_TYPES.ORDER_STATUS_UPDATE, (message) => {
+    handleOrderStatusUpdate(message, context);
+  });
+
+  wsManager.onMessage(MESSAGE_TYPES.SYSTEM_MESSAGE, (message) => {
+    handleSystemMessage(message, context);
+  });
+
+  // 注册连接状态变化处理器
+  wsManager.onStatusChange((status, data) => {
+    handleConnectionStatusChange(status, data, context);
+  });
+}
+
+/**
+ * 注销所有消息处理器
+ * @param {Object} wsManager WebSocket管理器实例
+ */
+export function unregisterMessageHandlers(wsManager) {
+  wsManager.offMessage(MESSAGE_TYPES.NEW_ORDER);
+  wsManager.offMessage(MESSAGE_TYPES.ORDER_CANCEL);
+  wsManager.offMessage(MESSAGE_TYPES.ORDER_STATUS_UPDATE);
+  wsManager.offMessage(MESSAGE_TYPES.SYSTEM_MESSAGE);
+  wsManager.clearCallbacks();
+}

+ 321 - 0
kxmall-rider-ui/utils/websocket.js

@@ -0,0 +1,321 @@
+/**
+ * 骑手端WebSocket管理工具
+ * 统一管理WebSocket连接、消息处理和状态维护
+ * 
+ * @author kxmall
+ * @date 2025-01-08
+ */
+
+class RiderWebSocketManager {
+  constructor() {
+    this.websocket = null;
+    this.heartbeatTimer = null;
+    this.reconnectTimer = null;
+    this.isManualClose = false; // 是否手动关闭
+    this.reconnectAttempts = 0; // 重连尝试次数
+    this.maxReconnectAttempts = 5; // 最大重连次数
+    this.reconnectInterval = 5000; // 重连间隔
+    this.heartbeatInterval = 30000; // 心跳间隔
+    
+    // 消息处理回调
+    this.onMessageCallbacks = new Map();
+    this.onStatusChangeCallback = null;
+    
+    // 绑定方法上下文
+    this.connect = this.connect.bind(this);
+    this.disconnect = this.disconnect.bind(this);
+    this.send = this.send.bind(this);
+    this.handleMessage = this.handleMessage.bind(this);
+  }
+
+  /**
+   * 连接WebSocket
+   * @param {Object} userInfo 用户信息
+   * @param {String} baseUrl 基础URL
+   */
+  connect(userInfo, baseUrl) {
+    if (!userInfo || !userInfo.id) {
+      console.error('用户信息不存在,无法连接WebSocket');
+      return;
+    }
+
+    if (this.websocket) {
+      console.warn('WebSocket已存在,先断开再重连');
+      this.disconnect();
+    }
+
+    // 构建WebSocket连接地址
+    const socketUrl = `ws://${baseUrl.replace('http://', '').replace('https://', '')}/websocket/rider/${userInfo.id}`;
+    console.log('连接WebSocket:', socketUrl);
+
+    try {
+      this.websocket = uni.connectSocket({
+        url: socketUrl,
+        success: () => {
+          console.log('WebSocket连接请求发送成功');
+        },
+        fail: (err) => {
+          console.error('WebSocket连接请求失败:', err);
+          this._scheduleReconnect(userInfo, baseUrl);
+        }
+      });
+
+      this._setupEventHandlers(userInfo, baseUrl);
+      
+    } catch (error) {
+      console.error('创建WebSocket连接失败:', error);
+      this._scheduleReconnect(userInfo, baseUrl);
+    }
+  }
+
+  /**
+   * 设置事件处理器
+   * @private
+   */
+  _setupEventHandlers(userInfo, baseUrl) {
+    if (!this.websocket) return;
+
+    // 监听连接打开
+    this.websocket.onOpen(() => {
+      console.log('WebSocket连接已打开');
+      this.reconnectAttempts = 0; // 重置重连次数
+      this._startHeartbeat();
+      this._notifyStatusChange('connected');
+    });
+
+    // 监听消息
+    this.websocket.onMessage((res) => {
+      console.log('收到WebSocket消息:', res.data);
+      this.handleMessage(res.data);
+    });
+
+    // 监听错误
+    this.websocket.onError((err) => {
+      console.error('WebSocket发生错误:', err);
+      this._notifyStatusChange('error', err);
+    });
+
+    // 监听关闭
+    this.websocket.onClose((res) => {
+      console.log('WebSocket连接关闭:', res);
+      this._stopHeartbeat();
+      this._notifyStatusChange('disconnected');
+      
+      // 如果不是手动关闭且用户仍然有效,则尝试重连
+      if (!this.isManualClose && userInfo && userInfo.state) {
+        this._scheduleReconnect(userInfo, baseUrl);
+      }
+    });
+  }
+
+  /**
+   * 断开WebSocket连接
+   */
+  disconnect() {
+    this.isManualClose = true;
+    this._stopHeartbeat();
+    this._stopReconnect();
+    
+    if (this.websocket) {
+      this.websocket.close();
+      this.websocket = null;
+    }
+    
+    console.log('WebSocket手动断开');
+    this._notifyStatusChange('disconnected');
+  }
+
+  /**
+   * 发送消息
+   * @param {String} message 消息内容
+   */
+  send(message) {
+    if (!this.websocket) {
+      console.warn('WebSocket未连接,无法发送消息');
+      return false;
+    }
+
+    this.websocket.send({
+      data: message,
+      success: () => {
+        console.log('WebSocket消息发送成功:', message);
+      },
+      fail: (err) => {
+        console.error('WebSocket消息发送失败:', err);
+      }
+    });
+
+    return true;
+  }
+
+  /**
+   * 处理接收到的消息
+   * @param {String} data 消息数据
+   */
+  handleMessage(data) {
+    try {
+      // 处理心跳响应
+      if (data === '连接成功' || data === 'pong') {
+        return;
+      }
+
+      // 解析JSON消息
+      const message = typeof data === 'string' ? JSON.parse(data) : data;
+      
+      // 调用对应的消息处理回调
+      const callback = this.onMessageCallbacks.get(message.type);
+      if (callback && typeof callback === 'function') {
+        callback(message);
+      } else {
+        console.warn('未找到消息类型处理器:', message.type);
+      }
+      
+    } catch (error) {
+      console.error('处理WebSocket消息失败:', error);
+    }
+  }
+
+  /**
+   * 注册消息处理回调
+   * @param {String} messageType 消息类型
+   * @param {Function} callback 回调函数
+   */
+  onMessage(messageType, callback) {
+    this.onMessageCallbacks.set(messageType, callback);
+  }
+
+  /**
+   * 注册状态变化回调
+   * @param {Function} callback 回调函数
+   */
+  onStatusChange(callback) {
+    this.onStatusChangeCallback = callback;
+  }
+
+  /**
+   * 移除消息处理回调
+   * @param {String} messageType 消息类型
+   */
+  offMessage(messageType) {
+    this.onMessageCallbacks.delete(messageType);
+  }
+
+  /**
+   * 清除所有回调
+   */
+  clearCallbacks() {
+    this.onMessageCallbacks.clear();
+    this.onStatusChangeCallback = null;
+  }
+
+  /**
+   * 获取连接状态
+   */
+  isConnected() {
+    return this.websocket !== null;
+  }
+
+  /**
+   * 开始心跳
+   * @private
+   */
+  _startHeartbeat() {
+    this._stopHeartbeat();
+    this.heartbeatTimer = setInterval(() => {
+      this.send('ping');
+    }, this.heartbeatInterval);
+  }
+
+  /**
+   * 停止心跳
+   * @private
+   */
+  _stopHeartbeat() {
+    if (this.heartbeatTimer) {
+      clearInterval(this.heartbeatTimer);
+      this.heartbeatTimer = null;
+    }
+  }
+
+  /**
+   * 计划重连
+   * @private
+   */
+  _scheduleReconnect(userInfo, baseUrl) {
+    if (this.isManualClose || this.reconnectAttempts >= this.maxReconnectAttempts) {
+      console.log('不再尝试重连');
+      return;
+    }
+
+    this._stopReconnect();
+    
+    this.reconnectAttempts++;
+    const delay = this.reconnectInterval * this.reconnectAttempts;
+    
+    console.log(`${delay/1000}秒后尝试第${this.reconnectAttempts}次重连`);
+    
+    this.reconnectTimer = setTimeout(() => {
+      console.log(`开始第${this.reconnectAttempts}次重连`);
+      this.connect(userInfo, baseUrl);
+    }, delay);
+  }
+
+  /**
+   * 停止重连
+   * @private
+   */
+  _stopReconnect() {
+    if (this.reconnectTimer) {
+      clearTimeout(this.reconnectTimer);
+      this.reconnectTimer = null;
+    }
+  }
+
+  /**
+   * 通知状态变化
+   * @private
+   */
+  _notifyStatusChange(status, data = null) {
+    if (this.onStatusChangeCallback && typeof this.onStatusChangeCallback === 'function') {
+      this.onStatusChangeCallback(status, data);
+    }
+  }
+
+  /**
+   * 重置连接状态(用于手动重连)
+   */
+  reset() {
+    this.isManualClose = false;
+    this.reconnectAttempts = 0;
+  }
+
+  /**
+   * 销毁实例
+   */
+  destroy() {
+    this.disconnect();
+    this.clearCallbacks();
+    this._stopReconnect();
+  }
+}
+
+// 消息类型常量
+export const MESSAGE_TYPES = {
+  NEW_ORDER: 'NEW_ORDER',
+  ORDER_CANCEL: 'ORDER_CANCEL',
+  ORDER_STATUS_UPDATE: 'ORDER_STATUS_UPDATE',
+  SYSTEM_MESSAGE: 'SYSTEM_MESSAGE'
+};
+
+// 连接状态常量
+export const CONNECTION_STATUS = {
+  CONNECTING: 'connecting',
+  CONNECTED: 'connected',
+  DISCONNECTED: 'disconnected',
+  ERROR: 'error'
+};
+
+// 导出单例实例
+const riderWebSocketManager = new RiderWebSocketManager();
+
+export default riderWebSocketManager;