微信小步调中使用WebSocket通讯

十念  论坛元老 | 2025-3-26 00:10:16 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1613|帖子 1613|积分 4839

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
一、在utils文件夹下新建websocket.js文件,用来封装websocket的连接以及生命周期非法:
  1. const app = getApp()
  2. // 域名地址(项目实地址)
  3. const Host = 'wss://fczd.hkbtwx.com/websocket/ws/';
  4. // Socket连接成功
  5. var socketOpen = false;
  6. // Socket关闭
  7. var socketClose = false;
  8. // 消息队列
  9. var socketMsgQueue = [];
  10. // 判断心跳变量
  11. var heart = null;
  12. // 心跳失败次数
  13. var heartBeatFailCount = 0;
  14. // 终止心跳
  15. var heartBeatTimeout = null;
  16. // 终止重连
  17. var connectSocketTimeout = null;
  18. var webSocket = {
  19.   // 连接Socket
  20.   connectSocket:function(options) {
  21.     if (socketOpen) return
  22.     // wx.showLoading({
  23.     //   title: 'Socket连接中...',
  24.     //   mask: true
  25.     // });
  26.     socketOpen = false;
  27.     socketClose = false;
  28.     socketMsgQueue = [];
  29.     let url = Host + app.globalData.userInfo.code
  30.     wx.connectSocket({
  31.       url: url,
  32.       header:{
  33.         'content-type': 'application/json'
  34.       },
  35.       success:function(res) {
  36.         console.log('链接成功')
  37.         if (options) {
  38.           options.success && options.success(res);
  39.         }
  40.       },
  41.       fail:function(res) {
  42.         if (options) {
  43.           options.fail && options.fail(res);
  44.         }
  45.       }
  46.     })
  47.   },
  48.   // 发送消息
  49.   sendSocketMessage:function(options) {
  50.     if (socketOpen) {
  51.       wx.sendSocketMessage({
  52.         data: options.msg,
  53.         success: function(res) {
  54.           if (options) {
  55.             options.success && options.success(res);
  56.           }
  57.         },
  58.         fail: function(res) {
  59.           if (options) {
  60.             options.fail && options.fail(res);
  61.           }
  62.         }
  63.       })
  64.     } else {
  65.       socketMsgQueue.push(options.msg)
  66.     }
  67.   },
  68.   // 关闭Socket
  69.   closeSocket: function(options) {
  70.     if (connectSocketTimeout) {
  71.       clearTimeout(connectSocketTimeout);
  72.       connectSocketTimeout = null;
  73.     };
  74.     socketClose = true;
  75.     this.stopHeartBeat();
  76.     wx.closeSocket({
  77.       success: function(res) {
  78.         if (options) {
  79.           options.success && options.success(res);
  80.         }
  81.       },
  82.       fail: function(res) {
  83.         if (options) {
  84.           options.fail && options.fail(res);
  85.         }
  86.       }
  87.     })
  88.   },
  89.   // 收到消息
  90.   onSocketMessageCallback: function(msg) {},
  91.   // 开始心跳
  92.   startHeartBeat: function() {
  93.     heart = true;
  94.     this.heartBeat();
  95.   },
  96.   // 正在心跳
  97.   heartBeat: function() {
  98.     var that = this;
  99.     if (!heart) {
  100.       return;
  101.     };
  102.     that.sendSocketMessage({
  103.       msg: JSON.stringify({
  104.         // 与后端约定,传点消息,保持链接
  105.         'message': 'ping',
  106.         'toUserId': app.globalData.userInfo.code
  107.       }),
  108.       success: function(res) {
  109.         if (heart) {
  110.           heartBeatTimeout = setTimeout(() => {
  111.             that.heartBeat();
  112.           }, 8000);
  113.         }
  114.       },
  115.       fail: function(res) {
  116.         if (heartBeatFailCount > 2) {
  117.           that.connectSocket();
  118.         };
  119.         if (heart) {
  120.           heartBeatTimeout = setTimeout(() => {
  121.             that.heartBeat();
  122.           }, 8000);
  123.         };
  124.         heartBeatFailCount++;
  125.       }
  126.     });
  127.   },
  128.   // 结束心跳
  129.   stopHeartBeat: function() {
  130.     heart = false;
  131.     if (heartBeatTimeout) {
  132.       clearTimeout(heartBeatTimeout);
  133.       heartBeatTimeout = null;
  134.     };
  135.     if (connectSocketTimeout) {
  136.       clearTimeout(connectSocketTimeout);
  137.       connectSocketTimeout = null;
  138.     }
  139.   }
  140. };
  141. // 监听WebSocket打开连接
  142. wx.onSocketOpen(function(res) {
  143.   wx.hideLoading();
  144.   // 如果已经关闭socket
  145.   if (socketClose) {
  146.     webSocket.closeSocket();
  147.   } else {
  148.     socketOpen = true
  149.     for (var i = 0; i < socketMsgQueue.length; i++) {
  150.       webSocket.sendSocketMessage(socketMsgQueue[i])
  151.     };
  152.     socketMsgQueue = []
  153.     webSocket.startHeartBeat();
  154.   }
  155. });
  156. // 监听WebSocket错误
  157. wx.onSocketError(function(res) {
  158.   console.log('WebSocket连接打开失败,请检查!', res);
  159.   wx.hideLoading();
  160.   // wx.showToast({
  161.   //   title: 'Socket连接失败:' + JSON.stringify(res),
  162.   //   icon: 'none',
  163.   //   duration: 3000
  164.   // })
  165. });
  166. // 监听WebSocket接受到服务器的消息
  167. wx.onSocketMessage(function(res) {
  168.   console.log(res.data)
  169.   webSocket.onSocketMessageCallback(res.data);
  170. });
  171. // 监听WebSocket关闭连接后重连
  172. wx.onSocketClose(function(res) {
  173.   if (!socketClose) {
  174.     clearTimeout(connectSocketTimeout);
  175.     connectSocketTimeout = setTimeout(() => {
  176.       webSocket.connectSocket();
  177.     }, 10000);
  178.   }
  179. });
  180. module.exports = webSocket;
