feat: ✨ 优化im 接口403判定,新增从买家端以及卖家端进行跳转im权限判定,新增im掉线重连以及重连成功提醒,新增发送的消息断线将会进行重新发送消息提示,优化最近浏览 订单列表空展示。注释部分im打印的日志信息
							parent
							
								
									32b29f45e7
								
							
						
					
					
						commit
						4700bc995a
					
				|  | @ -0,0 +1,43 @@ | |||
| <template> | ||||
|   <div></div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import { getIMDetail } from "@/api/common"; | ||||
| import Storage from "@/plugins/storage"; | ||||
| import { getMemberMsg } from "@/api/login"; | ||||
| export default { | ||||
|   data() { | ||||
|     return { | ||||
|       Storage, | ||||
|       IMLink: "", | ||||
|     }; | ||||
|   }, | ||||
|   methods: { | ||||
|     // 跳转im客服 | ||||
|     async IMService() { | ||||
|       // 获取访问Token | ||||
|       let accessToken = Storage.getItem("accessToken"); | ||||
|       await this.getIMDetailMethods(); | ||||
|       const userInfo = await getMemberMsg(); | ||||
|       if (userInfo.success) { | ||||
|         window.open( | ||||
|           this.IMLink + "?token=" + accessToken + "&id=" + this.storeMsg.storeId | ||||
|         ); | ||||
|       } else { | ||||
|         this.$Message.error("请登录后再联系客服"); | ||||
|         return; | ||||
|       } | ||||
|     }, | ||||
|     // 获取im信息 | ||||
|     async getIMDetailMethods() { | ||||
|       let res = await getIMDetail(); | ||||
|       if (res.success) { | ||||
|         this.IMLink = res.result; | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped></style> | ||||
|  | @ -50,8 +50,7 @@ import { | |||
|   getGoodsDistribution, | ||||
| } from "@/api/member"; | ||||
| import { getDetailById } from "@/api/shopentry"; | ||||
| import { getIMDetail } from "@/api/common"; | ||||
| import Storage from "../plugins/storage"; | ||||
| import imTalk from '@/components/mixes/talkIm' | ||||
| export default { | ||||
|   name: "GoodsDetail", | ||||
|   beforeRouteEnter (to, from, next) { | ||||
|  | @ -61,6 +60,7 @@ export default { | |||
|   created () { | ||||
|     this.getGoodsDetail(); | ||||
|   }, | ||||
|   mixins: [imTalk], | ||||
|   data () { | ||||
|     return { | ||||
|       goodsMsg: {}, // 商品信息 | ||||
|  | @ -68,49 +68,11 @@ export default { | |||
|       categoryBar: [], // 分类 | ||||
|       storeCollected: false, // 商品收藏 | ||||
|       storeMsg: {}, // 店铺信息 | ||||
|       IMLink: "", | ||||
| 
 | ||||
|     }; | ||||
|   }, | ||||
|   methods: { | ||||
|     // 跳转im客服 | ||||
|     async IMService () { | ||||
|       // 获取访问Token | ||||
|       let accessToken = Storage.getItem("accessToken"); | ||||
|       await this.getIMDetailMethods(); | ||||
|       if (!accessToken) { | ||||
|         this.$Message.error("请登录后再联系客服"); | ||||
|         return; | ||||
|       } | ||||
|       window.open( | ||||
|         this.IMLink + | ||||
|         "?token=" + | ||||
|         accessToken + | ||||
|         "&id=" + | ||||
|         this.goodsMsg.data.storeId + | ||||
|         "&goodsId=" + | ||||
|         this.goodsMsg.data.goodsId + | ||||
|         "&skuId=" + | ||||
|         this.goodsMsg.data.id | ||||
|       ); | ||||
|       // window.open( | ||||
|       //   'http://192.168.0.139:8000/' + | ||||
|       //   "?token=" + | ||||
|       //   accessToken + | ||||
|       //   "&id=" + | ||||
|       //   this.goodsMsg.data.storeId + | ||||
|       //   "&goodsId=" + | ||||
|       //   this.goodsMsg.data.goodsId + | ||||
|       //   "&skuId=" + | ||||
|       //   this.goodsMsg.data.id | ||||
|       // ); | ||||
|     }, | ||||
|     // 获取im信息 | ||||
|     async getIMDetailMethods () { | ||||
|       let res = await getIMDetail(); | ||||
|       if (res.success) { | ||||
|         this.IMLink = res.result; | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
|     // 点击规格 | ||||
|     targetClickSku (val) { | ||||
|       this.getGoodsDetail(val); | ||||
|  |  | |||
|  | @ -106,8 +106,7 @@ | |||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import {getIMDetail} from "@/api/common"; | ||||
| import Storage from "../plugins/storage"; | ||||
| 
 | ||||
| import {getDetailById, getCateById} from "@/api/shopentry"; | ||||
| import {cancelCollect, collectGoods, isCollection} from "@/api/member"; | ||||
| import {goodsList} from "@/api/goods"; | ||||
|  | @ -117,6 +116,7 @@ import HoverSearch from "@/components/header/hoverSearch"; | |||
| import storage from "@/plugins/storage"; | ||||
| import {getFloorStoreData} from "@/api/index.js"; | ||||
| import {seckillByDay} from "@/api/promotion"; | ||||
| import imTalk from '@/components/mixes/talkIm' | ||||
| 
 | ||||
| export default { | ||||
|   name: "Merchant", | ||||
|  | @ -125,6 +125,7 @@ export default { | |||
|     ModelForm, | ||||
|     HoverSearch, | ||||
|   }, | ||||
|   mixins: [imTalk], | ||||
|   data() { | ||||
|     return { | ||||
|       // 店铺装修的内容 | ||||
|  | @ -140,7 +141,6 @@ export default { | |||
|       cateList: [], // 店铺分裂 | ||||
|       goodsList: [], // 商品列表 | ||||
|       total: 0, // 商品数量 | ||||
|       IMLink: "", | ||||
|       params: { | ||||
|         // 请求参数 | ||||
|         pageNumber: 1, | ||||
|  | @ -187,30 +187,8 @@ export default { | |||
|         } | ||||
|       ); | ||||
|     }, | ||||
|     // 跳转im客服 | ||||
|     async IMService() { | ||||
|       // 获取访问Token | ||||
|       let accessToken = Storage.getItem("accessToken"); | ||||
|       await this.getIMDetailMethods(); | ||||
|       if (!accessToken) { | ||||
|         this.$Message.error("请登录后再联系客服"); | ||||
|         return; | ||||
|       } | ||||
|       window.open( | ||||
|         this.IMLink + | ||||
|         "?token=" + | ||||
|         accessToken + | ||||
|         "&id=" + | ||||
|         this.storeMsg.storeId | ||||
|       ); | ||||
|     }, | ||||
|     // 获取im信息 | ||||
|     async getIMDetailMethods() { | ||||
|       let res = await getIMDetail(); | ||||
|       if (res.success) { | ||||
|         this.IMLink = res.result; | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
| 
 | ||||
|     // getStoreMsg () { // 店铺信息 | ||||
|     //   getDetailById(this.$route.query.id).then(res => { | ||||
|     //     if (res.success) { | ||||
|  |  | |||
|  | @ -58,16 +58,21 @@ | |||
|                     {{ item.float == "right" ? name : toUser.name }} | | ||||
|                     {{ unixToDate(item.createTime, "MM月dd日 hh:mm") }} | ||||
|                   </span> | ||||
|                   <!-- 文本消息 --> | ||||
|                   <div v-if="item.messageType == 'MESSAGE'" style="background-color: #d0e9ff;color: black;" | ||||
|                     class="text-message" :class="{ | ||||
|                       left: item.float == 'left', | ||||
|                       right: item.float == 'right', | ||||
|                     }"> | ||||
|                     <div class="arrow"></div> | ||||
|                     <pre v-if="!emojistwo.includes(item.text)" v-html="item.text" /> | ||||
|                     <pre v-if="emojistwo.includes(item.text)" v-html="textReplaceEmoji(item.text)" /> | ||||
|                   <div class="flex flex-a-c"> | ||||
|                     <i @click="againSendMessage(item)" v-if="item.webSocketStatus" class="el-icon-refresh-left again main-color"></i> | ||||
|                     <!-- 文本消息 --> | ||||
|                     <div v-if="item.messageType == 'MESSAGE'" style="background-color: #d0e9ff;color: black;" | ||||
|                       class="text-message" :class="{ | ||||
|                         left: item.float == 'left', | ||||
|                         right: item.float == 'right', | ||||
|                       }"> | ||||
|                       <div class="arrow"></div> | ||||
|                       <pre v-if="!emojistwo.includes(item.text)" v-html="item.text" /> | ||||
|                       <pre v-if="emojistwo.includes(item.text)" v-html="textReplaceEmoji(item.text)" /> | ||||
|                     </div> | ||||
|                     | ||||
|                   </div> | ||||
|                    <div v-if="item.webSocketStatus" class="tips">网络异常发送失败,请重新发送。</div> | ||||
| 
 | ||||
|                   <div v-if="item.messageType == 'GOODS' && item.text != null" class="goodsStyle " :class="{ | ||||
|                     left: item.float == 'left', | ||||
|  | @ -93,17 +98,24 @@ | |||
|                     left: item.float == 'left', | ||||
|                     right: item.float == 'right', | ||||
|                   }" @click="linkToOrders(item.text.sn)"> | ||||
|                    | ||||
|                     <div class="oedersn"> | ||||
|                       <el-tooltip class="item" effect="dark" :content="item.text.sn" placement="top-start"> | ||||
|                         <a> 订单号:{{ item.text.sn }} </a> | ||||
|                       </el-tooltip> | ||||
|                     </div> | ||||
|                     <div class="goods-shared-box"> | ||||
|                       <div> | ||||
|                         <img :src="item.text.groupImages" style="height: 100px;width: 100px;" /> | ||||
|                       | ||||
|                       <div class="goods-item" v-for="(order,orderIndex) in item.text.orderItems" :key="orderIndex"> | ||||
|                         <img :src="order.image" style="height: 100px;width: 100px;" /> | ||||
|                         <div> | ||||
|                           <span class="orderGoodsName">{{ order.name }}</span> | ||||
|                           <div class="goods-item-price"> | ||||
|                            <span>{{ order.goodsPrice | unitPrice('¥') }}</span> | ||||
|                           </div>  | ||||
|                         </div> | ||||
|                       </div> | ||||
|                       <div class="shared-goods"> | ||||
|                       <span class="orderGoodsName">{{ item.text.groupName }}</span> | ||||
|                       <div class="orderGoodsTime">{{ item.text.paymentTime }}</div> | ||||
|                       <span class="orderFlowPrice"> | ||||
|                         订单金额:<span>{{ item.text.flowPrice | unitPrice('¥') }}</span> | ||||
|  | @ -395,6 +407,12 @@ export default { | |||
|     }, | ||||
|     // #冗余代码结束 | ||||
| 
 | ||||
|     // 重新发送消息 | ||||
|     againSendMessage(val){ | ||||
|       | ||||
|       this.submitSendMessage(val.text) | ||||
|     }, | ||||
| 
 | ||||
|     // 回车键发送消息回调事件 | ||||
|     submitSendMessage (content) { | ||||
|       const record = { | ||||
|  | @ -848,7 +866,21 @@ export default { | |||
| }; | ||||
| </script> | ||||
| <style lang="less" scoped> | ||||
| 
 | ||||
| .flex-a-c{ | ||||
|   align-items: center; | ||||
|   >div{ | ||||
|     margin-left: 10px; | ||||
|   } | ||||
| } | ||||
| .tips{ | ||||
|   margin-top:10px; | ||||
|   color: #999; | ||||
|   font-size: 12px; | ||||
| } | ||||
| .again{ | ||||
|   margin-top: 5px; | ||||
|   cursor: pointer; | ||||
| } | ||||
| 
 | ||||
| .oderStyle { | ||||
|   border: 1px solid #f2f2f2; | ||||
|  | @ -926,13 +958,18 @@ export default { | |||
| 
 | ||||
| .orderFlowPrice { | ||||
|   color: #999; | ||||
| 
 | ||||
|    | ||||
|   font-size: 12px; | ||||
|   >span{ | ||||
|     color: red; | ||||
|     font-size: 18px; | ||||
|   } | ||||
| } | ||||
| .goods-item-price{ | ||||
|   margin-top: 10px; | ||||
|   font-size: 13px; | ||||
|    color: red; | ||||
| } | ||||
| 
 | ||||
| .main-box { | ||||
|   position: relative; | ||||
|  | @ -1247,7 +1284,14 @@ export default { | |||
|   } | ||||
| } | ||||
| .goods-shared-box{ | ||||
|   display: flex; | ||||
|    cursor: pointer; | ||||
|   >.goods-item{ | ||||
|     | ||||
|     margin: 10px 0; | ||||
|     display: flex; | ||||
|     border-bottom: 1px solid #ededed; | ||||
|     align-items: center; | ||||
|   } | ||||
|   >.shared-goods{ | ||||
|     padding-left: 10px; | ||||
|     display: flex; | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
|     <div class="tab"> | ||||
|       <el-tabs v-model="activeName" @tab-click="handleClick" :stretch=true> | ||||
|         <el-tab-pane label="最近浏览" name="goods"> | ||||
|           <dl> | ||||
|           <dl v-if='list.length' class='base-list'> | ||||
|             <dd :key="index"  @click="linkToGoods(item.goodsId, item.id)" v-for="(item,index) in list" v-infinite-scroll="loadMore"> | ||||
|               <div class="base"> | ||||
|                 <div> | ||||
|  | @ -27,32 +27,38 @@ | |||
|             </dd> | ||||
| 
 | ||||
|           </dl> | ||||
|           <div v-else class='no-more'> | ||||
|               {{noMoreList.goods.title}} | ||||
|           </div> | ||||
|         </el-tab-pane> | ||||
|         <el-tab-pane label="订单列表" name="orders"> | ||||
|           <dl> | ||||
|             <dd v-for="(item, index) in orderList" v-infinite-scroll="loadMore" :key="index"> | ||||
|           <dl class='base-order-list' v-if='orderList.length'> | ||||
|             <dd  v-for="(item, index) in orderList" v-infinite-scroll="loadMore" :key="index"> | ||||
|               <div class="order-list"> | ||||
|                 <div class="order-top order-padding"> | ||||
|                   <span class="order-sn" @click="linkToOrders(item.sn)">订单号:{{ item.sn }}</span> | ||||
|                 </div> | ||||
|                 <div class="order-section order-padding"> | ||||
|                   <img :src="item.groupImages" alt=""> | ||||
|                   <span class="order-goods-name" @click="linkToOrders(item.sn)"> {{ item.groupName }}</span> | ||||
|                   <div class="order-items" v-for="(order,orderIndex) in item.orderItems" :key="orderIndex"> | ||||
|                       <img :src="order.image" alt=""> | ||||
|                       <span class="order-goods-name" @click="linkToOrders(item.sn)"> {{ order.name }}</span> | ||||
|                       <span class="price">{{order.goodsPrice | unitPrice("¥")}}</span> | ||||
|                   </div> | ||||
|                   <!-- <img  :src="item.groupImages" alt=""> --> | ||||
|                   | ||||
|                   <div class="order-btn "> | ||||
|                     <el-button class="store-button" v-if="item.btnHide == 1 && toUser.storeFlag" | ||||
|                       size="mini" @click="submitSendOrderMessage(item, index)" plain>发送</el-button> | ||||
|                   </div> | ||||
|                 </div> | ||||
|                 <div class="order-footer order-padding"> | ||||
|                   <span> 订单金额: <span style="color: red;">{{ | ||||
|                     item.orderItems[0].goodsPrice | unitPrice("¥") | ||||
|                   }}</span></span> | ||||
|                   <span></span> | ||||
|                   <el-tag  size='mini' :type="col[item.orderStatus]">{{ | ||||
|                     item.orderStatus == 'STAY_PICKED_UP' ? '待自提' | ||||
|                       : item.orderStatus == 'CANCELLED' ? '已取消' : item.orderStatus == 'UNPAID' ? '未付款' : item.orderStatus | ||||
|                         == | ||||
|                         'PAID' ? '已付款' : item.orderStatus == 'UNDELIVERED' ? '待发货' : item.orderStatus == 'DELIVERED' | ||||
|                           ? '已发货' : item.orderStatus == 'COMPLETED' ? '已完成' : item.orderStatus == 'TAKE' ? '待校验' : | ||||
|                           ? '已发货' : item.orderStatus == 'COMPLETED' ? '已完成' : item.orderStatus == 'TAKE' ? '待核销' : | ||||
|                             '' | ||||
|                   }}</el-tag> | ||||
|                 </div> | ||||
|  | @ -61,6 +67,9 @@ | |||
|              | ||||
|             </dd> | ||||
|           </dl> | ||||
|           <div v-else class='no-more'> | ||||
|               {{noMoreList.orders.title}} | ||||
|           </div> | ||||
|         </el-tab-pane> | ||||
|       </el-tabs> | ||||
|     </div> | ||||
|  | @ -78,12 +87,16 @@ export default { | |||
|   }, | ||||
|   data () { | ||||
|     return { | ||||
|       noMoreList:{ | ||||
|         'goods':{title:'暂无最近浏览',value:false}, | ||||
|         'orders':{title:'暂无订单信息',value:false}, | ||||
|       }, | ||||
|       activeName: 'goods', | ||||
|       btnHide: undefined, | ||||
|       hide: true, | ||||
|       col: { | ||||
|         CANCELLED: 'error', | ||||
|         PAID: 'error', | ||||
|         CANCELLED: 'danger', | ||||
|         PAID: 'danger', | ||||
|         TAKE: '', | ||||
|         COMPLETED: 'success', | ||||
|         DELIVERED: 'danger', | ||||
|  | @ -142,12 +155,14 @@ export default { | |||
|     // 发送订单列表 | ||||
|     submitSendOrderMessage (item, index) { | ||||
|       console.log(item, 'item'); | ||||
|      | ||||
|       const context = { | ||||
|         sn: item.sn, | ||||
|         groupImages: item.groupImages, | ||||
|         paymentTime: item.paymentTime, | ||||
|         groupName: item.groupName, | ||||
|         flowPrice: item.flowPrice, | ||||
|         orderItems:item.orderItems, | ||||
|         orderStatus: item.orderStatus | ||||
|       } | ||||
|       const record = { | ||||
|  | @ -163,7 +178,7 @@ export default { | |||
|       item.btnHide = 0 | ||||
|     }, | ||||
|     handleClick (tab, event) { | ||||
|       console.log(tab, event); | ||||
|        | ||||
|     } | ||||
|   }, | ||||
|   props: { | ||||
|  | @ -178,7 +193,7 @@ export default { | |||
|   }, | ||||
|   mounted () { | ||||
|     //  state.user.toUser | ||||
|     console.log(this.orderList, '  this.$store.state.user.toUser  this.$store.state.user.toUser  this.$store.state.user.toUser'); | ||||
|     // console.log(this.orderList, '  this.$store.state.user.toUser  this.$store.state.user.toUser  this.$store.state.user.toUser'); | ||||
|     this.btnHide = localStorage.getItem('btnHide') | ||||
|   } | ||||
| } | ||||
|  | @ -193,7 +208,12 @@ export default { | |||
|   white-space: nowrap; | ||||
|   width: 200px; | ||||
| } | ||||
| 
 | ||||
| .no-more{ | ||||
|   font-size: 12px; | ||||
|   color: #999; | ||||
|   text-align: center; | ||||
|   margin-top: 20px; | ||||
| } | ||||
| 
 | ||||
| .box { | ||||
|   max-width: 362px; | ||||
|  | @ -374,4 +394,18 @@ export default { | |||
| //   height: calc(100vh - 110px); | ||||
| //   overflow-y: auto; | ||||
| // } | ||||
| .base-list{ | ||||
|   padding-bottom: 120px; | ||||
| } | ||||
| .base-order-list{ | ||||
|   padding-bottom: 100px; | ||||
| } | ||||
| .price{ | ||||
|   color: red; | ||||
|   margin-left: 10px; | ||||
|   margin-bottom: 10px; | ||||
| } | ||||
| .order-items{ | ||||
|   margin-bottom: 10px; | ||||
| } | ||||
| </style> | ||||
|  | @ -173,7 +173,6 @@ export default { | |||
|   methods: { | ||||
|     // 读取对话编辑草稿信息 并赋值给当前富文本 | ||||
|     getDraftText (index_name) { | ||||
|       console.log("findTalk(index_name)", findTalk(index_name)); | ||||
|       return findTalk(index_name)?.draft_text || ""; | ||||
|     }, | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
|     <img :src="text" alt=""/> | ||||
|     <img :src="text || 'https://avatars.dicebear.com/api/initials/'+name+'.svg?fontSize=38'" alt=""/> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
|  | @ -8,7 +8,11 @@ | |||
|             text:{ | ||||
|                 type:null, | ||||
|                 default:'' | ||||
|             } | ||||
|             }, | ||||
|             name:{ | ||||
|                 type:null, | ||||
|                 default:'' | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  |  | |||
|  | @ -85,5 +85,4 @@ Vue.prototype.$alert = MessageBox.alert | |||
| import Contextmenu from 'vue-contextmenujs' | ||||
| Vue.use(Contextmenu) | ||||
| 
 | ||||
| process.env.NODE_ENV !== 'production' && | ||||
|   console.warn('[Lumen-IM] NOTICE: element-ui use lazy-load.') | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,3 +1,6 @@ | |||
| import { Notification, MessageBox  } from "element-ui"; | ||||
| let wsSignIn; // ws 是否是掉线状态
 | ||||
| import store from "@/store"; | ||||
| class WsSocket { | ||||
|   /** | ||||
|    * Websocket 连接 | ||||
|  | @ -25,7 +28,7 @@ class WsSocket { | |||
|     reconnect: { | ||||
|       lockReconnect: false, | ||||
|       setTimeout: null, // 计时器对象
 | ||||
|       time: 5000, // 重连间隔时间
 | ||||
|       time: 1000, // 重连间隔时间
 | ||||
|       number: 1000, // 重连次数
 | ||||
|     }, | ||||
|   }; | ||||
|  | @ -49,9 +52,9 @@ class WsSocket { | |||
|     // 定义 WebSocket 原生方法
 | ||||
|     this.events = Object.assign( | ||||
|       { | ||||
|         onError: (evt) => { }, | ||||
|         onOpen: (evt) => { }, | ||||
|         onClose: (evt) => { }, | ||||
|         onError: (evt) => {}, | ||||
|         onOpen: (evt) => {}, | ||||
|         onClose: (evt) => {}, | ||||
|       }, | ||||
|       events | ||||
|     ); | ||||
|  | @ -63,7 +66,7 @@ class WsSocket { | |||
|    * @param {String} event 事件名 | ||||
|    * @param {Function} callBack 回调方法 | ||||
|    */ | ||||
|   on (event, callBack) { | ||||
|   on(event, callBack) { | ||||
|     // 对应 socket-instance.js
 | ||||
|     this.onCallBacks[event] = callBack; | ||||
|     return this; | ||||
|  | @ -72,7 +75,23 @@ class WsSocket { | |||
|   /** | ||||
|    * 加载 WebSocket | ||||
|    */ | ||||
|   loadSocket () { | ||||
|   loadSocket() { | ||||
|     /** | ||||
|      * 判断当前如果是掉线提示 | ||||
|      * 重连成功后关闭掉线提示,新增重连提示 | ||||
|      */ | ||||
|     if (wsSignIn) { | ||||
|       store.commit('SET_WS_STATUS',true); | ||||
|       wsSignIn.close(); | ||||
| 
 | ||||
|       Notification({ | ||||
|         title: "成功", | ||||
|         message: "重连成功", | ||||
|         type: "success", | ||||
|         position: "top-right", | ||||
|         duration: 1000, | ||||
|       }); | ||||
|     } | ||||
|     // 判断当前是否已经连接
 | ||||
|     if (this.connect != null) { | ||||
|       this.connect.close(); | ||||
|  | @ -85,41 +104,72 @@ class WsSocket { | |||
|     connect.onopen = this.onOpen.bind(this); | ||||
|     connect.onmessage = this.onMessage.bind(this); | ||||
|     connect.onclose = this.onClose.bind(this); | ||||
| 
 | ||||
|     this.connect = connect; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 连接 Websocket | ||||
|    */ | ||||
|   connection () { | ||||
|   connection() { | ||||
|     this.loadSocket(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 掉线重连 Websocket | ||||
|    */ | ||||
|   reconnect () { | ||||
|     console.log("掉线重连接"); | ||||
|   reconnect() { | ||||
|      | ||||
|     /** | ||||
|      * 长时间挂载页面中并且重连次数为空的时候进行提示 | ||||
|      */ | ||||
|     if (this.config.reconnect.number == 0) { | ||||
|       MessageBox("当前对话链接已失效,请从关闭重新进入。", "提示", { | ||||
|         confirmButtonText: "确定", | ||||
|         cancelButtonText: "取消", | ||||
|         closeOnPressEscape: false, | ||||
|         closeOnClickModal: false, | ||||
|         type: "warning", | ||||
|       }) | ||||
|         .then(() => { | ||||
|           window.close(); | ||||
|           Notification({ | ||||
|             title: "对话链接已失效提示", | ||||
|             message: "请手动关闭当前页面", | ||||
|             type: "error", | ||||
|             position: "top-right", | ||||
|           }); | ||||
|         }) | ||||
|         .catch(() => { | ||||
|           Notification({ | ||||
|             title: "对话链接已失效提示", | ||||
|             message: "请手动关闭当前页面", | ||||
|             type: "error", | ||||
|             position: "top-right", | ||||
|           }); | ||||
|         }); | ||||
|       return false; | ||||
|     } | ||||
|     // 掉线重连提示
 | ||||
|     wsSignIn = Notification({ | ||||
|       title: "掉线重连接提示", | ||||
|       message: `网络连接已断开,正在尝试重新连接......`, | ||||
|       type: "error", | ||||
|       position: "top-right", | ||||
|       duration: 0, | ||||
|     }); | ||||
|     // 掉线更改消息状态
 | ||||
|     store.commit('SET_WS_STATUS',true); | ||||
|     let reconnect = this.config.reconnect; | ||||
|     if (reconnect.lockReconnect || reconnect.number == 0) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     this.config.reconnect.lockReconnect = true; | ||||
| 
 | ||||
|     // 没连接上会一直重连,设置延迟避免请求过多
 | ||||
|     reconnect.setTimeout && clearTimeout(reconnect.setTimeout); | ||||
| 
 | ||||
|     this.config.reconnect.setTimeout = setTimeout(() => { | ||||
|       this.connection(); | ||||
| 
 | ||||
|       this.config.reconnect.lockReconnect = false; | ||||
|       this.config.reconnect.number--; | ||||
| 
 | ||||
|       console.log( | ||||
|         `网络连接已断开,正在尝试重新连接(${this.config.reconnect.number})...` | ||||
|       ); | ||||
|     }, reconnect.time); | ||||
|   } | ||||
| 
 | ||||
|  | @ -128,8 +178,7 @@ class WsSocket { | |||
|    * | ||||
|    * @param {Object} evt Websocket 消息 | ||||
|    */ | ||||
|   onParse (evt) { | ||||
| 
 | ||||
|   onParse(evt) { | ||||
|     const res = JSON.parse(evt.data).result; | ||||
| 
 | ||||
|     //如果创建时间是时间戳类型则转换为 日期类型,否则新压入栈的消息的创建时间和从数据库读取出来的创建时间格式对不上,处理的时候会出异常。
 | ||||
|  | @ -145,7 +194,7 @@ class WsSocket { | |||
|    * @param format 转换格式 | ||||
|    * @returns {*|string} | ||||
|    */ | ||||
|   unixToDate (unix, format) { | ||||
|   unixToDate(unix, format) { | ||||
|     if (!unix) return unix; | ||||
|     let _format = format || "yyyy-MM-dd hh:mm:ss"; | ||||
|     const d = new Date(unix); | ||||
|  | @ -179,7 +228,7 @@ class WsSocket { | |||
|    * | ||||
|    * @param {Object} evt Websocket 消息 | ||||
|    */ | ||||
|   onOpen (evt) { | ||||
|   onOpen(evt) { | ||||
|     this.events.onOpen(evt); | ||||
| 
 | ||||
|     if (this.config.heartbeat.enabled) { | ||||
|  | @ -192,7 +241,7 @@ class WsSocket { | |||
|    * | ||||
|    * @param {Object} evt Websocket 消息 | ||||
|    */ | ||||
|   onClose (evt) { | ||||
|   onClose(evt) { | ||||
|     console.log("关闭连接", evt); | ||||
|     if (this.config.heartbeat.enabled) { | ||||
|       clearInterval(this.config.heartbeat.setInterval); | ||||
|  | @ -211,7 +260,7 @@ class WsSocket { | |||
|    * | ||||
|    * @param {Object} evt Websocket 消息 | ||||
|    */ | ||||
|   onError (evt) { | ||||
|   onError(evt) { | ||||
|     this.events.onError(evt); | ||||
|   } | ||||
| 
 | ||||
|  | @ -220,28 +269,30 @@ class WsSocket { | |||
|    * | ||||
|    * @param {Object} evt Websocket 消息 | ||||
|    */ | ||||
|   onMessage (evt) { | ||||
|   onMessage(evt) { | ||||
|     let result = this.onParse(evt); | ||||
|     if (this.onParse(evt).text.includes('goodsName') || this.onParse(evt).text.includes('groupName')) { | ||||
|     if ( | ||||
|       this.onParse(evt).text.includes("goodsName") || | ||||
|       this.onParse(evt).text.includes("groupName") | ||||
|     ) { | ||||
|       let params = { | ||||
|         ...this.onParse(evt), | ||||
|         text: JSON.parse(this.onParse(evt).text) | ||||
|       } | ||||
|         text: JSON.parse(this.onParse(evt).text), | ||||
|       }; | ||||
|       this.onCallBacks["event_talk"](params); | ||||
|     } else { | ||||
|       let params = { | ||||
|         ...this.onParse(evt), | ||||
|         text: this.onParse(evt).text | ||||
|       } | ||||
|         text: this.onParse(evt).text, | ||||
|       }; | ||||
|       this.onCallBacks["event_talk"](params); | ||||
|     } | ||||
|     // 指定推送消息
 | ||||
| 
 | ||||
|   } | ||||
|   /** | ||||
|    * WebSocket心跳检测 | ||||
|    */ | ||||
|   heartbeat () { | ||||
|   heartbeat() { | ||||
|     console.log("WebSocket心跳检测"); | ||||
|     this.config.heartbeat.setInterval = setInterval(() => { | ||||
|       this.connect.send("PING"); | ||||
|  | @ -253,14 +304,14 @@ class WsSocket { | |||
|    * | ||||
|    * @param {Object} message | ||||
|    */ | ||||
|   send (message) { | ||||
|   send(message) { | ||||
|     this.connect.send(JSON.stringify(message)); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 关闭连接 | ||||
|    */ | ||||
|   close () { | ||||
|   close() { | ||||
|     this.connect.close(); | ||||
|   } | ||||
| 
 | ||||
|  | @ -270,7 +321,7 @@ class WsSocket { | |||
|    * @param {String} event 事件名 | ||||
|    * @param {Object} data 数据 | ||||
|    */ | ||||
|   emit (event, data) { | ||||
|   emit(event, data) { | ||||
|     if (this.connect && this.connect.readyState === 1) { | ||||
|       this.connect.send(JSON.stringify(data)); | ||||
|     } else { | ||||
|  |  | |||
|  | @ -21,11 +21,15 @@ export default { | |||
|         float: "", | ||||
|       }, | ||||
|     ], | ||||
| 
 | ||||
|     webSocketWithOut:false, // ws 是否是掉线 无输出状态
 | ||||
|     // 对话索引(聊天对话的唯一索引)
 | ||||
|     index_name: null, | ||||
|   }, | ||||
|   mutations: { | ||||
|      // 设置ws状态
 | ||||
|      SET_WS_STATUS: (state,resource) =>{ | ||||
|       state.webSocketWithOut = resource | ||||
|     }, | ||||
|     // 更新对话
 | ||||
|     UPDATE_DIALOGUE_MESSAGE (state, resource) { | ||||
|       state.records = []; | ||||
|  | @ -56,6 +60,8 @@ export default { | |||
| 
 | ||||
|     // 推送对话记录
 | ||||
|     PUSH_DIALOGUE (state, record) { | ||||
|       record = {...record,webSocketStatus:state.webSocketWithOut} | ||||
|       console.log("推送对话",) | ||||
|       state.records.push(record); | ||||
|     }, | ||||
| 
 | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ const Talk = { | |||
|     talkNum: (state) => state.items.length, | ||||
|   }, | ||||
|   mutations: { | ||||
|     | ||||
|     // 设置对话列表
 | ||||
|     SET_TALK_ITEMS (state, resource) { | ||||
|       Vue.set(state, 'items', resource.items) | ||||
|  |  | |||
|  | @ -19,7 +19,6 @@ let state = { | |||
| // 判断用户是否登录
 | ||||
| if (getToken()) { | ||||
|   let userInfo = getUserInfo(); | ||||
|   console.error(userInfo) | ||||
|   state.name = userInfo.name; | ||||
|   state.id = userInfo.id; | ||||
|   state.face = userInfo.face ? userInfo.face : state.avatar; | ||||
|  |  | |||
|  | @ -4,8 +4,6 @@ const defaultAvatar = require('@/assets/image/detault-avatar.jpg') | |||
| const state = { | ||||
|   socketStatus: false, | ||||
|   website_name: process.env.VUE_APP_WEBSITE_NAME, | ||||
|   copyright: `©2020 - 2021 ${process.env.VUE_APP_WEBSITE_NAME} 在线聊天 <a href="https://github.com/gzydong/LumenIM" style="color: #777272;font-weight: 400;" target="_blank">Github源码</a>`, | ||||
| 
 | ||||
|   // 头像加载失败后的默认头像
 | ||||
|   defaultAvatar: "this.src='" + defaultAvatar + "'", | ||||
| } | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ const request = axios.create({ | |||
|   timeout: 20000, | ||||
| }); | ||||
| 
 | ||||
| let isRefreshing = false | ||||
| /** | ||||
|  * 异常拦截处理器 | ||||
|  * | ||||
|  | @ -27,10 +28,12 @@ const errorHandler = (error) => { | |||
|       removeAll(); | ||||
|       location.reload(); | ||||
|     } else if (error.response.status == 403) { | ||||
|       if(!isRefreshing){ | ||||
|        | ||||
|       /** | ||||
|        * 403提示将重新从商家移动端进入当前页面 | ||||
|        */ | ||||
|       MessageBox("当前登录已失效,请从商家管理后台重新登录。", "提示", { | ||||
|       MessageBox("当前登录已失效,请从关闭重新进入。", "提示", { | ||||
|         confirmButtonText: "确定", | ||||
|         cancelButtonText: "取消", | ||||
|         closeOnPressEscape: false, | ||||
|  | @ -38,6 +41,7 @@ const errorHandler = (error) => { | |||
|         type: "warning", | ||||
|       }) | ||||
|         .then(() => { | ||||
|           isRefreshing = true | ||||
|           window.close(); | ||||
|           Notification({ | ||||
|             title:"登录失效提示", | ||||
|  | @ -45,9 +49,10 @@ const errorHandler = (error) => { | |||
|             type:"error", | ||||
|             position: "top-right", | ||||
|           }); | ||||
| 
 | ||||
|         }) | ||||
|         .catch(() => { | ||||
|           | ||||
|           isRefreshing = true | ||||
|           Notification({ | ||||
|             title:"登录失效提示", | ||||
|             message: "请手动关闭当前页面", | ||||
|  | @ -55,6 +60,8 @@ const errorHandler = (error) => { | |||
|             position: "top-right", | ||||
|           }); | ||||
|         }); | ||||
|         isRefreshing = false | ||||
|       } | ||||
|     } else if(error.response.status == 400){ | ||||
|       Notification({ | ||||
|         message: error.response.data.message, | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
|             <el-header height="60px" class="header"> | ||||
|               <div class="user-login" v-popover:usercard> | ||||
|                 <div class="user-box"> | ||||
|                   <face :text="face" class="user-face"></face> | ||||
|                   <face :text="face" :name="name" class="user-face"></face> | ||||
|                 </div> | ||||
|               </div> | ||||
|               <p class="user-status"> | ||||
|  | @ -263,7 +263,7 @@ export default { | |||
|     }, | ||||
|     unreadNum (value) { | ||||
|       clearInterval(this.interval); | ||||
|       console.log("%c 更新未读消息", "color:#32ccbc"); | ||||
|       // console.log("%c 更新未读消息", "color:#32ccbc"); | ||||
|       this.$store.commit("SET_UNREAD_NUM", value); | ||||
| 
 | ||||
|       if (value > 0) { | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ | |||
|               <div>店铺状态:{{ userData.storeDisable == 'OPEN' ? '开启中' : '关闭' }}</div> | ||||
|             </div> | ||||
|             <div class="box-item" @click="im()"> | ||||
|               <Button type="info">点击登录客服</Button> | ||||
|               <Button type="info" :loading='load'>点击登录客服</Button> | ||||
|             </div> | ||||
|           </div> | ||||
| 
 | ||||
|  | @ -207,7 +207,7 @@ import { getSellerHomeData, getHomeNotice } from "@/api/index"; | |||
| import { getIMDetail } from "@/api/common" | ||||
| import { seeArticle } from "@/api/pages"; | ||||
| import Cookies from "js-cookie"; | ||||
| 
 | ||||
| import { userMsg } from "@/api/index"; | ||||
| export default { | ||||
|   name: "home", | ||||
|   data () { | ||||
|  | @ -221,8 +221,10 @@ export default { | |||
|         title: "", | ||||
|       }, | ||||
|       IMLink: "", | ||||
|       load:false, //加载Im | ||||
|     }; | ||||
|   }, | ||||
| 
 | ||||
|   methods: { | ||||
|     // 跳转页面 | ||||
|     navigateTo (name) { | ||||
|  | @ -249,16 +251,24 @@ export default { | |||
|         this.noticeFlage = true; | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
|     /** | ||||
|      * 点击登录im的时候需要去判断一下当前店铺信息是否失效 | ||||
|      * 失效的话重新请求刷新token保证最新的token去访问im | ||||
|      */ | ||||
|     async im () { | ||||
|       // 获取访问Token | ||||
|       let accessToken = this.getStore("accessToken"); | ||||
|       this.load = true | ||||
|       await this.getIMDetailMethods(); | ||||
|       let res = await getIMDetail(); | ||||
|       if (!accessToken) { | ||||
|         this.$Message.error("请登录后再联系客服"); | ||||
|         return; | ||||
|       const userInfo = await userMsg(); | ||||
|       this.load = false | ||||
|       if (userInfo.success && this.IMLink) { | ||||
|         window.open(`${this.IMLink}?token=` + accessToken); | ||||
|       } | ||||
|       else{ | ||||
|         this.$Message.error("请登录后再联系客服"); | ||||
|       } | ||||
|       window.open(`${res.result}?token=` + accessToken); | ||||
|     }, | ||||
| 
 | ||||
|     // 获取im信息 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue