chat.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. <template>
  2. <view>
  3. <view class="flex_c page" @touchmove="touchmove">
  4. <scroll-view class="flex1 scroll-Y" @tap.stop="onPage" id="scroll-view" lower-threshold="100" scroll-y
  5. scroll-with-animation :scroll-top="scroll_top" @scroll="scroll" @scrolltoupper="scrolltoupper"
  6. @scrolltolower="scrolltolower">
  7. <view class="scroll-view-str" :style="{height: `${reserveHeight}px`}" v-if="reserveHeight>0"></view>
  8. <view class="messageList_">
  9. <template v-for="(item, index) in history.messages">
  10. <!-- #ifdef APP || H5 -->
  11. <view class="z_index2" :class="`oneheight_${index}`" style="transform: rotate(-180deg)"
  12. :key="item.messageId + index" v-if="!item.isHide">
  13. <view class="icon_ text_26 color__ time">
  14. {{ renderMessageDate(item, index) }}
  15. </view>
  16. <view :key="item.messageId + index" v-if="!item.recalled">
  17. <item :isMy="isSelf(item.senderId)" :myid="myid" :item="item" @onClick="onItem"
  18. @onLongpress="onLongpress" @mention="mention" @imgLoad="imgLoad"></item>
  19. </view>
  20. <view class="icon_ text_26 recalled" v-else>
  21. <view class="">
  22. <text v-if="isSelf(item.senderId)">你</text>
  23. <text v-else>{{ item.senderData.name }}</text>
  24. 撤回了一条消息
  25. </view>
  26. <view class="recalled-edit" v-if="item.type === 'text' && isSelf(item.senderId)"
  27. @click="recalledEdit(item)">重新编辑</view>
  28. </view>
  29. </view>
  30. <!-- #endif -->
  31. <!-- #ifdef MP -->
  32. <view class="z_index2" style="transform: rotate(-180deg)" :key="item.messageId"
  33. v-if="!item.isHide">
  34. <view class="icon_ text_26 color__ time">
  35. {{ renderMessageDate(item, index) }}
  36. </view>
  37. <view :key="item.messageIds" v-if="!item.recalled">
  38. <item :isMy="isSelf(item.senderId)" :myid="myid" :item="item" @onClick="onItem"
  39. @onLongpress="onLongpress" @mention="mention"></item>
  40. </view>
  41. <view class="icon_ text_26 recalled" v-else>
  42. <view class="">
  43. <text v-if="isSelf(item.senderId)">你</text>
  44. <text v-else>{{ item.senderData.name }}</text>
  45. 撤回了一条消息
  46. </view>
  47. <view class="recalled-edit" v-if="item.type === 'text' && isSelf(item.senderId)"
  48. @click="recalledEdit(item)">重新编辑</view>
  49. </view>
  50. </view>
  51. <!-- #endif -->
  52. </template>
  53. </view>
  54. </scroll-view>
  55. <view class="bottomOperationRef">
  56. <view class="bottom-btn-group">
  57. <view class="btn-item">
  58. <image src="https://jymini.oss-cn-guangzhou.aliyuncs.com/mini/star.svg"></image>
  59. <view class="name">评价服务</view>
  60. </view>
  61. <view class="btn-item" @click="showOrder(1)">
  62. <image src="https://jymini.oss-cn-guangzhou.aliyuncs.com/mini/order.svg"></image>
  63. <view class="name">我的订单</view>
  64. </view>
  65. </view>
  66. <view class="bottom-operation-box">
  67. <view class="input-text">
  68. <textarea class="input" auto-height="true" confirm-type="send" type="text" :focus="isFocus"
  69. :maxlength="-1" :adjust-position="false" v-model="text" confirm-hold
  70. :show-confirm-bar="false" @input="input" @confirm="sendingText" @focus="focus"
  71. @blur="blured" @keyboardheightchange="keyboardheightchange" />
  72. </view>
  73. <view class="check-img">
  74. <view class="check-icon" @click="tapEmoji"><image src="@/static/image/face.svg" mode="aspectFill"></image></view>
  75. <view class="check-icon" @click="tapMore"><image src="@/static/image/more.svg" mode="aspectFill"></image></view>
  76. </view>
  77. </view>
  78. <view class="safe-box"></view>
  79. <view class="keyboardheight" :style="{ height: keyboardHeight + 'px' }"></view>
  80. </view>
  81. <view>
  82. <emoji v-model="isEmoji" @onEmoji="onEmoji" @deleteFn="deleteFn" @sendingText="sendingText"
  83. @sendingEmojiPack="sendingEmojiPack"></emoji>
  84. </view>
  85. <view>
  86. <more v-model="isMore" @onMore="onMore"></more>
  87. </view>
  88. </view>
  89. <uni-popup ref="orderpopup" type="bottom" border-radius="10px 10px 0 0" background-color="#ffffff" :mask-click="false">
  90. <view class="order-popup">
  91. <view class="order-title">
  92. <view class="name">我的订单</view>
  93. <view class="close" @click="showOrder(2)"><image src="/static/image/close.png"></image></view>
  94. </view>
  95. <view class="order-body"></view>
  96. </view>
  97. </uni-popup>
  98. </view>
  99. </template>
  100. <script>
  101. var app;
  102. let getSelectedTextRangeSetInterval = null;
  103. let inputValue = '';
  104. let cursor = 0; //输入框光标
  105. let scroll_top = 0;
  106. import emoji from '@/components/bottom-operation/emoji.vue';
  107. import more from '@/components/bottom-operation/more.vue';
  108. import item from '@/components/item/index';
  109. // 浏览照片数组
  110. let imageList = [];
  111. // 是否是手动触发的列表滑动
  112. let isBottomOperationScrollToBottom = false;
  113. import {
  114. show,
  115. formatDate,
  116. throttle,
  117. openimg,
  118. getLocation,
  119. to as tofn
  120. } from '@/utils/index.js';
  121. import {
  122. mapState
  123. } from 'vuex';
  124. var {Push} = require("@/static/js/push.js")
  125. import * as Api from "@/static/api/service.js";
  126. export default {
  127. components: {
  128. emoji,
  129. more,
  130. item
  131. },
  132. data() {
  133. return {
  134. serviceId:null, // 客服ID
  135. reserveHeight:0,
  136. isFocus: false, //键盘焦点
  137. isEmoji:false,
  138. isMore:false,
  139. keyboardHeight:0,
  140. optionsData:null,
  141. testData:null,
  142. text:"",
  143. // 历史数据
  144. history: {
  145. messages: [],
  146. allLoaded: false
  147. },
  148. showOrderState:false,
  149. storeData:null,
  150. sendId:null,
  151. myid:"",
  152. pushObj:null,
  153. wsAuth:"http://192.168.3.16:9881/plugin/webman/push/auth",
  154. // wsAuth:"https://panel.huiyinduo.cn/plugin/webman/push/auth",
  155. wsUrl: 'ws://192.168.3.16:3131',
  156. page:2,
  157. size:10
  158. }
  159. },
  160. computed: mapState({
  161. //显示时间
  162. renderMessageDate() {
  163. return (message, index) => {
  164. if (message.timestamp - this.history.messages[index + 1]?.timestamp > 3 * 60 * 1000) {
  165. return formatDate(message.timestamp, 'timestamp');
  166. }
  167. return '';
  168. };
  169. },
  170. // 是否本人isMy
  171. isSelf() {
  172. return (senderId) => {
  173. const {
  174. member_id = this.myid
  175. } = {"member_id":this.myid};
  176. return senderId === `${member_id}`;
  177. };
  178. },
  179. }),
  180. onLoad(options) {
  181. var app = this;
  182. app.optionsData = options;
  183. uni.setNavigationBarTitle({
  184. title:"加载中..."
  185. });
  186. app.getService(options)
  187. },
  188. beforeDestroy() {
  189. cursor = 0;
  190. this.pushObj.disconnect()
  191. },
  192. onPageScroll(e) {
  193. // this.$refs.bottomOperationRef.closeAll();
  194. },
  195. onReady() {
  196. // this.videoPlayer.context = uni.createVideoContext('videoPlayer', this);
  197. },
  198. methods: {
  199. initPush(){
  200. var _this = this;
  201. this.pushObj = new Push({
  202. "url":this.wsUrl,
  203. "app_key":"265c33b73d5c04f918978577df2c48d2",
  204. "auth":this.wsAuth
  205. });
  206. var user_channel = this.pushObj.subscribe('user-' + this.myid);
  207. user_channel.on('message', function (data) {
  208. // console.log(data);
  209. _this.formatMsg(data);
  210. })
  211. },
  212. imgLoad(data){},
  213. onItem(data){
  214. console.log(data)
  215. switch(data.type) {
  216. case "order":
  217. var order = data.payload.order;
  218. break;
  219. case "image":
  220. uni.previewImage({
  221. urls:[data.payload.url]
  222. })
  223. break;
  224. case "pay":
  225. var payInfo = data.payload.pay;
  226. if (payInfo.status == 1) {
  227. return this.$dialog.showSuccess("已完成支付")
  228. }
  229. Api.pricePay({order:payInfo.order}).then((res)=>{
  230. if (res.code == 0) {
  231. return app.$dialog.showSuccess(res.msg)
  232. }
  233. uni.requestPayment({
  234. orderInfo:res.data.pay,
  235. service:5,
  236. success(resp){
  237. if (resp.code == 0) { // res.code=0时,才表示支付成功
  238. app.$dialog.showSuccess("支付成功","none",function(){
  239. app.getService(app.optionsData)
  240. });
  241. }
  242. },
  243. fail(resp) {
  244. app.$dialog.showSuccess("支付失败,可再次重试","none");
  245. }
  246. })
  247. })
  248. break;
  249. default:
  250. break;
  251. }
  252. },
  253. onLongpress(data){
  254. },
  255. // 滚动中
  256. scroll(e) {
  257. scroll_top = e.detail.scrollTop;
  258. // this.$refs.operateRef.close();
  259. if (isBottomOperationScrollToBottom) return;
  260. // this.$refs.bottomOperationRef.closeAll();
  261. },
  262. // 滚动到底部
  263. scrolltolower() {
  264. if (this.history.allLoaded) return;
  265. console.log('触底')
  266. this.getMoreMsg()
  267. // this.loadHistoryMessage();
  268. },
  269. // 滚动到顶部
  270. scrolltoupper() {
  271. console.log('滚动到顶部')
  272. },
  273. showOrder(type){
  274. if (type == 2) {
  275. this.$refs.orderpopup.close()
  276. return ;
  277. }
  278. this.$refs.orderpopup.open()
  279. },
  280. getMoreMsg(){
  281. this.history.allLoaded = true;
  282. var formData = this.optionsData;
  283. formData.page = this.page;
  284. Api.msg(formData).then((res)=>{
  285. if(res.code == 0) {
  286. return this.$dialog.showSuccess(res.msg)
  287. }
  288. if (res.data.rows.length > 0) {
  289. this.page ++;
  290. var msgData = []
  291. var list = res.data.rows
  292. // 同步混入数据
  293. list.forEach((item, ix) => {
  294. var payload = {};
  295. if (item.type == 'text') {
  296. payload.text = item.content
  297. }
  298. if (item.type == 'order') {
  299. var order = JSON.parse(item.content)
  300. payload.order = order
  301. }
  302. if (item.type == 'pay') {
  303. var order = JSON.parse(item.content)
  304. payload.pay = order
  305. }
  306. if (item.type == 'image') {
  307. payload = {
  308. contentType: 'image/png',
  309. name: 'uni-image.png',
  310. size: 82942,
  311. url: item.content,
  312. width: 2732,
  313. height: 2732,
  314. thumbnail: item.content
  315. };
  316. }
  317. const message = {
  318. groupId: item.poi_id,
  319. senderData: {
  320. avatar:item.avatar
  321. },
  322. senderId: (item.source==1?item.openid:item.service_id),
  323. messageId: item.msgId,
  324. payload: payload,
  325. timestamp: item.time,
  326. type: item.type,
  327. recalled: false,
  328. status: 'success',
  329. isHide: 0
  330. };
  331. msgData[ix] = message
  332. });
  333. // console.log(msgData)
  334. // 模拟只有少量数据
  335. // this.history.messages = [list[0],list[1],list[2]];
  336. this.history.messages = [...this.history.messages, ...msgData];
  337. this.history.allLoaded = false;
  338. // if (this.history.messages.length > 20) return;
  339. // msgData.forEach((item,index)=>{
  340. // this.formatMsg(item);
  341. // })
  342. } else {
  343. this.history.allLoaded = true;
  344. }
  345. })
  346. },
  347. getService(options){
  348. Api.shareout(options).then((res)=>{
  349. if (res.code && res.code == 0) {
  350. return app.$dialog.showSuccess(res.msg)
  351. }
  352. console.log(res.data)
  353. uni.setNavigationBarTitle({
  354. title:"与"+res.data.store.poi_name+"对话中"
  355. })
  356. this.storeData = res.data.store;
  357. this.sendId = res.data.sendId;
  358. this.serviceId = res.data.serviceId;
  359. this.myid = res.data.sendId;
  360. if (res.data.code == 3) { // 无客服在线
  361. }
  362. if (res.data.msg.rows.length > 0) {
  363. var msgData = res.data.msg.rows
  364. msgData.forEach((item,index)=>{
  365. this.formatMsg(item);
  366. })
  367. }
  368. this.initPush()
  369. })
  370. },
  371. formatMsg(item){
  372. var payload = {};
  373. if (item.type == 'text') {
  374. payload.text = item.content
  375. }
  376. if (item.type == 'order') {
  377. var order = JSON.parse(item.content)
  378. payload.order = order
  379. }
  380. if (item.type == 'pay') {
  381. var order = JSON.parse(item.content)
  382. payload.pay = order
  383. }
  384. if (item.type == 'image') {
  385. payload = {
  386. contentType: 'image/png',
  387. name: 'uni-image.png',
  388. size: 82942,
  389. url: item.content,
  390. width: 2732,
  391. height: 2732,
  392. thumbnail: item.content
  393. };
  394. }
  395. const message = {
  396. groupId: item.poi_id,
  397. senderData: {
  398. avatar:item.avatar
  399. },
  400. senderId: (item.source==1?item.openid:item.service_id),
  401. messageId: item.msgId,
  402. payload: payload,
  403. timestamp: item.time,
  404. type: item.type,
  405. recalled: false,
  406. status: 'success',
  407. isHide: 0
  408. };
  409. this.pushList(message);
  410. },
  411. // 更多操作相关===============
  412. onMore(item) {
  413. // console.log(item)
  414. switch (item.type) {
  415. case 'img':
  416. this.sendImageMessage();
  417. break;
  418. case 'video':
  419. this.sendVideoMessage();
  420. break;
  421. case 'order':
  422. this.sendOrder();
  423. break;
  424. }
  425. },
  426. sendOrder(){
  427. },
  428. // 创建发送照片内容
  429. sendImageMessage() {
  430. console.log('upload')
  431. uni.chooseImage({
  432. count: 1,
  433. sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
  434. success: async (res) => {
  435. res.tempFiles.forEach((file) => {
  436. console.log(file);
  437. this.createImageMessage(file);
  438. });
  439. },
  440. fail(e) {
  441. console.log("选择失败",e)
  442. }
  443. });
  444. },
  445. // 创建发送照片内容
  446. createImageMessage(file) {
  447. this.sendMessage({
  448. payload: {
  449. contentType: 'image/png',
  450. name: 'uni-image.png',
  451. size: 82942,
  452. url: file.path,
  453. width: 2732,
  454. height: 2732,
  455. thumbnail: file.path
  456. },
  457. type: 'image'
  458. });
  459. },
  460. // 创建发送视频内容
  461. sendVideoMessage() {
  462. uni.chooseVideo({
  463. success: async (res) => {
  464. console.log(res);
  465. this.createVideoMessage(res);
  466. }
  467. });
  468. },
  469. createVideoMessage(file) {
  470. this.sendMessage({
  471. payload: {
  472. video: {
  473. name: '3003009356267921_uni-video.mp4',
  474. url: file.tempFilePath,
  475. width: 640,
  476. height: 352,
  477. contentType: 'video/mp4',
  478. size: 501774,
  479. duration: 8.32
  480. },
  481. thumbnail: {
  482. name: 'uni-thumbnail.jpg',
  483. url: '封面路径',
  484. width: 364,
  485. height: 200,
  486. contentType: 'image/jpg'
  487. }
  488. },
  489. type: 'video'
  490. });
  491. },
  492. sendMessage({payload,type}){
  493. const message = {
  494. groupId: this.storeData.poi_id,
  495. senderData: {},
  496. senderId: this.sendId,
  497. messageId: Date.now(),
  498. payload: payload,
  499. timestamp: Date.now(),
  500. type: type,
  501. recalled: false,
  502. status: 'success',
  503. isHide: 0
  504. };
  505. this.pushList(message);
  506. },
  507. // 组装item
  508. initMessageItem(message, index) {
  509. message['isHide'] = 0;
  510. if (index === 0 && (message.type === 'text' || message.type === 'text_quote')) {
  511. this.onSetText(message.payload.text);
  512. }
  513. },
  514. // 文本触发效果相关========
  515. onSetText(text) {
  516. // 触发礼花
  517. throttle(() => {
  518. if (text.includes('[彩带]')) {
  519. this.$refs.mScreenAnimationLihua.show();
  520. uni.vibrateLong();
  521. }
  522. }, 4000);
  523. },
  524. sendingEmojiPack(){
  525. },
  526. // 删除表情
  527. deleteFn() {
  528. const str = this.text.charAt(this.text.length - 1);
  529. if (str === ']') {
  530. let metaChars = /\[.*?(\u4e00*\u597d*)\]/g;
  531. let xstr = '';
  532. this.text.replace(metaChars, (match) => {
  533. xstr = match;
  534. });
  535. var text = this.text;
  536. function del(str) {
  537. return text.slice(0, text.length - str.length);
  538. }
  539. this.text = del(xstr);
  540. } else {
  541. this.text = this.text.substring(0, this.text.length - 1);
  542. }
  543. },
  544. onEmoji(key) {
  545. const text = `${this.text.slice(0, cursor)}${key}${this.text.slice(cursor)}`;
  546. this.text = text;
  547. },
  548. tapEmoji() {
  549. this.isEmoji = !this.isEmoji;
  550. if (this.isEmoji) {
  551. this.isKeyboard = true;
  552. }
  553. this.isMore = false;
  554. },
  555. tapMore() {
  556. this.isMore = !this.isMore;
  557. this.isEmoji = false;
  558. },
  559. touchmove(){
  560. this.isFocus = false;
  561. this.isFocisEmojius = false;
  562. this.isMore = false;
  563. this.isKeyboard = false;
  564. this.keyboardHeight = 0
  565. },
  566. onPage(){},
  567. // 监听输入
  568. input() {
  569. // if (inputValue.length > this.text.length) {} else {
  570. // const str = this.text.charAt(this.text.length - 1);
  571. // }
  572. // inputValue = this.text;
  573. },
  574. blured(){
  575. this.isFocus = false;
  576. this.keyboardHeight = 0
  577. },
  578. // 获取焦点
  579. focus(e) {
  580. this.testData = JSON.stringify(e.detail);
  581. this.isFocus = true;
  582. this.isEmoji = false;
  583. this.isMore = false;
  584. this.keyboardHeight = (e.detail.height==100)?0:e.detail.height
  585. },
  586. keyboardheightchange(e){
  587. // app.$dialog.showSuccess('asdasdasd')
  588. console.log(e)
  589. },
  590. sendingText() {
  591. if (this.text === '')
  592. return ;
  593. let body = this.text;
  594. if (this.text.length >= 50) {
  595. body = this.text.substring(0, 30) + '...';
  596. }
  597. Api.send({type:"text",content: this.text,sendId: this.serviceId,groupId: this.storeData.poi_id}).then((res)=>{
  598. if (res.code == 0) {
  599. return this.$dialog.showSuccess(res.msg)
  600. }
  601. this.sendMessage({
  602. payload: {
  603. text: this.text
  604. },
  605. type: 'text'
  606. });
  607. this.text = '';
  608. })
  609. },
  610. async pushList(message) {
  611. this.initMessageItem(message);
  612. this.history.messages.unshift(message);
  613. // 是否触发文字动效果
  614. if (message.type === 'text' || message.type === 'text_quote') {
  615. this.onSetText(message.payload.text);
  616. }
  617. // 缓存照片地址,
  618. if (message.type === 'image' || message.type === 'image_transmit') {
  619. imageList.push(message.payload.url);
  620. }
  621. },
  622. }
  623. }
  624. </script>
  625. <style lang="scss">
  626. @import '@/static/index.scss';
  627. .order-popup .order-title{display: flex;align-items: center;justify-content: space-between;font-size: 28upx;padding: 20upx;}
  628. .order-popup .order-title image{width: 40upx;height: 40upx;}
  629. .order-popup .order-body{height: 40vh;overflow: auto;}
  630. .bottomOperationRef{background-color: #f6f6f6;}
  631. .bottom-btn-group{display: flex;gap: 20upx;padding: 10rpx 14rpx;}
  632. .bottom-btn-group .btn-item{display: flex;align-items: center;gap: 10upx;font-size: 24upx;background-color: #f6f6f6;padding: 10rpx 14rpx;border-radius: 10upx;}
  633. .bottom-btn-group .btn-item image{width: 30upx;height: 30upx;}
  634. .bottom-operation-box{display: flex;padding: 20rpx 20rpx 0 20rpx;gap: 20rpx;align-items: flex-end;}
  635. .bottom-operation-box .check-img{display: flex;align-items: center;justify-content: center;}
  636. .bottom-operation-box .check-img .check-icon{width: 84rpx;height: 84rpx;display: flex;align-items: center;justify-content: center;}
  637. .bottom-operation-box .check-img image{width: 75%;height: 75%;}
  638. .bottom-operation-box .input-text{flex: 1;width: 100%;
  639. box-sizing: border-box;
  640. padding: 10rpx 14rpx;
  641. min-height: 84rpx;
  642. max-height: 300rpx;
  643. overflow: auto;
  644. border-radius: 10rpx;
  645. background-color: #fff}
  646. .bottom-operation-box .input{margin: 10rpx 0;width: 100%;font-size: 24upx;height: 100%;background-color: transparent;min-height: 48rpx;
  647. max-height: 300rpx;}
  648. .safe-box{width: 100%;height: env(safe-area-inset-bottom);background-color: #f6f6f6;}
  649. .page {
  650. position: fixed;
  651. z-index: 1;
  652. top: 0;
  653. left: 0;
  654. bottom: 0;
  655. right: 0;
  656. background-color: #ededed;
  657. }
  658. .scroll-Y {
  659. width: 100%;
  660. height: 0;
  661. transition: all 0.2s;
  662. transform: rotate(180deg);
  663. background-color: #ededed;
  664. padding: 20upx 0;
  665. ::-webkit-scrollbar {
  666. display: none;
  667. }
  668. }
  669. .scroll-view-str {
  670. width: 100%;
  671. }
  672. .time {
  673. width: 100%;
  674. color: #a3a3a3;
  675. line-height: 100rpx;
  676. }
  677. .recalled {
  678. width: 100%;
  679. height: 50rpx;
  680. margin: 20rpx 0;
  681. color: #a3a3a3;
  682. .recalled-edit {
  683. color: #5a6693;
  684. margin-left: 14rpx;
  685. }
  686. }
  687. </style>