复制代码
二、在须要吸收websocket消息的页面引入:
  1. const app = getApp()
  2. const $api = require("../../utils/api.js")
  3. const myRequest = require('../../utils/request.js')
  4. let navBarTitleText = ''
  5. const WebSocket = require('../../utils/websocket.js')
  6. Page({
  7.   data: {
  8.     isBindWx: false,
  9.     showLoginPop: false,
  10.     userInfo: {},
  11.     statisticsData: {}, //统计数据
  12.     avatarUrl: '',
  13.     // 登录页面相关参数
  14.     avHeight: '',
  15.     navTop: '',
  16.     navHeight: '',
  17.     loading: false,
  18.     isOut: false,
  19.     isLogin: false,
  20.     message: '',
  21.     messageCode: ''
  22.   },
  23.   onLoad: function(options) {
  24.     if (wx.getStorageSync('isOut')) {
  25.       this.setData({
  26.         isOut: true
  27.       })
  28.       wx.setStorageSync('isOut', '')
  29.     }
  30.   },
  31.   onShow: function() {
  32.     if (typeof this.getTabBar === 'function' && this.getTabBar()) {
  33.       this.getTabBar().setData({
  34.         selected: 0,
  35.         show: app.globalData.isLogin
  36.       })
  37.     }
  38.     // 打开调试
  39.     wx.setEnableDebug({
  40.       enableDebug: false
  41.     })
  42.     wx.setNavigationBarTitle({
  43.       title: navBarTitleText
  44.     })
  45.     this.setData({
  46.       isBindWx: app.globalData.isBindWx,
  47.       userInfo: app.globalData.userInfo,
  48.       avatarUrl: app.globalData.avatarUrl,
  49.       navHeight: app.globalData.navHeight,
  50.       navTop: app.globalData.navTop,
  51.       loading: false,
  52.       isLogin: app.globalData.isLogin,
  53.       navBarTitleText: app.globalData.isLogin ? '首页' : '登录'
  54.     })
  55.     if (app.globalData.isLogin) {
  56.       this.getStatistics()
  57.     }
  58.     // 设置接收消息回调
  59.     WebSocket.onSocketMessageCallback = this.onSocketMessageCallback
  60.   },
  61.   // 用户信息
  62.   onUserInfo() {
  63.     let that = this
  64.     if (app.globalData.avatarUrl) {
  65.       wx.navigateTo({
  66.         url: '/pages/userInfo/index'
  67.       })
  68.       return
  69.     }
  70.     wx.getUserProfile({
  71.       desc: '用于展示用户信息',
  72.       success: (res) => {
  73.         that.setData({
  74.           avatarUrl: res.userInfo.avatarUrl
  75.         })
  76.         app.globalData.avatarUrl = res.userInfo.avatarUrl
  77.         wx.navigateTo({
  78.           url: '/pages/userInfo/index'
  79.         })
  80.         console.log('---------------' + res.userInfo.nickName)
  81.         that.uploadPhoto(that.data.avatarUrl, res.userInfo.nickName)
  82.       }
  83.     })
  84.   },
  85.   // 上传微信头像
  86.   uploadPhoto(url, nickName) {
  87.     myRequest.request({
  88.       url: $api.baseUrl + $api.uploadPhoto,
  89.       method: 'POST',
  90.       data: {
  91.         photo: url,
  92.         wxName: nickName,
  93.         way: '1'
  94.       },
  95.       success: res => {
  96.         if (res.data.code != '0') {
  97.           wx.showToast({
  98.             title: res.data.msg,
  99.             duration: 3000,
  100.             icon: 'none'
  101.           })
  102.         }
  103.       }
  104.     })
  105.   },
  106.   // 订阅消息
  107.   onSubscribeMessage() {
  108.     wx.requestSubscribeMessage({
  109.       tmplIds: ['hkKPWSfweqkac20HAD2rlvweeV6vsuNysxKqibtECl8', 'eJ5j3aLYIO_rRax1dcKPrIaXAz99kmdxurSYX29CDbY'],
  110.       complete: messageData => {
  111.         app.globalData.isFirstOperation = false
  112.         wx.showToast({
  113.           title: '消息订阅成功',
  114.           duration: 2000,
  115.           icon: 'success'
  116.         })
  117.       }
  118.     })
  119.   },
  120.   // 拒绝绑定微信
  121.   onRefuse() {
  122.     this.setData({
  123.       showLoginPop: false
  124.     })
  125.   },
  126.   // 同意绑定微信
  127.   onAgree() {
  128.     let that = this
  129.     wx.login({
  130.       success(res) {
  131.         if (res.code) {
  132.           // todo 发送 res.code 到后台换取 openId, sessionKey, unionId
  133.           app.globalData.isBindWx = true
  134.           that.setData({
  135.             showLoginPop: false,
  136.             isBindWx: true
  137.           })
  138.         } else {
  139.           wx.showToast({
  140.             title: '登录失败!' + res.errMsg,
  141.             duration: 3000,
  142.             icon: 'none'
  143.           })
  144.         }
  145.       }
  146.     })
  147.   },
  148.   /**
  149.    * 登录相关
  150.    */
  151.   loginSucccess(e) {
  152.     this.getTabBar().setData({
  153.       show: true
  154.     })
  155.     // 创建WebSocket连接
  156.     WebSocket.connectSocket()
  157.     if (this.data.isOut) {
  158.       wx.switchTab({
  159.         url: '/pages/news/index'
  160.       })
  161.       this.setData({
  162.         isOut: false
  163.       })
  164.     } else {
  165.       this.setData({
  166.         loading: false,
  167.         isLogin: true,
  168.         userInfo: e.detail,
  169.         navBarTitleText: '首页'
  170.       })
  171.       // 获取数据
  172.       // sthis.getStatistics()
  173.       wx.setNavigationBarTitle({
  174.         title: '首页'
  175.       })
  176.     }
  177.   },
  178.   // Socket收到的信息
  179.   onSocketMessageCallback: function(res) {
  180.     let message = JSON.parse(res)
  181.     if (message.message == '连接成功' || message.message == 'ping') return
  182.     this.setData({
  183.       message: message.message,
  184.       messageCode: message.code
  185.     })
  186.     setTimeout(() => {
  187.       this.setData({
  188.         message: '',
  189.         messageCode: ''
  190.       })
  191.     }, 10000)
  192.   },
  193.   // 页面销毁时关闭连接
  194.   onUnload: function(options) {
  195.     WebSocket.closeSocket();
  196.   }
  197. })
