info.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. <template>
  2. <mobile-frame>
  3. <view class="main">
  4. <view class="one">
  5. <!-- 聊天内容 -->
  6. <scroll-view class="scroll-view" scroll-y="true" scroll-with-animation="true"
  7. :scroll-into-view="scrollToView" refresher-enabled="true" :refresher-triggered="triggered"
  8. @refresherrefresh="getFresh">
  9. <view class="list-scroll-view">
  10. <view class="chat-ls" v-for="(item,index) in msgList" :key="index" :id="'msg'+ item._id">
  11. <view class="chat-time" v-if="item.time != ''">
  12. {{item.time}}
  13. </view>
  14. <view class="msg-m msg-left" v-if="item.speaker !=  user._id">
  15. <image @tap="toShop()" class="user-img"
  16. :src="shop.logo&&shop.logo.length>0?shop.logo[0].url:''">
  17. </image>
  18. <!-- 文字 -->
  19. <view class="message" v-if="item.msg_type =='0'">                           
  20. <view class="msg-text">{{item.content}}</view>
  21. </view>
  22. <!-- 图像 -->
  23. <view class="message img" v-else-if="item.msg_type =='1'"
  24. @tap="previewImg(item.content)">
  25. <image :src="item.content" class="msg-img" mode="widthFix"></image>
  26. </view>
  27. <!-- 商品 -->
  28. <view class="message img" v-else-if="item.msg_type =='2'">
  29. <view class="msg-goods">
  30. <image :src="item.goodsInfo&&item.goodsInfo.file" class="image" mode="widthFix">
  31. </image>
  32. <view class="goods_1">
  33. <view class="name textOver">{{item.goodsInfo&&item.goodsInfo.name}}</view>
  34. <view class="money">
  35. <text class="money_1"
  36. v-if="item.goodsInfo&&item.goodsInfo.spec&&item.goodsInfo.spec.leader_price"><text>团长价¥</text>{{item.goodsInfo.spec.leader_price||0}}</text>
  37. <text class="money_2"
  38. v-else-if="item.goodsInfo&&item.goodsInfo.spec&&item.goodsInfo.spec.price"><text>特价¥</text>{{item.goodsInfo.spec.price||0}}</text>
  39. <text class="money_2"
  40. v-else><text>¥</text>{{item.goodsInfo.spec.sell_money||0}}</text>
  41. <text
  42. class="money_2"><text>¥</text>{{item.goodsInfo.spec.flow_money||0}}</text>
  43. </view>
  44. <view class="act" v-if="item.goodsInfo&&item.goodsInfo.act">
  45. <text class="label" v-for="(tag,index) in item.goodsInfo.act"
  46. :key="index">{{tag}}</text>
  47. </view>
  48. <view class="button" @tap="toSend(item)" v-if="item.is_send=='0'">
  49. <text>发送连接</text>
  50. </view>
  51. </view>
  52. </view>
  53. </view>
  54. <!-- 订单 -->
  55. <view class="message img" v-else-if="item.msg_type =='3'">
  56. <view class="msg-text">{{item.content}}</view>
  57. </view>
  58. </view>
  59. <view class="msg-m msg-right" v-else-if="item.speaker == user._id">
  60. <image class="user-img" :src="user.icon&&user.icon.length>0?user.icon[0].url:''">
  61. </image>
  62. <!-- 文字 -->
  63. <view class="message" v-if="item.msg_type =='0'">
  64. <view class="msg-text">{{item.content}}</view>
  65. </view>
  66. <!-- 图像 -->
  67. <view class="message img" v-else-if="item.msg_type =='1'"
  68. @tap="previewImg(item.content)">
  69. <image :src="item.content" class="msg-img" mode="widthFix"></image>
  70. </view>
  71. <!-- 商品 -->
  72. <view class="message img" v-else-if="item.msg_type =='2'">
  73. <view class="msg-goods">
  74. <image :src="item.goodsInfo&&item.goodsInfo.file" class="image" mode="widthFix">
  75. </image>
  76. <view class="goods_1">
  77. <view class="name textOver">{{item.goodsInfo&&item.goodsInfo.name}}</view>
  78. <view class="money">
  79. <text class="money_1"
  80. v-if="item.goodsInfo&&item.goodsInfo.spec&&item.goodsInfo.spec.leader_price"><text>团长价¥</text>{{item.goodsInfo.spec.leader_price||0}}</text>
  81. <text class="money_2"
  82. v-else-if="item.goodsInfo&&item.goodsInfo.spec&&item.goodsInfo.spec.price"><text>特价¥</text>{{item.goodsInfo.spec.price||0}}</text>
  83. <text class="money_2"
  84. v-else><text>¥</text>{{item.goodsInfo.spec.sell_money||0}}</text>
  85. <text
  86. class="money_2"><text>¥</text>{{item.goodsInfo.spec.flow_money||0}}</text>
  87. </view>
  88. <view class="act" v-if="item.goodsInfo&&item.goodsInfo.act">
  89. <text class="label" v-for="(tag,index) in item.goodsInfo.act"
  90. :key="index">{{tag}}</text>
  91. </view>
  92. <view class="button" @tap="toSend(item)" v-if="item.is_send=='0'">
  93. <text>发送连接</text>
  94. </view>
  95. </view>
  96. </view>
  97. </view>
  98. <!-- 订单 -->
  99. <view class="message img" v-else-if="item.msg_type =='3'">
  100. <view class="msg-text">{{item.content}}</view>
  101. </view>
  102. </view>
  103. </view>
  104. </view>
  105. </scroll-view>
  106. </view>
  107. <view class="two">
  108. <submit_1 @inputs="inputs" @heights="heights"></submit_1>
  109. </view>
  110. </view>
  111. </mobile-frame>
  112. </template>
  113. <script>
  114. import moment from 'moment';
  115. import submit_1 from './components/submit_1.vue';
  116. export default {
  117. components: {
  118. submit_1,
  119. },
  120. data() {
  121. return {
  122. // 用户信息
  123. user: {},
  124. // 房间号id
  125. id: '',
  126. // 店铺信息
  127. shop: {},
  128. shop_id: "",
  129. // 商品或订单信息条件
  130. goods: {},
  131. // 订单状态
  132. statusList: [],
  133. // 历史记录
  134. msgList: [],
  135. total: 0,
  136. skip: 0,
  137. limit: 10,
  138. page: 0,
  139. // 判断是否跳到最后一条
  140. is_bottom: true,
  141. // 判断是否下拉刷新复位
  142. triggered: false,
  143. scrollToView: '', //滑动最后一条信息
  144. };
  145. },
  146. onShow: async function() {
  147. const that = this;
  148. that.clearPage();
  149. if (that.id) await that.search()
  150. await that.searchOther();
  151. },
  152. onLoad: async function(e) {
  153. const that = this;
  154. that.$set(that, `goods`, e);
  155. that.$set(that, `id`, e.id);
  156. that.$set(that, `shop_id`, e.shop);
  157. that.watchlogin();
  158. },
  159. computed: {
  160. listenWebsocket() {
  161. return this.$store.state.websocketData;
  162. }
  163. },
  164. watch: {
  165. listenWebsocket: function(newstr) {
  166. if (newstr && newstr.type == 'chat' && newstr.room == this.id) {
  167. this.msgList.push(newstr);
  168. this.goBottom();
  169. }
  170. }
  171. },
  172. methods: {
  173. // 监听用户是否登录
  174. watchlogin() {
  175. const that = this;
  176. uni.getStorage({
  177. key: 'token',
  178. success: async function(res) {
  179. let user = that.$jwt(res.data);
  180. if (user) {
  181. let res;
  182. that.$set(that, `user`, user)
  183. }
  184. }
  185. })
  186. },
  187. // 查询商品或订单信息
  188. async searchOther() {
  189. const that = this;
  190. let res;
  191. // 发送商品信息
  192. if (that.goods && that.goods.goods) {
  193. // 商品
  194. res = await that.$api(`/viewGoods/goodsDetail`, `POST`, {
  195. id: that.goods.goods
  196. });
  197. if (res.errcode == '0') {
  198. let indexSpecs;
  199. // 显示最低价格的规格信息,不考虑库存问题
  200. if (res.data.specs && res.data.specs.length > 0) {
  201. // 规格排序
  202. indexSpecs = res.data.specs.sort(function(a, b) {
  203. let i, j;
  204. if (a.price) i = 'price'
  205. else i = 'sell_money'
  206. if (b.price) j = 'price'
  207. else j = 'sell_money'
  208. return a[i] - b[j];
  209. })
  210. }
  211. let act = res.data.act.map(i => {
  212. return i.tag
  213. })
  214. let goodsInfo = {
  215. "file": res.data && res.data.goods && res.data.goods.file && res.data.goods.file[0]
  216. .url,
  217. "name": res.data && res.data.goods && res.data.goods.name,
  218. "spec": indexSpecs[0],
  219. "act": act
  220. }
  221. let data = {
  222. "_id": 'xx' + moment(new Date()).valueOf(),
  223. "speaker": that.user._id,
  224. "content": that.goods.goods,
  225. "goodsInfo": goodsInfo,
  226. "time": moment().format('YYYY-MM-DD HH:mm:ss'),
  227. "msg_type": '2',
  228. "is_send": '0'
  229. }
  230. if (!that.id) data.shop = that.shop_id
  231. else data.room = that.id
  232. that.msgList.push(data);
  233. // 跳转到最后一条数据 与前面的:id进行对照
  234. // 如果是下拉刷新聊天记录不跳到最后一条
  235. if (that.is_bottom == true) that.goBottom();
  236. }
  237. } else if (that.goods && that.goods.order) {
  238. // 订单
  239. if (that.goods.status == '0') res = await that.$api(`/order/${that.goods.order}`)
  240. else res = await that.$api(`/orderDetail/${that.goods.order}`);
  241. if (res.errcode == '0') {
  242. // 查询状态
  243. let arr = await that.$api(`/dictData`, 'GET', {
  244. code: 'order_process'
  245. })
  246. if (arr.errcode == '0') that.$set(that, `statusList`, arr.data);
  247. let status = arr.data.find(i => i.value == res.data.status)
  248. // 过滤店铺订单
  249. let shop_goods;
  250. if (!that.id) shop_goods = res.data.goods.filter(i => i.shop == that.shop_id)
  251. else shop_goods = res.data.goods.filter(i => i.shop == that.shop._id)
  252. let ordernfo = {
  253. "no": res.data && res.data.no,
  254. "goods": shop_goods,
  255. "status": status.label || '暂无'
  256. }
  257. let data = {
  258. "_id": 'dd' + moment(new Date()).valueOf(),
  259. "speaker": that.user._id,
  260. "content": that.goods.order,
  261. "ordernfo": ordernfo,
  262. "time": moment().format('YYYY-MM-DD HH:mm:ss'),
  263. "msg_type": '3',
  264. "is_send": '0'
  265. }
  266. if (!that.id) data.shop = that.shop_id
  267. else data.room = that.id
  268. that.msgList.push(data);
  269. console.log(data);
  270. // 跳转到最后一条数据 与前面的:id进行对照
  271. // 如果是下拉刷新聊天记录不跳到最后一条
  272. if (that.is_bottom == true) that.goBottom();
  273. }
  274. }
  275. },
  276. // 查询历史记录
  277. async search() {
  278. const that = this;
  279. let info = {
  280. skip: that.skip,
  281. limit: that.limit,
  282. room: that.id
  283. }
  284. let res;
  285. res = await that.$api(`/chatRecord`, `GET`, {
  286. ...info,
  287. }, 'chat');
  288. if (res.errcode == '0') {
  289. let msgList = [...res.data, ...that.msgList];
  290. msgList.sort(function(a, b) {
  291. return a.time > b.time ? 1 : -1;
  292. });
  293. for (let val of msgList) {
  294. if (val.msg_type == '2') {
  295. // 商品
  296. res = await that.$api(`/viewGoods/goodsDetail`, `POST`, {
  297. id: val.content
  298. });
  299. if (res.errcode == '0') {
  300. let indexSpecs;
  301. // 显示最低价格的规格信息,不考虑库存问题
  302. if (res.data.specs && res.data.specs.length > 0) {
  303. // 规格排序
  304. indexSpecs = res.data.specs.sort(function(a, b) {
  305. let i, j;
  306. if (a.price) i = 'price'
  307. else i = 'sell_money'
  308. if (b.price) j = 'price'
  309. else j = 'sell_money'
  310. return a[i] - b[j];
  311. })
  312. }
  313. let act = res.data.act.map(i => {
  314. return i.tag
  315. })
  316. let goodsInfo = {
  317. "file": res.data && res.data.goods && res.data.goods.file && res.data.goods
  318. .file[0]
  319. .url,
  320. "name": res.data && res.data.goods && res.data.goods.name,
  321. "spec": indexSpecs[0],
  322. "act": act,
  323. }
  324. val.goodsInfo = goodsInfo
  325. val.is_send = '1'
  326. }
  327. }
  328. }
  329. that.$set(that, `msgList`, msgList);
  330. that.$set(that, `total`, res.total)
  331. } else {
  332. uni.showToast({
  333. title: res.errmsg,
  334. icon: 'none'
  335. })
  336. }
  337. // 查询房间信息
  338. // 如果是下拉刷新聊天记录不请求房间信息
  339. if (that.is_bottom == true) {
  340. res = await that.$api(`/room/${that.id}`, `GET`, {}, 'chat')
  341. if (res.errcode == '0') {
  342. that.$set(that, `shop`, res.data && res.data.shop);
  343. if (res.data && res.data.shop && res.data.shop.name) {
  344. uni.setNavigationBarTitle({
  345. title: res.data.shop.name
  346. });
  347. }
  348. } else {
  349. uni.showToast({
  350. title: res.errmsg,
  351. icon: 'none'
  352. })
  353. }
  354. }
  355. let id = that.msgList.filter(i => {
  356. if (i.speaker != that.user._id) return i.is_read = '0';
  357. })
  358. let ids = id.map(i => {
  359. return i._id
  360. })
  361. if (ids.length > 0) {
  362. // 信息已读
  363. res = await that.$api(`/chatRecord/read`, `POST`, {
  364. ids
  365. }, 'chat')
  366. if (res.errcode == '0') {} else {
  367. uni.showToast({
  368. title: res.errmsg,
  369. icon: 'none'
  370. })
  371. }
  372. }
  373. },
  374. // 进行图片的预览
  375. previewImg(e) {
  376. const that = this;
  377. // 预览图片
  378. uni.previewImage({
  379. current: 0,
  380. urls: [e],
  381. longPressActions: {
  382. itemList: ['发送给朋友', '保存图片', '收藏'],
  383. success: function(data) {
  384. console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) +
  385. '张图片');
  386. },
  387. fail: function(err) {
  388. console.log(err.errMsg);
  389. }
  390. }
  391. });
  392. },
  393. //接受输入内容
  394. async inputs(e) {
  395. const that = this;
  396. let user = that.user
  397. if (user._id) {
  398. //时间间隔处理
  399. let data = {
  400. "room": that.id,
  401. "speaker": user._id,
  402. "content": e.message,
  403. "time": moment().format('YYYY-MM-DD HH:mm:ss'),
  404. "msg_type": e.type
  405. };
  406. // 发送给服务器消息
  407. if (!that.id) data.shop = that.shop_id
  408. let res = await that.$api(`/chatRecord`, `POST`, data, 'chat');
  409. if (res.errcode == '0') {
  410. that.msgList.push(res.data);
  411. // 发送websocket
  412. that.$store.dispatch('websocketSend', res.data)
  413. } else {
  414. uni.showToast({
  415. title: res.errmsg,
  416. icon: 'none'
  417. })
  418. }
  419. // 跳转到最后一条数据 与前面的:id进行对照
  420. that.goBottom();
  421. } else {
  422. uni.showToast({
  423. title: '未登录账号无法发送消息 ,请及时登录!',
  424. icon: 'none'
  425. })
  426. }
  427. },
  428. // 发送连接
  429. async toSend(e) {
  430. const that = this;
  431. //时间间隔处理
  432. let data = {
  433. "speaker": e.speaker,
  434. "content": e.content,
  435. "time": moment().format('YYYY-MM-DD HH:mm:ss'),
  436. "msg_type": e.msg_type
  437. };
  438. if (!that.id) data.shop = that.shop_id
  439. else data.room = that.id
  440. let res = await that.$api(`/chatRecord`, `POST`, data, 'chat');
  441. if (res.errcode == '0') {
  442. e.is_send = '1'
  443. that.msgList = that.msgList.map(t => {
  444. return t.time === e.time ? e : t;
  445. });
  446. } else {
  447. uni.showToast({
  448. title: res.errmsg,
  449. icon: 'none'
  450. })
  451. }
  452. },
  453. // 店铺
  454. toShop() {
  455. const that = this;
  456. let id = that.shop._id
  457. uni.navigateTo({
  458. url: `/pagesHome/shop/index?id=${id}`
  459. })
  460. },
  461. //输入框高度
  462. heights(e) {
  463. const that = this;
  464. that.goBottom();
  465. },
  466. // 滚动到底部
  467. goBottom() {
  468. const that = this;
  469. that.scrollToView = '';
  470. let lastItem = that.msgList.at(-1);
  471. that.$nextTick(function() {
  472. that.scrollToView = 'msg' + (lastItem._id)
  473. })
  474. },
  475. // 下拉刷新分页
  476. getFresh(e) {
  477. const that = this;
  478. that.triggered = true;
  479. let list = that.msgList;
  480. let limit = that.limit;
  481. setTimeout(() => {
  482. if (that.total > list.length) {
  483. uni.showLoading({
  484. title: '加载中',
  485. mask: true
  486. })
  487. let page = that.page + 1;
  488. that.$set(that, `page`, page)
  489. let skip = page * limit;
  490. that.$set(that, `skip`, skip)
  491. that.$set(that, `is_bottom`, false)
  492. that.search();
  493. uni.hideLoading();
  494. } else {
  495. uni.showToast({
  496. title: `没有更多聊天记录了`,
  497. icon: 'none'
  498. })
  499. }
  500. that.triggered = false;
  501. }, 1000)
  502. },
  503. clearPage() {
  504. const that = this;
  505. that.$set(that, `msgList`, [])
  506. that.$set(that, `skip`, 0)
  507. that.$set(that, `limit`, 10)
  508. that.$set(that, `page`, 0)
  509. },
  510. }
  511. }
  512. </script>
  513. <style lang="scss">
  514. .main {
  515. display: flex;
  516. flex-direction: column;
  517. width: 100vw;
  518. height: 100vh;
  519. overflow: hidden;
  520. .one {
  521. position: relative;
  522. flex-grow: 1;
  523. .scroll-view {
  524. .chat-ls {
  525. padding: 2vw 2vw 0 2vw;
  526. .chat-time {
  527. font-size: 24rpx;
  528. color: rgba(39, 40, 50, 0.3);
  529. line-height: 34rpx;
  530. padding: 10rpx 0rpx;
  531. text-align: center;
  532. }
  533. .msg-m {
  534. display: flex;
  535. padding: 20rpx 0;
  536. .user-img {
  537. flex: none;
  538. width: 80rpx;
  539. height: 80rpx;
  540. border-radius: 40rpx;
  541. border: 1px solid #c0c0c0;
  542. }
  543. .message {
  544. flex: none;
  545. max-width: 480rpx;
  546. }
  547. .img {
  548. margin: 0 20rpx;
  549. }
  550. .msg-text {
  551. font-size: 32rpx;
  552. color: rgba(39, 40, 50, 1);
  553. line-height: 44rpx;
  554. padding: 18rpx 24rpx;
  555. word-break: break-all;
  556. }
  557. .msg-img {
  558. max-width: 400rpx;
  559. border-radius: 20rpx;
  560. }
  561. .msg-goods {
  562. display: flex;
  563. padding: 20rpx 10rpx;
  564. background-color: #f0f0f0;
  565. border-radius: 10rpx;
  566. .image {
  567. width: 100rpx;
  568. height: 100rpx;
  569. border-radius: 10rpx;
  570. }
  571. .goods_1 {
  572. width: 350rpx;
  573. margin: 0 0 0 10rpx;
  574. .name {
  575. font-size: 14px;
  576. }
  577. .money {
  578. .money_1 {
  579. color: #23B67A;
  580. font-size: 16px;
  581. padding: 0 1vw 0 0;
  582. font-weight: bold;
  583. text {
  584. font-size: 12px;
  585. }
  586. }
  587. .money_2 {
  588. font-size: 16px;
  589. padding: 0 1vw 0 0;
  590. color: #ff0000;
  591. font-weight: bold;
  592. text {
  593. font-size: 12px;
  594. }
  595. }
  596. .money_2:last-child {
  597. font-size: 14px;
  598. color: #858585;
  599. text-decoration: line-through;
  600. }
  601. }
  602. .act {
  603. .label {
  604. font-size: 12px;
  605. color: #FFA500;
  606. border: 1px solid #FFA500;
  607. border-radius: 5px;
  608. padding: 0 1vw;
  609. margin: 0 1vw 0 0;
  610. }
  611. }
  612. .button {
  613. text-align: right;
  614. margin: 1rpx 0;
  615. text {
  616. font-size: 12px;
  617. padding: 10rpx;
  618. border-radius: 10rpx;
  619. color: #FF4500;
  620. background-color: #FFF8DC;
  621. }
  622. }
  623. }
  624. }
  625. }
  626. .msg-left {
  627. flex-direction: row;
  628. .msg-text {
  629. word-break: break-all;
  630. margin-left: 16rpx;
  631. background-color: #f1f1f1;
  632. border-radius: 0rpx 20rpx 20rpx 20rpx;
  633. }
  634. .ms-img {
  635. margin-left: 16rpx;
  636. }
  637. }
  638. .msg-right {
  639. flex-direction: row-reverse;
  640. .msg-text {
  641. margin-right: 16rpx;
  642. background-color: rgba(255, 228, 49, 0.8);
  643. border-radius: 20rpx 0rpx 20rpx 20rpx;
  644. }
  645. .ms-img {
  646. margin-right: 16rpx;
  647. }
  648. }
  649. }
  650. }
  651. }
  652. .two {
  653. background-color: #f0f0f0;
  654. border-top: 1px solid rgba(39, 40, 50, 0.1);
  655. }
  656. }
  657. .scroll-view {
  658. position: absolute;
  659. top: 0;
  660. left: 0;
  661. right: 0;
  662. bottom: 0;
  663. .list-scroll-view {
  664. display: flex;
  665. flex-direction: column;
  666. }
  667. }
  668. </style>