imFloat.vue 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278
  1. <template>
  2. <div class="global-im">
  3. <!-- 悬浮按钮与角标 -->
  4. <div class="im-trigger" :class="{ active: visible }" @click="toggleChat">
  5. <el-icon :size="18"><el-icon-chat-round /></el-icon>
  6. <div class="unreadCount">
  7. <el-badge :value="unreadCount" :hidden="unreadCount === 0" :max="99">
  8. <i class="el-icon-chat-dot-round" style="font-size: 24px"></i>
  9. </el-badge>
  10. </div>
  11. </div>
  12. </div>
  13. <el-drawer v-model="visible" title="在线客服" size="100%" destroy-on-close :close-on-click-modal="false" :with-header="false">
  14. <el-container class="flex-column">
  15. <div class="drawer-detail-main">
  16. <div class="drawer-detail-header">
  17. <div class="drawer-detail-header-body">
  18. <div class="drawer-detail-header-left">在线客服</div>
  19. <div class="drawer-detail-header-left">
  20. <el-button type="default" icon="el-icon-close" round @click="hideWindow"></el-button>
  21. </div>
  22. </div>
  23. </div>
  24. </div>
  25. <el-main class="nopadding">
  26. <el-container>
  27. <el-aside width="300px" v-loading="loadMsg">
  28. <el-container class="flex-column">
  29. <!-- <div class="aside-tabs bg">
  30. <div :class="orderData.tab=='user'?'aside-tab-items active':'aside-tab-items'" @click="handleClick('user')">当前咨询</div>
  31. <div :class="orderData.tab=='order'?'aside-tab-items active':'aside-tab-items'" @click="handleClick('order')">历史咨询</div>
  32. </div> -->
  33. <el-main class="nopadding">
  34. <div class="chat-contact">
  35. <div :class="checkChat==index?'contact-item active':'contact-item'" v-for="(item,index) in chatContact" :key="index" @click="checkChatItem(index)">
  36. <div class="img">
  37. <el-image :src="item.user.avatar" fit="contain" style="width: 40px; height: 40px"></el-image>
  38. <span class="last-num" v-if="item.last.num > 0">{{item.last.num}}</span>
  39. </div>
  40. <div class="kefu-store">
  41. <div class="name">{{item.user.nickname}}</div>
  42. <div class="desc" v-if="item.last.type=='text'">{{item.last.content}}</div>
  43. <div class="desc" v-if="item.last.type=='order'">[订单消息]</div>
  44. <div class="desc" v-if="item.last.type=='image'">[图片消息]</div>
  45. <div class="desc" v-if="item.last.type=='video'">[视频消息]</div>
  46. <div class="desc" v-if="item.last.type=='pay'">[支付消息]</div>
  47. <div class="desc" v-if="item.last.type=='address'">[收集地址]</div>
  48. </div>
  49. <div class="time">
  50. {{item.last.create_at}}
  51. </div>
  52. </div>
  53. </div>
  54. </el-main>
  55. </el-container>
  56. </el-aside>
  57. <el-container v-if="chatItem">
  58. <el-header><div class="msg-title">与{{ chatItem.user.nickname }}的对话</div></el-header>
  59. <el-main class="nopadding" ref="main">
  60. <div class="chat-container" v-if="chatItem">
  61. <div class="loading-text" v-if="msgLoad">努力加载中...</div>
  62. <div class="message-list" ref="messageListRef" @scroll="handleScroll">
  63. <template v-for="(item, index) in history.messages">
  64. <div class="z_index2" style="transform: rotate(-180deg)" :key="item.messageId" v-if="!item.isHide">
  65. <div class="icon_ text_26 color__ time">
  66. {{ renderMessageDate(item, index) }}
  67. </div>
  68. <div :key="item.messageIds" v-if="!item.recalled">
  69. <item :isMy="isSelf(item.senderId)" :myid="myid" :item="item" @onClick="onItem"
  70. @onLongpress="onLongpress" @mention="mention"></item>
  71. </div>
  72. <div class="icon_ text_26 recalled" v-else>
  73. <div class="">
  74. <span v-if="isSelf(item.senderId)">你</span>
  75. <span v-else>{{ item.senderData.name }}</span>
  76. 撤回了一条消息
  77. </div>
  78. <div class="recalled-edit" v-if="item.type === 'text' && isSelf(item.senderId)"
  79. @click="recalledEdit(item)">重新编辑</div>
  80. </div>
  81. </div>
  82. </template>
  83. </div>
  84. <div class="input-area">
  85. <div class="quick-group">
  86. <div class="emoji-item">
  87. <el-popover placement="top" :width="400" trigger="click">
  88. <template #reference>
  89. <div class="item-btn">
  90. <el-image style="width:25px;height:25px;" src="https://jymini.oss-cn-guangzhou.aliyuncs.com/pc/face.svg"></el-image>
  91. <div class="name">表情</div>
  92. </div>
  93. </template>
  94. <div class="swiper-item-box">
  95. <div class="flex_r swiper-item-box-l" v-for="(im, ix) in emojiList" :key="ix">
  96. <div
  97. class="icon_ emoji-item"
  98. v-for="(item, index) in im"
  99. :key="index"
  100. :style="{ opacity: ix === opacityGroup && (index == 5 || index == 6 || index == 7) ? opacity : 1 }"
  101. >
  102. <el-image class="img" :src="emojiUrl + item.image" @click="chooseEmoji(item.text)"></el-image>
  103. </div>
  104. </div>
  105. </div>
  106. </el-popover>
  107. </div>
  108. <div class="emoji-item">
  109. <el-popover placement="top" :width="174">
  110. <template #reference>
  111. <div class="item-btn">
  112. <el-image style="width:25px;height:25px;" src="https://jymini.oss-cn-guangzhou.aliyuncs.com/pc/more.svg"></el-image>
  113. <div class="name">图片</div>
  114. </div>
  115. </template>
  116. <sc-upload v-model="uploadImg" :on-success="uploadSuccess"></sc-upload>
  117. </el-popover>
  118. </div>
  119. <div class="emoji-item" @click="showQuick">
  120. <el-popover placement="top" :width="400" trigger="click" @show="getQuick">
  121. <template #reference>
  122. <div class="item-btn">
  123. <el-image style="width:25px;height:25px;" src="https://jymini.oss-cn-guangzhou.aliyuncs.com/pc/quick.svg"></el-image>
  124. <div class="name">快捷回复</div>
  125. </div>
  126. </template>
  127. <div class="quick-table">
  128. <div class="quick-search">
  129. <div class="search-input">
  130. <el-input placeholder="搜索一下..." style="width: 100%;" />
  131. <el-button>搜索</el-button>
  132. </div>
  133. <div class="search-btn-txt"><el-button type="primary" @click="addQuick">添加</el-button></div>
  134. </div>
  135. <div class="quick-table-body">
  136. <el-table :data="quickData.list" style="width: 100%" height="300" border>
  137. <el-table-column prop="content" label="内容"></el-table-column>
  138. <el-table-column prop="content" label="操作" width="80">
  139. <template #default="scope">
  140. <el-button size="small" @click="sendQuick(scope.row.content)">发送</el-button>
  141. </template>
  142. </el-table-column>
  143. </el-table>
  144. </div>
  145. <div class="quick-page">
  146. <el-pagination
  147. small
  148. background
  149. layout="prev, pager, next"
  150. :total="quickData.total"
  151. @size-change="quickPageChange" @current-change="quickPageChange"
  152. />
  153. </div>
  154. </div>
  155. </el-popover>
  156. </div>
  157. </div>
  158. <el-input
  159. type="textarea"
  160. :rows="3"
  161. v-model="inputMessage"
  162. placeholder="请输入消息..."
  163. @keyup.enter.native="handleKeyCode"
  164. @keydown.native="handleKeyCode"
  165. ></el-input>
  166. <div class="send-btn-group">
  167. <div class="send-btn-right">
  168. <el-button type="primary" @click="sendMessage">发送</el-button>
  169. </div>
  170. </div>
  171. </div>
  172. </div>
  173. </el-main>
  174. </el-container>
  175. <el-container v-else>
  176. <div class="empty-data">
  177. 请选择左侧的聊天列表
  178. </div>
  179. </el-container>
  180. <el-aside width="400px" v-if="chatItem">
  181. <el-container class="flex-column">
  182. <div class="aside-tabs">
  183. <div :class="orderData.tab=='user'?'aside-tab-items active':'aside-tab-items'" @click="handleClick('user')">用户信息</div>
  184. <div :class="orderData.tab=='order'?'aside-tab-items active':'aside-tab-items'" @click="handleClick('order')">团购订单</div>
  185. <!-- <div :class="orderData.tab=='price'?'aside-tab-items active':'aside-tab-items'" @click="handleClick('price')">差价订单</div> -->
  186. </div>
  187. <el-main class="nopadding">
  188. <template v-if="orderData.tab == 'user'">
  189. <div class="order-info-user">
  190. <div class="info-item">
  191. <div class="name">昵称</div>
  192. <div class="right">{{ chatItem.user.nickname }} </div>
  193. </div>
  194. <div class="info-item">
  195. <div class="name">OpenId</div>
  196. <div class="right">{{ chatItem.user.openid }} </div>
  197. </div>
  198. <div class="info-item">
  199. <div class="name">手机号码</div>
  200. <div class="right">{{ chatItem.user.mobile }} </div>
  201. </div>
  202. <div class="info-item">
  203. <div class="name">注册时间</div>
  204. <div class="right">{{ chatItem.user.create_at }} </div>
  205. </div>
  206. </div>
  207. <div class="order-info-user" v-if="msgOrder">
  208. <div class="title">咨询订单</div>
  209. <div class="m-order-s" style="margin-top: 15px;">
  210. <div class="m-order-item" @click="viewOrder(msgOrder)">
  211. <div class="order-img"><el-image style="width: 80px;height: 80px;" :src="msgOrder.img" fit="fit"></el-image></div>
  212. <div class="order-info">
  213. <div class="title" style="padding: 0;">{{msgOrder.product.product_name}}</div>
  214. <div class="desc" v-if="msgOrder.sku_name">规格:{{msgOrder.sku_name}}</div>
  215. <div class="desc">{{msgOrder.order_sn}}</div>
  216. <div class="price">
  217. {{$TOOL.money(msgOrder.price)}}
  218. <text v-if="msgOrder.custom_price > 0">差价:{{$TOOL.money(msgOrder.custom_price)}}</text>
  219. </div>
  220. </div>
  221. <div class="order-status-btn">
  222. <div class="status info" v-if="msgOrder.status=='0'">未支付</div>
  223. <div class="status primary" v-if="msgOrder.status=='1'">待使用</div>
  224. <div class="status success" v-if="msgOrder.status=='2'">已完成</div>
  225. <div class="status danger" v-if="msgOrder.status=='3'">已退款</div>
  226. <div class="status warn" v-if="msgOrder.status=='4'">退款中</div>
  227. <div class="status warn" v-if="msgOrder.status=='5'">已关闭</div>
  228. <div class="status success" v-if="msgOrder.product.product_type==11 && msgOrder.custom_price > 0 && msgOrder.custom_status ==1">差价已付</div>
  229. <div class="status danger" v-if="msgOrder.product.product_type==11 && msgOrder.custom_price > 0 && msgOrder.custom_status ==0">差价未付</div>
  230. </div>
  231. </div>
  232. <div class="order-btn-group">
  233. <div class="status primary cus" @click="sendAddress(msgOrder)" v-if="msgOrder.status=='1' && user.trade == '2'">发起预约</div>
  234. <div class="status info cus" @click="editAddress(msgOrder)" v-if="msgOrder.status=='1' && user.trade == '2'">代客预约</div>
  235. <!-- <div class="status warn cus" v-if="msgOrder.product.product_type==11 && msgOrder.custom_price > 0 && msgOrder.custom_status ==0" @click="submitPrice(msgOrder)">补差价</div> -->
  236. <!-- <div class="status danger cus" v-if="msgOrder.product.product_type==11 && msgOrder.custom_price > 0 && msgOrder.custom_status ==0" @click="submitPrice(msgOrder)">改差价</div> -->
  237. <div class="status danger cus" @click="sendOrder(msgOrder)">发送订单</div>
  238. <div class="status primary cus" @click="doneOrder(msgOrder)" v-if="msgOrder.status=='1' && user.trade == '1'">手动核销</div>
  239. </div>
  240. </div>
  241. </div>
  242. </template>
  243. <template v-if="orderData.tab == 'price'">
  244. <el-container>
  245. <el-header>
  246. <div class="order-search-input">
  247. <el-input v-model="priceData.order" placeholder="搜索订单编号..." style="width: 100%;" />
  248. <el-button @click="searchPrice">搜索</el-button>
  249. </div>
  250. </el-header>
  251. <el-main>
  252. </el-main>
  253. <el-footer style="text-align: right;">
  254. <div class="order-page">
  255. <el-pagination small background :default-page-size="priceData.size" layout="prev, pager, next" @size-change="orderPageChange" @current-change="orderPageChange" :total="priceData.total" />
  256. </div>
  257. </el-footer>
  258. </el-container>
  259. </template>
  260. <template v-if="orderData.tab == 'order'">
  261. <el-container v-loading="orderData.loading">
  262. <el-header>
  263. <div class="order-search-input">
  264. <el-input v-model="orderData.order" placeholder="搜索订单编号..." style="width: 100%;" />
  265. <el-button @click="searchOrder">搜索</el-button>
  266. </div>
  267. </el-header>
  268. <el-main>
  269. <div class="order-list-chat" v-if="orderData.list.length > 0">
  270. <div class="m-order-s" v-for="(item,index) in orderData.list" :key="index">
  271. <div class="m-order-item" @click="viewOrder(item)">
  272. <div class="order-img"><el-image style="width: 80px;height: 80px;" :src="item.img" fit="fit"></el-image></div>
  273. <div class="order-info">
  274. <div class="title">{{item.product.product_name}}</div>
  275. <div class="desc" v-if="item.sku_name">规格:{{item.sku_name}}</div>
  276. <div class="desc">{{item.order_sn}}</div>
  277. <div class="price">
  278. {{$TOOL.money(item.price)}}
  279. <text v-if="item.custom_price > 0">差价:{{$TOOL.money(item.custom_price)}}</text>
  280. </div>
  281. </div>
  282. <div class="order-status-btn">
  283. <div class="status info" v-if="item.status=='0'">未支付</div>
  284. <div class="status primary" v-if="item.status=='1'">待使用</div>
  285. <div class="status success" v-if="item.status=='2'">已完成</div>
  286. <div class="status danger" v-if="item.status=='3'">已退款</div>
  287. <div class="status warn" v-if="item.status=='4'">退款中</div>
  288. <div class="status warn" v-if="item.status=='5'">已关闭</div>
  289. <div class="status success" v-if="item.product.product_type==11 && item.custom_price > 0 && item.custom_status ==1">差价已付</div>
  290. <div class="status danger" v-if="item.product.product_type==11 && item.custom_price > 0 && item.custom_status ==0">差价未付</div>
  291. </div>
  292. </div>
  293. <div class="order-btn-group">
  294. <div class="status primary cus" @click="sendAddress(item)" v-if="item.status=='1' && user.trade == '2'">发起预约</div>
  295. <div class="status info cus" @click="editAddress(item)" v-if="item.status=='1' && user.trade == '2'">代客预约</div>
  296. <!-- <div class="status warn cus" v-if="item.product.product_type==11 && item.custom_price > 0 && item.custom_status ==0" @click="submitPrice(item)">补差价</div>
  297. <div class="status danger cus" v-if="item.product.product_type==11 && item.custom_price > 0 && item.custom_status ==0" @click="submitPrice(item)">改差价</div> -->
  298. <div class="status danger cus" @click="sendOrder(item)">发送订单</div>
  299. <div class="status primary cus" @click="doneOrder(item)" v-if="item.status=='1' && user.trade == '1'">手动核销</div>
  300. </div>
  301. </div>
  302. </div>
  303. <div class="order-list-chat" v-else>
  304. <el-empty description="没有订单" />
  305. </div>
  306. </el-main>
  307. <el-footer style="text-align: right;">
  308. <div class="order-page">
  309. <el-pagination small background :default-page-size="orderData.size" layout="prev, pager, next" @size-change="orderPageChange" @current-change="orderPageChange" :total="orderData.total" />
  310. </div>
  311. </el-footer>
  312. </el-container>
  313. </template>
  314. </el-main>
  315. </el-container>
  316. </el-aside>
  317. </el-container>
  318. </el-main>
  319. </el-container>
  320. </el-drawer>
  321. <orderDetail ref="orderDetail" @success="orderSuccess"></orderDetail>
  322. <quickTxt ref="quickTxt" @success="orderSuccess"></quickTxt>
  323. <addressMain ref="addressMain" @success="addressSuccess"></addressMain>
  324. </template>
  325. <script>
  326. import {
  327. mapState
  328. } from 'vuex';
  329. import { Push } from "@/utils/push";
  330. import { emojiMap } from "@/utils/emoji";
  331. import item from '@/components/chat/index';
  332. var pushObj = null;
  333. let height2 = null;
  334. const emojiUrl = 'https://imgcache.qq.com/open/qcloud/tim/assets/emoji/';
  335. import {
  336. formatDate,
  337. to as tofn
  338. } from '@/util/index.js';
  339. import orderDetail from '@/components/imFloat/order';
  340. import quickTxt from '@/components/imFloat/quickTxt';
  341. import addressMain from '@/components/imFloat/address';
  342. let imageList = [];
  343. export default {
  344. name: 'GlobalIM',
  345. components:{
  346. item,
  347. orderDetail,
  348. quickTxt,
  349. addressMain
  350. },
  351. props: {
  352. user: { type: Object, default: () => {} }
  353. },
  354. data() {
  355. return {
  356. activeName:"user",
  357. rules:{
  358. name: [
  359. {required: true, message: '请输入'}
  360. ],
  361. price: [
  362. {required: true, message: '请输入'}
  363. ],
  364. },
  365. priceForm:{
  366. name:"补差价"
  367. },
  368. uploadImg:"",
  369. loadMsg:false,
  370. history: {
  371. messages: [],
  372. allLoaded: false
  373. },
  374. msgLoad:false,
  375. emojiUrl,
  376. loading:false,
  377. visible: false, // 聊天窗口可见性
  378. unreadCount: 0, // 未读消息数量
  379. inputMessage: '', // 输入的消息
  380. timer: null, // 定时器
  381. messages: [],
  382. wsAuth:"https://tran.jsshuita.cn/plugin/webman/push/auth",
  383. wsUrl: 'wss://tran.jsshuita.cn/ws',
  384. // wsAuth:"http://127.0.0.1:9981/plugin/webman/push/auth",
  385. // wsUrl: 'ws://127.0.0.1:3131',
  386. page:1,
  387. size:10,
  388. msgPage:1,
  389. msgSize:20,
  390. orderSize:10,
  391. chatContact:[],
  392. checkChat:null,
  393. chatItem:null,
  394. emojiList:[],
  395. sendId:null,
  396. myid: this.user.userId,
  397. userId:null,
  398. isSaveing:false,
  399. quickData:{
  400. page:1,
  401. size:10,
  402. list:[],
  403. popState:false,
  404. total:0
  405. },
  406. msgOrder:null, // 咨询订单
  407. orderData:{
  408. page:1,
  409. size:10,
  410. list:[],
  411. loading:false,
  412. total:0,
  413. tab:"user",
  414. order:""
  415. },
  416. priceData:{
  417. page:1,
  418. size:10,
  419. list:[],
  420. loading:false,
  421. total:0,
  422. order:""
  423. }
  424. }
  425. },
  426. watch: {
  427. chatItem(val){
  428. if (val) {
  429. this.$nextTick(() => {
  430. this.$refs.messageListRef?.addEventListener('scroll', this.handleScroll)
  431. })
  432. }
  433. }
  434. },
  435. computed: mapState({
  436. //显示时间
  437. renderMessageDate() {
  438. return (message, index) => {
  439. if (message.timestamp - this.history.messages[index + 1]?.timestamp > 3 * 60 * 1000) {
  440. return formatDate(message.timestamp, 'timestamp');
  441. }
  442. return '';
  443. };
  444. },
  445. // 是否本人isMy
  446. isSelf() {
  447. return (senderId) => {
  448. const {
  449. member_id = this.myid
  450. } = {"member_id":this.myid};
  451. return senderId === `${member_id}`;
  452. };
  453. }
  454. }),
  455. mounted() {
  456. // console.log("pushObj")
  457. this.initEmoji()
  458. this.initPush();
  459. // this.simulateNewMessage()
  460. },
  461. beforeDestroy() {
  462. // if (this.timer) {
  463. // clearInterval(this.timer)
  464. // }
  465. this.$refs.messageListRef.removeEventListener('scroll', this.handleScroll)
  466. },
  467. methods: {
  468. editAddress(item){
  469. this.$nextTick(() => {
  470. this.$refs.addressMain.open("edit").setData(item);
  471. })
  472. },
  473. addressSuccess(){
  474. this.getOrderList();
  475. },
  476. handleClick(event){
  477. this.orderData.tab = event;
  478. switch (event) {
  479. case "user":
  480. break;
  481. case "order":
  482. this.orderData.page = 1;
  483. this.orderData.list = [];
  484. this.getOrderList();
  485. break;
  486. case "price":
  487. this.priceData.page = 1;
  488. this.priceData.list = [];
  489. this.getOrderPrice();
  490. break;
  491. }
  492. },
  493. searchOrder(){
  494. this.orderData.page = 1;
  495. this.orderData.list = [];
  496. this.getOrderList();
  497. },
  498. searchPrice(){
  499. this.priceData.page = 1;
  500. this.priceData.list = [];
  501. this.getOrderPrice();
  502. },
  503. getOrderPrice(){
  504. },
  505. viewOrder(data){
  506. data.order = data.out_order_no;
  507. this.$refs.orderDetail.open("add").setData(data)
  508. },
  509. quickPageChange(e){
  510. this.quickData.page = e;
  511. this.getQuick()
  512. },
  513. async getQuick(){
  514. var resp = await this.$API.merQuick.list.get({"page":this.quickData.page,"pageSize":this.quickData.size});
  515. if (resp.code == 0) {
  516. return this.$message.error(resp.msg)
  517. }
  518. this.quickData.total = resp.data.total;
  519. this.quickData.list = resp.data.rows;
  520. },
  521. addQuick(){
  522. this.$nextTick(() => {
  523. this.$refs.quickTxt.open("add")
  524. })
  525. },
  526. sendQuick(text){
  527. this.inputMessage = text;
  528. this.sendMessage()
  529. },
  530. orderSuccess(type,data){
  531. switch(type) {
  532. case "address":
  533. this.sendAddress(data)
  534. break;
  535. }
  536. },
  537. async submitPrice(data){
  538. var { priceForm,$message } = this;
  539. if (data.custom_status == 1) return $message.error("该订单差价已完成支付")
  540. // var validate = await this.$refs.priceForm.validate().catch(()=>{});
  541. // if(!validate){ return false }
  542. this.isSaveing = true;
  543. priceForm.name = "订单差额补款";
  544. priceForm.price = parseInt(data.custom_price) / 100;
  545. priceForm.type = "pay";
  546. priceForm.groupId = this.chatItem.poi_id
  547. priceForm.openid = this.chatItem.openid
  548. var resp = await this.$API.chat.price.post(priceForm);
  549. this.isSaveing = false;
  550. if (resp.code == 0) {
  551. return $message.error(resp.msg)
  552. }
  553. this.sendMessageData({
  554. payload: {
  555. pay: resp.data,
  556. },
  557. type: 'pay'
  558. });
  559. this.uploadImg = "";
  560. },
  561. orderPageChange(e){
  562. this.orderData.page = e;
  563. this.getOrderList()
  564. },
  565. hideWindow(){
  566. this.chatItem = null;
  567. this.msgPage = 1;
  568. this.unreadCount = 0;
  569. this.checkChat = null;
  570. this.visible = false;
  571. },
  572. handleScroll(e){
  573. const { scrollTop, clientHeight, scrollHeight } = e.target
  574. const isBottom = scrollTop + clientHeight >= scrollHeight - 5 // 5px 缓冲区
  575. const isTop = scrollTop <= 5
  576. // console.log("滚动",this.history.allLoaded)
  577. if (isBottom && !this.history.allLoaded) {
  578. console.log('到达底部')
  579. // 加载更多等操作
  580. this.getMsgMore()
  581. }
  582. if (isTop && this.history.allLoaded) {
  583. // console.log('到达顶部')
  584. }
  585. },
  586. playAudio(){
  587. const audio = new Audio('/img/msn.mp3');
  588. audio.play().catch(error => {
  589. console.error("播放音频时出错:", error);
  590. });
  591. },
  592. onLongpress(){},
  593. onItem(data){
  594. switch(data.type)
  595. {
  596. case "order":
  597. this.$nextTick(() => {
  598. this.$refs.orderDetail.open("add").setData(data.payload.order)
  599. })
  600. break;
  601. case "address":
  602. this.$nextTick(() => {
  603. this.$refs.orderDetail.open("add").setData(data.payload.address)
  604. })
  605. break;
  606. }
  607. },
  608. chooseEmoji(data){
  609. this.inputMessage += data;
  610. },
  611. initEmoji() {
  612. let newList = [];
  613. let i = 1;
  614. for (let key in emojiMap) {
  615. let obj = {};
  616. obj['index'] = i;
  617. obj['text'] = key;
  618. obj['image'] = emojiMap[key];
  619. i++;
  620. newList.push(obj);
  621. }
  622. var list = [];
  623. let A = newList.filter((item, index) => {
  624. return (index + 1) % 8 === 0;
  625. });
  626. A.unshift({ index: 0 });
  627. A.push({ index: newList.length });
  628. this.emojiList = A.map((item, index) => {
  629. return newList.slice(A[index].index, A[index + 1]?.index || newList.length);
  630. });
  631. // this.scroll({ detail: { scrollTop: 10 } });
  632. },
  633. /**
  634. * 发送订单消息
  635. * @param data
  636. */
  637. async sendOrder(data){
  638. var orderData = {"img":data.img,"name":data.product.product_name,"goods_id":data.life_goods_id,"order":data.out_order_no,"price":this.$TOOL.disMoney(data.pay_money),"status":data.status};
  639. var resp = await this.sendMessageApi("order",orderData);
  640. if (!resp) {
  641. return ;
  642. }
  643. this.sendMessageData({
  644. payload: {
  645. order: orderData
  646. },
  647. type: 'order'
  648. });
  649. },
  650. /**
  651. * 收集地址消息
  652. * @param data
  653. */
  654. async sendAddress(data){
  655. var orderData = {"img":data.img,"name":"完善会员信息","order":data.out_order_no,"status":0};
  656. var resp = await this.sendMessageApi("address",orderData);
  657. if (!resp) {
  658. return ;
  659. }
  660. this.sendMessageData({
  661. payload: {
  662. address: orderData
  663. },
  664. type: 'address'
  665. });
  666. },
  667. async sendMessage(){
  668. if (!this.inputMessage) return ;
  669. var resp = await this.sendMessageApi("text",this.inputMessage);
  670. if (!resp) {
  671. this.inputMessage = "";
  672. return ;
  673. }
  674. this.sendMessageData({
  675. payload: {
  676. text: this.inputMessage
  677. },
  678. type: 'text'
  679. });
  680. this.inputMessage = ""
  681. },
  682. sendMessageData({payload,type}){
  683. const message = {
  684. groupId: this.chatItem.poi_id,
  685. senderData: {
  686. avatar:this.chatItem.avatar
  687. },
  688. senderId: this.myid,
  689. messageId: Date.now(),
  690. payload: payload,
  691. timestamp: Date.now(),
  692. type: type,
  693. recalled: false,
  694. status: 'success',
  695. isHide: 0
  696. };
  697. this.pushList(message);
  698. },
  699. handleKeyCode(event){
  700. if (event.keyCode == 13) {
  701. if (!event.ctrlKey) {
  702. this.sendMessage()
  703. event.preventDefault();
  704. } else {
  705. this.inputMessage += "\n";
  706. }
  707. } else {
  708. return ;
  709. }
  710. },
  711. checkChatItem(index){
  712. this.checkChat = index;
  713. this.chatItem = this.chatContact[index]
  714. this.chatContact[index].last.num = 0;
  715. this.msgPage = 1;
  716. this.history.messages = []
  717. this.history.allLoaded = false
  718. this.orderData.list = [];
  719. this.orderData.page = 1;
  720. this.orderData.tab = "user";
  721. this.getMsgData();
  722. this.getMsgOrder();
  723. this.getOrderList();
  724. },
  725. async getMsgOrder(){
  726. var resp = await this.$API.chat.msgOrder.get({"msgId":this.chatItem.id})
  727. if (resp.code == 0) return ;
  728. this.msgOrder = resp.data.order;
  729. },
  730. initPush(){
  731. var _this = this;
  732. if (!this.pushObj) {
  733. this.pushObj = new Push({
  734. "url":this.wsUrl,
  735. "app_key":"265c33b73d5c04f918978577df2c48d2",
  736. "auth":this.wsAuth
  737. });
  738. }
  739. var user_channel = this.pushObj.subscribe('service-' + this.myid);
  740. user_channel.on('message', function (data) {
  741. _this.playAudio()
  742. _this.unreadCount ++;
  743. _this.sortItemData(data.item)
  744. if (_this.chatItem && _this.chatItem.openid == data.openid) { // 当前窗口实时推送
  745. _this.formatMsg(data);
  746. }
  747. // 给消息标红并更新最后一条消息
  748. var msgData = _this.chatContact;
  749. for (var i=0; i <= msgData.length;i ++) {
  750. var item = msgData[i];
  751. if (item && item.openid == data.openid) {
  752. item.last.type = data.type;
  753. item.last.content = data.content;
  754. item.last.create_at = data.create_at
  755. item.last.last_at = data.last_at
  756. // item.last.num ++;
  757. }
  758. }
  759. })
  760. },
  761. truncateByBytes(str, maxBytes) {
  762. let count = 0;
  763. let result = '';
  764. for (const char of str) {
  765. // 中文、全角符号算 2 字节,其他算 1 字节
  766. const byteLen = char.match(/[^\x00-\xff]/) ? 2 : 1;
  767. if (count + byteLen > maxBytes) break;
  768. result += char;
  769. count += byteLen;
  770. }
  771. return result + (count < str.length ? '...' : '');
  772. },
  773. sortItemData(data){
  774. const existIdx = this.chatContact.findIndex(item => item.id === data.id);
  775. if (existIdx !== -1) { // 若已存在,按更新处理
  776. this.handleNewMessage(data);
  777. return;
  778. }
  779. // 新增数据到最顶部
  780. this.chatContact.unshift(data);
  781. },
  782. handleNewMessage(updateData) {
  783. const idx = this.chatContact.findIndex(item => item.id === updateData.id);
  784. this.chatContact.splice(idx, 1, updateData);
  785. this.sortSessions();
  786. },
  787. sortSessions() {
  788. this.chatContact.sort((a, b) => {
  789. const timeA = a.last_at && a.last_at !== '-' ? new Date(a.last_at).getTime() : 0;
  790. const timeB = b.last_at && b.last_at !== '-' ? new Date(b.last_at).getTime() : 0;
  791. return timeB - timeA;
  792. });
  793. },
  794. sortBooks() {
  795. this.chatContact.sort((a, b) => b.last_at - a.last_at)
  796. },
  797. formatMsg(item){
  798. var payload = {};
  799. if (item.type == 'text') {
  800. payload.text = item.content
  801. }
  802. if (item.type == 'order') {
  803. var order = JSON.parse(item.content)
  804. payload.order = order
  805. }
  806. if (item.type == 'pay') {
  807. var order = JSON.parse(item.content)
  808. payload.pay = order
  809. }
  810. if (item.type == 'address') {
  811. var order = JSON.parse(item.content)
  812. payload.address = order
  813. }
  814. if (item.type == 'image') {
  815. payload = {
  816. contentType: 'image/png',
  817. name: 'uni-image.png',
  818. size: 82942,
  819. url: item.content,
  820. width: 2732,
  821. height: 2732,
  822. thumbnail: item.content
  823. };
  824. }
  825. const message = {
  826. groupId: item.poi_id,
  827. senderData: {
  828. avatar:item.source==2?item.avatar:item.user.avatar,
  829. name:item.source==2?item.nick.truename:item.user.nickname
  830. },
  831. senderId: (item.source==1?item.openid:item.service_id),
  832. messageId: item.msgId,
  833. payload: payload,
  834. timestamp: item.time,
  835. type: item.type,
  836. recalled: false,
  837. status: 'success',
  838. isHide: 0
  839. };
  840. this.pushList(message);
  841. },
  842. async pushList(message) {
  843. this.initMessageItem(message);
  844. this.history.messages.unshift(message);
  845. // 是否触发文字动效果
  846. if (message.type === 'text' || message.type === 'text_quote') {
  847. this.onSetText(message.payload.text);
  848. }
  849. // 缓存照片地址,
  850. if (message.type === 'image' || message.type === 'image_transmit') {
  851. imageList.push(message.payload.url);
  852. }
  853. },
  854. // 组装item
  855. initMessageItem(message, index) {
  856. message['isHide'] = 0;
  857. if (index === 0 && (message.type === 'text' || message.type === 'text_quote')) {
  858. this.onSetText(message.payload.text);
  859. }
  860. },
  861. // 文本触发效果相关========
  862. onSetText(text) {},
  863. // 切换聊天窗口
  864. async toggleChat() {
  865. this.visible = !this.visible
  866. if (this.visible) {
  867. // 打开窗口时清除未读角标
  868. this.unreadCount = 0
  869. }
  870. if (this.chatContact.length == 0) {
  871. this.loadMsg = true;
  872. var resp = await this.$API.chat.list.get({page:this.page,pageSize:this.size})
  873. this.loadMsg = false;
  874. if (resp.code == 0) {
  875. return this.$message.error(resp.msg)
  876. }
  877. this.chatContact = this.chatContact.concat(resp.data.rows)
  878. }
  879. // this.chatItem = this.chatContact[0]
  880. // this.userId = this.chatItem.openid;
  881. // this.getMsgData();
  882. },
  883. /**
  884. * 加载更多
  885. */
  886. async getMsgMore(){
  887. this.history.allLoaded = true;
  888. this.msgLoad = true;
  889. var resp = await this.$API.chat.msg.get({"page":this.msgPage,"size":this.msgSize,"openid":this.chatItem.openid})
  890. this.msgLoad = false;
  891. if (resp.data.rows.length > 0) {
  892. this.msgPage ++;
  893. var msgData = []
  894. var list = resp.data.rows
  895. // 同步混入数据
  896. list.forEach((item, ix) => {
  897. var payload = {};
  898. if (item.type == 'text') {
  899. payload.text = item.content
  900. }
  901. if (item.type == 'order') {
  902. var order = JSON.parse(item.content)
  903. payload.order = order
  904. }
  905. if (item.type == 'pay') {
  906. var order = JSON.parse(item.content)
  907. payload.pay = order
  908. }
  909. if (item.type == 'address') {
  910. var order = JSON.parse(item.content)
  911. payload.address = order
  912. }
  913. if (item.type == 'image') {
  914. payload = {
  915. contentType: 'image/png',
  916. name: 'uni-image.png',
  917. size: 82942,
  918. url: item.content,
  919. width: 2732,
  920. height: 2732,
  921. thumbnail: item.content
  922. };
  923. }
  924. const message = {
  925. groupId: item.poi_id,
  926. senderData: {
  927. avatar:item.avatar
  928. },
  929. senderId: (item.source==1?item.openid:item.service_id),
  930. messageId: item.msgId,
  931. payload: payload,
  932. timestamp: item.time,
  933. type: item.type,
  934. recalled: false,
  935. status: 'success',
  936. isHide: 0
  937. };
  938. msgData[ix] = message
  939. });
  940. this.history.messages = [...this.history.messages, ...msgData];
  941. this.history.allLoaded = false;
  942. } else {
  943. this.history.allLoaded = true;
  944. }
  945. },
  946. /**
  947. * 首次加载
  948. */
  949. async getMsgData(){
  950. var resp = await this.$API.chat.msg.get({"page":this.msgPage,"size":this.msgSize,"openid":this.chatItem.openid})
  951. if (resp.data.rows.length > 0) {
  952. var msgData = resp.data.rows
  953. msgData.forEach((item,index)=>{
  954. this.formatMsg(item);
  955. })
  956. this.msgPage ++;
  957. }
  958. },
  959. async uploadSuccess(data){
  960. var resp = await this.sendMessageApi("image",data.data.src);
  961. if (!resp) {
  962. return ;
  963. }
  964. this.sendMessageData({
  965. payload: {
  966. contentType: 'image/png',
  967. name: data.data.fileName,
  968. size: 0,
  969. url: data.data.src,
  970. width: 2732,
  971. height: 2732,
  972. thumbnail: data.data.src
  973. },
  974. type: 'image'
  975. });
  976. this.uploadImg = "";
  977. },
  978. async sendMessageApi(type,content){
  979. this.chatContact[this.checkChat].last.num = 0;
  980. var resp = await this.$API.chat.send.post({type:type,content: content,groupId: this.chatItem.poi_id,openid:this.chatItem.openid})
  981. if (resp.code == 0) return false;
  982. this.chatContact[this.checkChat].last.type = type;
  983. this.chatContact[this.checkChat].last.content = (type=='text'?this.truncateByBytes(content,18):content);
  984. this.chatContact[this.checkChat].last.create_at = resp.data.time;
  985. return true;
  986. },
  987. doneOrder(data){
  988. this.$confirm(`手动核销订单后,该订单不支持线上退款,如需退款需与客户线下进行处理❗️如商家原因拒绝退款,平台会代替商家先行垫付且店铺无法正常经营`, '注意事项', {
  989. type: 'warning'
  990. }).then(async ()=>{
  991. this.isDoneing = true;
  992. var resp = await this.$API.merOrder.done.post({"id":data.order_sn});
  993. this.isDoneing = false;
  994. if (resp.code == 0) {
  995. return this.$message.error(resp.msg);
  996. }
  997. this.$message.success(resp.msg);
  998. this.getOrderList()
  999. }).catch(()=>{})
  1000. },
  1001. async getOrderList(){
  1002. this.orderData.loading = true;
  1003. var resp = await this.$API.order.list.get({"page":this.orderData.page,"pageSize":this.orderData.size,"order":this.orderData.order,"openid":this.chatItem.openid});
  1004. this.orderData.loading = false;
  1005. if (resp.code == 0) {
  1006. return ;
  1007. }
  1008. this.orderData.list = resp.data.rows;
  1009. this.orderData.total = resp.data.total;
  1010. }
  1011. }
  1012. }
  1013. </script>
  1014. <style scoped lang="scss">
  1015. @import '@/style/index.scss';
  1016. .order-info-user{background-color: #fff;padding: 10px;margin-top: 10px;}
  1017. .order-info-user .title{font-size: 16px;color: #333;font-weight: bold;padding-bottom: 10px;border-bottom: 1px solid #f8f8f8;}
  1018. .order-info-user .info-item{height: 40px;line-height: 40px;font-size: 12px;display: flex;align-items: center;border-bottom: 1px solid #f8f8f8;}
  1019. .order-info-user .info-item .right{color: #666;margin-left: auto;margin-right: 0;}
  1020. .order-info-user .info-item .right text{border: 1px solid #ccc;color: #333;font-size: 12px;padding: 0 15upx;margin-left: 10px;border-radius: 5px;}
  1021. .aside-tabs{display: flex;height: 65px;align-items: center;gap: 10px;border-bottom: 1px solid var(--el-border-color-light);padding: 13px 15px;}
  1022. .aside-tabs .aside-tab-items{font-size: 14px;cursor: pointer;padding: 0 20px;}
  1023. .aside-tabs .aside-tab-items.active{color: var(--el-color-primary);}
  1024. .quick-search{display: flex;align-items: center;justify-content: space-between;gap: 10px;}
  1025. .quick-search .search-input{flex: 1;display: flex;align-items: center;gap: 5px;}
  1026. .quick-search .search-btn-txt{font-size: 14px;color: #409eff;cursor: pointer;}
  1027. .quick-table .quick-table-body{margin: 10px 0;}
  1028. .border-none{border: 0;}
  1029. .msg-title{font-size: 18px;font-weight: bold;}
  1030. .loading-text{background-color: #f8f8f8;text-align: center;font-size: 12px;}
  1031. .order-list-chat{height: 100%;flex-direction: column;flex: 1;display: flex;}
  1032. .m-order-s{background-color: #fff;border-radius: 5px;margin-bottom: 10px;border: 1px solid #f8f8f8;}
  1033. .m-order-s .m-order-item{display: flex;font-size: 14px;position: relative;background-color: #f8f8f8;}
  1034. .m-order-s .order-btn-group{display: flex;align-items: center;padding: 10px;gap: 10px;}
  1035. .order-search-input{display: flex;align-items: center;gap: 10px;flex: 1;}
  1036. .m-order-s .order-img{display: flex;}
  1037. .m-order-s .order-img image{width: 80px;height: 80px;}
  1038. .m-order-s .order-info {padding: 5px 10px 10px;flex: 1;}
  1039. .m-order-s .order-info .title{font-size: 14px;overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical;}
  1040. .m-order-s .order-info .desc{font-size: 12px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;color: #999;}
  1041. .m-order-s .order-info .price{font-size: 14px;color: #f00;}
  1042. .m-order-s .order-status-btn{position: absolute;right: 0;bottom: 0;display: flex;align-items: center;gap: 5px;}
  1043. .m-order-s .status{border-radius: 5px 0 5px 0;background-color: #909399;font-size: 12px;color: #fff;padding: 5px;}
  1044. .m-order-s .status.cus{cursor: pointer;}
  1045. .m-order-s .status.info{background-color: #909399;}
  1046. .m-order-s .status.primary{background-color: #409eff;}
  1047. .m-order-s .status.success{background-color: #67c23a;}
  1048. .m-order-s .status.danger{background-color: #f56c6c;}
  1049. .m-order-s .status.warn{background-color: #e6a23c;}
  1050. .hideStr{overflow: hidden;width: 80%;}
  1051. .contact-item{display: flex;align-items: center;background-color: #fff;padding: 10px;border-bottom: 1px solid #f8f8f8;cursor: pointer;}
  1052. .contact-item.active{background-color: #f8f8f8;}
  1053. .contact-item .img{position: relative;border: 1px solid #f8f8f8;border-radius: 5px;display: flex;align-items: center;justify-content: center;}
  1054. .contact-item span.last-num{background-color: #f00;color: #fff;width: 20px;height: 20px;display: flex;align-items: center;justify-content: center;border-radius: 20px;font-size: 12px;margin-left: 10px;position: absolute;right: 0;top: 0;}
  1055. .contact-item .kefu-store{margin-left: 10px;}
  1056. .contact-item .kefu-store .name{font-size: 14px;color: #333;display: flex;align-items: center;}
  1057. .contact-item .kefu-store .desc{font-size: 12px;color: #666;margin-top: 5px;}
  1058. .contact-item .time{margin-left: auto;margin-right: 0;font-size: 12px;color: #666;}
  1059. .send-btn-group{margin-top: 10px;display: flex;align-items: center;justify-content: space-between;}
  1060. .send-btn-group .send-emoji{display: flex;align-items: center;gap: 15px;cursor: pointer;}
  1061. .quick-group{display: flex;align-items: center;gap: 10px;padding: 15px 0;}
  1062. .quick-group .item-btn{background-color: #f8f8f8;padding: 5px;border-radius: 3px;display: flex;align-items: center;gap: 5px;cursor: pointer;}
  1063. .z_index2{position: relative;z-index: 2;}
  1064. .empty-data{display: flex;width: 100%;text-align: center;justify-content: center;font-size: 18px;background-color: #f8f8f8;align-items: center;}
  1065. .swiper-item-box {
  1066. position: relative;
  1067. z-index: 1;
  1068. box-sizing: border-box;
  1069. width: 100%;
  1070. height: 100%;
  1071. max-height: 300px;
  1072. overflow: auto;
  1073. flex-wrap: wrap;
  1074. align-items: flex-start;
  1075. .swiper-item-box-l {
  1076. // width: calc(62.5vw);
  1077. width: calc(100%);
  1078. flex-wrap: wrap;
  1079. align-items: flex-start;
  1080. }
  1081. .swiper-item-box-r {
  1082. width: calc(37.5%);
  1083. flex-wrap: wrap;
  1084. align-items: flex-start;
  1085. }
  1086. .emoji-item {
  1087. width: calc(12.5%);
  1088. height: calc(12.5%);
  1089. flex-shrink: 0;
  1090. transition: all 0.1s;
  1091. padding: 10px 0;
  1092. .img {
  1093. width: 55%;
  1094. height: 55%;
  1095. cursor: pointer;
  1096. }
  1097. &:hover {
  1098. background-color: #f8f8f8;
  1099. }
  1100. }
  1101. }
  1102. .flex_r {
  1103. display: flex;
  1104. flex-direction: row;
  1105. }
  1106. .swiper-item-box-b {
  1107. width: calc(38%);
  1108. position: absolute;
  1109. z-index: 99;
  1110. right: 0rpx;
  1111. bottom: 0px;
  1112. padding: 0 0 40rpx 0;
  1113. // background-color: #000;
  1114. background-image: linear-gradient(to top, #ececec, #ececec, #ececec, #ececec, #ececec, rgba(0, 0, 0, 0));
  1115. }
  1116. .swiper-item-box-delete {
  1117. width: 116rpx;
  1118. height: calc(12.5%);
  1119. border-radius: 10rpx;
  1120. margin-right: 20rpx;
  1121. background-color: #fff;
  1122. .img {
  1123. width: 45%;
  1124. height: 45%;
  1125. margin-right: 4rpx;
  1126. }
  1127. }
  1128. .swiper-item-box-button {
  1129. width: 116rpx;
  1130. height: calc(12.5vw);
  1131. border-radius: 10rpx;
  1132. margin-right: 10rpx;
  1133. background-color: #05c160;
  1134. }
  1135. .page {
  1136. position: fixed;
  1137. z-index: 1;
  1138. top: 0;
  1139. left: 0;
  1140. bottom: 0;
  1141. right: 0;
  1142. background-color: #ededed;
  1143. }
  1144. .scroll-Y {
  1145. width: 100%;
  1146. height: 0;
  1147. transition: all 0.2s;
  1148. transform: rotate(180deg);
  1149. background-color: #ededed;
  1150. ::-webkit-scrollbar {
  1151. display: none;
  1152. }
  1153. }
  1154. .scroll-view-str {
  1155. width: 100%;
  1156. }
  1157. .message-list .time {
  1158. width: 100%;
  1159. color: #a3a3a3;
  1160. line-height: 50px;
  1161. }
  1162. .message-list .recalled {
  1163. width: 100%;
  1164. height: 50rpx;
  1165. margin: 20rpx 0;
  1166. color: #a3a3a3;
  1167. .recalled-edit {
  1168. color: #5a6693;
  1169. margin-left: 14rpx;
  1170. }
  1171. }
  1172. @keyframes simple-blink {
  1173. 0%, 100% { opacity: 1; }
  1174. 50% { opacity: 0; }
  1175. }
  1176. .global-im {
  1177. .unreadCount{
  1178. position: fixed;
  1179. right: 42px;
  1180. bottom: 162px;
  1181. animation: simple-blink 2s ease-in-out infinite;
  1182. }
  1183. .im-trigger {
  1184. position: fixed;
  1185. right: 24px;
  1186. bottom: 124px;
  1187. width: 48px;
  1188. height: 48px;
  1189. background: #409eff;
  1190. border-radius: 50%;
  1191. display: flex;
  1192. align-items: center;
  1193. justify-content: center;
  1194. cursor: pointer;
  1195. box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
  1196. z-index: 2000;
  1197. transition: all 0.3s;
  1198. color: white;
  1199. &:hover {
  1200. background: #66b1ff;
  1201. }
  1202. &.active {
  1203. background: #909399;
  1204. }
  1205. }
  1206. }
  1207. .chat-container {
  1208. display: flex;
  1209. flex-direction: column;
  1210. height: 100%;
  1211. .message-list {
  1212. flex: 1;
  1213. overflow-y: auto;
  1214. background-color: #f8f8f8;
  1215. transition: all 0.2s;
  1216. transform: rotate(180deg);
  1217. .message-item {
  1218. display: flex;
  1219. margin-bottom: 16px;
  1220. &.self {
  1221. flex-direction: row-reverse;
  1222. .content {
  1223. margin-right: 12px;
  1224. align-items: flex-end;
  1225. .text {
  1226. background: #95ec69;
  1227. }
  1228. }
  1229. }
  1230. &.other {
  1231. .content {
  1232. margin-left: 12px;
  1233. }
  1234. }
  1235. .content {
  1236. display: flex;
  1237. flex-direction: column;
  1238. max-width: 70%;
  1239. .name {
  1240. font-size: 12px;
  1241. color: #999;
  1242. margin-bottom: 4px;
  1243. }
  1244. .text {
  1245. padding: 8px 12px;
  1246. background: #f0f0f0;
  1247. border-radius: 12px;
  1248. word-break: break-all;
  1249. }
  1250. .time {
  1251. font-size: 10px;
  1252. color: #ccc;
  1253. margin-top: 4px;
  1254. }
  1255. }
  1256. }
  1257. }
  1258. .input-area {
  1259. padding: 0 16px 16px 16px;
  1260. border-top: 1px solid #eee;
  1261. }
  1262. }
  1263. </style>