复制代码
三、以上便是微信小步调中WebSocket的封装和使用;下面再先容单Activity多Fragment的Android项目中使用WebSocket举行通讯:
3.1 编写WebSocket工具类:
  1. public class MyWsManager {
  2.     private static final String URL = SystemConst.WS_URL + "?appKey=" + CommonUtils.getDeviceNumber();
  3.     private static MyWsManager myWsManager;
  4.     private static WsManager wsManager;
  5.     protected Logger logger = Logger.getLogger(MyWsManager.class);
  6.     private Handler.Callback wsConnectCallBack;
  7.     private static final int WS_OPEN_STATUS = 1;
  8.     private static final int WS_MESSAGE_STATUS = 2;
  9.     private static final int WS_MESSAGE_2_STATUS = 3;
  10.     private static final int WS_RECONNECT_STATUS = 4;
  11.     private static final int WS_CLOSING_STATUS = 5;
  12.     private static final int WS_CLOSED_STATUS = 6;
  13.     private static final int WS_FAILURE_STATUS = 7;
  14.     public static MyWsManager getInstance() {
  15.         if (myWsManager == null) {
  16.             synchronized (MyWsManager.class) {
  17.                 if (myWsManager == null) {
  18.                     myWsManager = new MyWsManager();
  19.                 }
  20.             }
  21.         }
  22.         return myWsManager;
  23.     }
  24.     public void setWsConnectCallBack(Handler.Callback wsCallBack){
  25.         if (wsCallBack != null){
  26.             wsConnectCallBack = wsCallBack;
  27.         }
  28.     }
  29.     private void onCallBack(Message message){
  30.         if (wsConnectCallBack != null){
  31.             wsConnectCallBack.handleMessage(message);
  32.         }
  33.     }
  34.     public void initWS(Context context) {
  35.         try {
  36.             wsManager = new WsManager.Builder(context).client(
  37.                 new OkHttpClient().newBuilder()
  38.                     .pingInterval(15, TimeUnit.SECONDS)
  39. //                        .retryOnConnectionFailure(true)
  40.                     .build())
  41.                     .needReconnect(true)
  42.                     .wsUrl(URL)
  43.                     .build();
  44.             wsManager.setWsStatusListener(wsStatusListener);
  45.             wsManager.startConnect();
  46.         } catch (Exception e) {
  47.             logger.error("WebSocket连接异常:" + e.getMessage());
  48.         }
  49.     }
  50.     //状态监听
  51.     private WsStatusListener wsStatusListener = new WsStatusListener() {
  52.         Message message = new Message();
  53.         @Override
  54.         public void onOpen(Response response) {
  55.             logger.info("WebSocket服务器连接成功");
  56.             EventBus.getDefault().postSticky("connect");
  57.             message.what = WS_OPEN_STATUS;
  58.             onCallBack(message);
  59.         }
  60.         @Override
  61.         public void onMessage(String text) {
  62.             message.what = WS_MESSAGE_STATUS;
  63.             message.obj = text;
  64.             onCallBack(message);
  65.         }
  66.         @Override
  67.         public void onMessage(ByteString bytes) {
  68.             message.what = WS_MESSAGE_2_STATUS;
  69.             message.obj = bytes.toString();
  70.             onCallBack(message);
  71.         }
  72.         @Override
  73.         public void onReconnect() {
  74. //            logger.debug("WebSocket服务器重连接中...");
  75.             message.what = WS_RECONNECT_STATUS;
  76.             onCallBack(message);
  77.         }
  78.         @Override
  79.         public void onClosing(int code, String reason) {
  80. //            logger.debug("WebSocket服务器连接关闭中:" + reason);
  81.             message.what = WS_CLOSING_STATUS;
  82.             onCallBack(message);
  83.             //上面提及了设备会出现断开后无法连接的情况,那这种无法连接的情
  84.             //况我发现有可能会卡在这个关闭过程中,因为如果是确实断开后会确实的启动重连机制
  85.             //这里主要的目的就死让他跳出这个关闭中的状态,确实的关闭了ws
  86.             if (wsManager != null) {
  87.                 wsManager.stopConnect();
  88.                 wsManager.startConnect();
  89.             }
  90.         }
  91.         @Override
  92.         public void onClosed(int code, String reason) {
  93.             logger.debug("WebSocket服务器连接已关闭:" + reason);
  94.             message.what = WS_CLOSED_STATUS;
  95.             onCallBack(message);
  96.         }
  97.         @Override
  98.         public void onFailure(Throwable t, Response response) {
  99.             logger.error("WebSocket服务器连接失败:" +  t.getMessage());
  100.             message.what = WS_FAILURE_STATUS;
  101.             onCallBack(message);
  102.         }
  103.     };
  104.     //发送ws数据
  105.     public void sendData(String content) {
  106.         if (wsManager != null && wsManager.isWsConnected()) {
  107.             boolean isSend = wsManager.sendMessage(content);
  108.             if (isSend) {
  109.                 logger.info("WebSocket发送数据成功");
  110.             } else {
  111.                 logger.error("WebSocket发送数据失败");
  112.             }
  113.         } else {
  114.             ToastUtils.showToast("WebSocket连接已断开");
  115.         }
  116.     }
  117.     //断开ws
  118.     public void disConnect() {
  119.         if (wsManager != null)
  120.             wsManager.stopConnect();
  121.     }
  122.     //判断WS是否断开了
  123.     public boolean wsIsConnect(){
  124.         if (wsManager == null) {
  125.             return false;
  126.         }
  127.         return wsManager.isWsConnected();
  128.     }
  129. }
复制代码
3.2 在activity中初始化WebSocket的连接、吸收WebSocket消息,处理后分发给对应的fragment:
在processLogic中初始化WebSocket的连接
  1. private void initWebSocket() {
  2.     MyWsManager.getInstance().initWS(this);
  3.     time = new TimeCount(2000, 1000);
  4.     //WS状态监听
  5.     MyWsManager.getInstance().setWsConnectCallBack(new Handler.Callback() {
  6.         @Override
  7.         public boolean handleMessage(@NonNull Message message) {
  8.             switch (message.what) {
  9.                 case 1: //连接成功
  10.                     timerCount = 1;
  11.                     time.cancel();
  12.                     homeModel.websocketStatus.set(1);
  13.                     break;
  14.                 case 2: //接收string类型数据
  15.                     String data = (String) message.obj;
  16.                     if (!data.equals("连接建立成功")){
  17.                         showWebSocketData(data);
  18.                     }
  19.                     break;
  20.                 case 3: //接收ByteString类型数据
  21.                     String data2 = (String) message.obj;
  22.                     break;
  23.                 case 4: //websocket连接中
  24.                     break;
  25.                 case 5: //连接关闭中
  26.                     break;
  27.                 case 6: //连接已关闭
  28.                 case 7: //连接失败
  29.                     if (time != null){
  30.                         time.cancel();
  31.                         time.start();
  32.                     }
  33.                     homeModel.websocketStatus.set(0);
  34.                     break;
  35.             }
  36.             return false;
  37.         }
  38.     });
  39. }
复制代码
  1. // 重连机制
  2. public class TimeCount extends CountDownTimer {
  3.     public TimeCount(long millisInFuture, long countDownInterval) {
  4.         super(millisInFuture, countDownInterval);
  5.     }
  6.     @Override
  7.     public void onTick(long millisUntilFinished) {  // 计时过程
  8.     }
  9.     @Override
  10.     public void onFinish() {// 计时完毕
  11.         isTimer = false;
  12.         //判断WebSocket是否断开
  13.         if (!MyWsManager.getInstance().wsIsConnect()) {
  14.             MyWsManager.getInstance().disConnect();  //断开socket
  15.             MyWsManager.getInstance().initWS(MyApplication.getContext());
  16.         }
  17.     }
  18. }
复制代码
3.2 处理WebSocket消息,分发给对应的fragment:
  1. private WebsocketPushBean data = null;
  2. private void showWebSocketData(String pushData) {
  3.     try {
  4.         data = JSON.parseObject(pushData, WebsocketPushBean.class);
  5.     } catch (Exception e){
  6.         logger.error("WebSocket推送数据解析失败");
  7.     }
  8.     if (data != null){
  9.         if (data.getType() == 6 || data.getType() == 8){ //6-主扫支付
  10.             fragment.setWebSocketData(data);
  11.         }else if (data.getType() == 7) { //主扫跳主动支付
  12.             websocketPushBeans.add(data);
  13.             fragment.setWebSocketData(data);
  14.             payPopupView();
  15.         } else if (data.getType() == 2){ //主动支付
  16.             websocketPushBeans.add(data);
  17.             fragment.setWebSocketData(data);
  18.             payPopupView();
  19.         } else if (data.getType() == 1){ //网约车支付
  20.             websocketAutoPayBeans.add(data);
  21.             autoPayPopupView();
  22.         } else if (data.getType() == 9){ //车牌支付
  23.             fragment.setWebSocketData(data);
  24.         } else if (data.getType() == 10) { //流水绑定
  25.             fragment.setWebSocketData(data);
  26.         } else { //换购、赠品提示
  27.             websocketPushBeans.add(data);
  28.             payPopupView();
  29.         }
  30.     }
  31. }
复制代码
3.3 activity销毁时断开WebSocket连接
  1. @Override
  2. protected void onDestroy() {
  3.     super.onDestroy();
  4.     EventBus.getDefault().unregister(this);
  5.     MyWsManager.getInstance().disConnect();
  6.     if (time != null){
  7.         time.cancel();
  8.         time = null;
  9.     }
  10. }
复制代码
3.4 项目中那么多fagmen,上面的fagmen指哪个?因为所有的fagmenl都继续BaseFragment,所有上面的fagmen是BaseFragment,至于如何确认是哪个详细的fagment,背面会说明。
3.5 创建一个IBusinessInterface接口,用来把当前表现的fragment设置给activity,内容如下:
  1. public interface IBusinessInterface {
  2.     void setSelectedFragment(BaseFragment fragment);
  3. }
复制代码
3.6 Activity实现此接口,并把通报过来的fragment设置给BaseFragment:
  1. @Override
  2. public void setSelectedFragment(BaseFragment fragment) {
  3.     this.fragment = fragment;
  4. }
复制代码
3.7 fragment表现到前台时,把当前fragment设置给activity:
  1. IBusinessInterface backInterface = (IBusinessInterface)getActivity();
  2. backInterface.setSelectedFragment(this); // 将fragment传递到Activity中
复制代码
至此,activity中的fragment便确认了。
3.8 fragment中重写吸收WebSocket消息的方法setWebSocketData:
  1. /**
  2. * 接收Websocket返回的数据
  3. */
  4. public void setWebSocketData(WebsocketPushBean data){
  5.     if (data.getType() == 6 || data.getType() == 7){ //支付成功
  6.         if (customPopupView != null){
  7.             customPopupView.dismiss();
  8.         }
  9.         vm.getOrderInfoByFlowNo(data.getFlowNo());
  10.     } else if (data.getType() == 2){ //主动支付
  11.         if (beScanPopupView != null){
  12.             beScanPopupView.dismiss();
  13.         }
  14.         if (customPopupView != null){
  15.             customPopupView.dismiss();
  16.         }
  17.     } else if (data.getType() == 8){ //扫码开票、扫码兑换关闭弹框
  18.         if (customPopupView != null && customPopupView.isShow()){
  19.             customPopupView.dismiss();
  20.         }
  21.         currentPage = 1;
  22.         oilGunPage = 1;
  23.         lastPage = false;
  24.         oilWaterData.clear();
  25.         //获取当前选中的油枪信息
  26.         vm.getOrderInfoCountPos(model.deptCode.get(), model.selectGunList.get(), "1");
  27.         //获取油品流水信息
  28.         vm.getOrderInfoAndPayFlagPos(model.deptCode.get(), model.selectGunList.get(), 1);
  29.     } else if (data.getType() == 9){ //车牌支付
  30.         if (customPopupView != null){
  31.             customPopupView.dismiss();
  32.         }
  33.         model.flowNo.set(data.getFlowNo());
  34.         vm.getOrderInfoByFlowNo(data.getFlowNo());
  35.     }
  36. }
复制代码
至此,activity吸收WebSocket消息、分发给对应的fragment、fragment处理对应的业务整个链路已经通了。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

十念

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表