main.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <!--
  2. - Copyright (C) 2018-2019
  3. - All rights reserved, Designed By www.joolun.com
  4. 芋道源码:
  5. ① 移除暂时用不到的 websocket
  6. ② 代码优化,补充注释,提升阅读性
  7. -->
  8. <template>
  9. <div class="msg-main">
  10. <div class="msg-div" :id="'msg-div' + nowStr">
  11. <!-- 加载更多 -->
  12. <div v-loading="tableLoading"></div>
  13. <div v-if="!tableLoading">
  14. <div class="el-table__empty-block" v-if="loadMore" @click="loadingMore"><span class="el-table__empty-text">点击加载更多</span></div>
  15. <div class="el-table__empty-block" v-if="!loadMore"><span class="el-table__empty-text">没有更多了</span></div>
  16. </div>
  17. <!-- 消息列表 -->
  18. <div class="execution" v-for="item in tableData" :key='item.id'>
  19. <div class="avue-comment" :class="item.sendFrom === 2 ? 'avue-comment--reverse' : ''">
  20. <div class="avatar-div">
  21. <img :src="item.sendFrom === 1 ? user.avatar : mp.avatar" class="avue-comment__avatar">
  22. <div class="avue-comment__author">{{item.sendFrom === 1 ? user.nickname : mp.nickname }}</div>
  23. </div>
  24. <div class="avue-comment__main">
  25. <div class="avue-comment__header">
  26. <div class="avue-comment__create_time">{{ parseTime(item.createTime) }}</div>
  27. </div>
  28. <div class="avue-comment__body" :style="item.sendFrom === 2 ? 'background: #6BED72;' : ''">
  29. <!-- 【事件】区域 -->
  30. <div v-if="item.type === 'event' && item.event === 'subscribe'">
  31. <el-tag type="success" size="mini">关注</el-tag>
  32. </div>
  33. <div v-else-if="item.type === 'event' && item.event === 'unsubscribe'">
  34. <el-tag type="danger" size="mini">取消关注</el-tag>
  35. </div>
  36. <div v-else-if="item.type === 'event' && item.event === 'CLICK'">
  37. <el-tag size="mini">点击菜单</el-tag>【{{ item.eventKey }}】
  38. </div>
  39. <div v-else-if="item.type === 'event' && item.event === 'VIEW'">
  40. <el-tag size="mini">点击菜单链接</el-tag>【{{ item.eventKey }}】
  41. </div>
  42. <div v-else-if="item.type === 'event' && item.event === 'scancode_waitmsg'"> <!-- TODO 芋艿:需要测试下 -->
  43. <el-tag size="mini">扫码结果</el-tag>【{{ item.eventKey }}】
  44. </div>
  45. <div v-else-if="item.type === 'event'">
  46. <el-tag type="danger" size="mini">未知事件类型</el-tag>
  47. </div>
  48. <!-- 【消息】区域 -->
  49. <div v-else-if="item.type === 'text'">{{ item.content }}</div>
  50. <div v-else-if="item.type === 'voice'">
  51. <wx-voice-player :url="item.mediaUrl" :content="item.recognition" />
  52. </div>
  53. <div v-else-if="item.type === 'image'">
  54. <a target="_blank" :href="item.mediaUrl">
  55. <img :src="item.mediaUrl" style="width: 100px">
  56. </a>
  57. </div>
  58. <div v-else-if="item.type === 'video' || item.type === 'shortvideo'" style="text-align: center">
  59. <wx-video-player :url="item.mediaUrl" />
  60. </div>
  61. <div v-else-if="item.type === 'link'" class="avue-card__detail">
  62. <el-link type="success" :underline="false" target="_blank" :href="item.url">
  63. <div class="avue-card__title"><i class="el-icon-link"></i>{{ item.title }}</div>
  64. </el-link>
  65. <div class="avue-card__info" style="height: unset">{{item.description}}</div>
  66. </div>
  67. <!-- TODO 芋艿:待完善 -->
  68. <div v-else-if="item.type === 'location'">
  69. <wx-location :label="item.label" :location-y="item.locationY" :location-x="item.locationX" />
  70. </div>
  71. <div v-else-if="item.type === 'news'" style="width: 300px"> <!-- TODO 芋艿:待测试;详情页也存在类似的情况 -->
  72. <wx-news :articles="item.articles" />
  73. </div>
  74. <!-- <div v-if="item.repType == 'music'">-->
  75. <!-- <el-link type="success" :underline="false" target="_blank" :href="item.repUrl">-->
  76. <!-- <div class="avue-card__body" style="padding:10px;background-color: #fff;border-radius: 5px">-->
  77. <!-- <div class="avue-card__avatar"><img :src="item.repThumbUrl" alt=""></div>-->
  78. <!-- <div class="avue-card__detail">-->
  79. <!-- <div class="avue-card__title" style="margin-bottom:unset">{{item.repName}}</div>-->
  80. <!-- <div class="avue-card__info" style="height: unset">{{item.repDesc}}</div>-->
  81. <!-- </div>-->
  82. <!-- </div>-->
  83. <!-- </el-link>-->
  84. <!-- </div>-->
  85. </div>
  86. </div>
  87. </div>
  88. </div>
  89. </div>
  90. <!-- <div class="msg-send" v-loading="sendLoading">-->
  91. <!-- <WxReplySelect :objData="objData"></WxReplySelect>-->
  92. <!-- <el-button type="success" size="small" class="send-but" @click="sendMsg">发送(S)</el-button>-->
  93. <!-- </div>-->
  94. </div>
  95. </template>
  96. <script>
  97. import { getMessagePage } from '@/api/mp/message'
  98. // import WxReplySelect from '@/components/wx-reply/main.vue'
  99. // import WxNews from '@/components/wx-news/main.vue'
  100. import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
  101. import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
  102. import WxNews from '@/views/mp/components/wx-news/main.vue';
  103. import WxLocation from '@/views/mp/components/wx-location/main.vue';
  104. export default {
  105. name: "wxMsg",
  106. components: {
  107. // WxReplySelect,
  108. WxVideoPlayer,
  109. WxVoicePlayer,
  110. WxNews,
  111. WxLocation
  112. },
  113. props: {
  114. wxUserId: {
  115. type: String
  116. }
  117. },
  118. data() {
  119. return {
  120. nowStr: new Date().getTime(), // 当前的时间戳,用于每次消息加载后,回到原位置;具体见 :id="'msg-div' + nowStr" 处
  121. objData:{
  122. repType: 'text'
  123. },
  124. sendLoading: false, // 发送消息是否加载中
  125. tableLoading: false, // 消息列表是否正在加载中
  126. loadMore: true, // 是否可以加载更多
  127. tableData: [], // 消息列表
  128. page: {
  129. pageNo: 1, // 当前页数
  130. pageSize: 14, // 每页显示多少条
  131. },
  132. user: { // 由于微信不再提供昵称,直接使用“用户”展示
  133. nickname: '用户',
  134. avatar: require("@/assets/images/profile.jpg"),
  135. },
  136. mp: {
  137. nickname: '公众号',
  138. avatar: require("@/assets/images/wechat.png"),
  139. },
  140. qqMapKey: '' //
  141. }
  142. },
  143. created() {
  144. this.refreshChange()
  145. },
  146. methods:{
  147. sendMsg(){
  148. if (this.objData) {
  149. if(this.objData.repType === 'news'){
  150. this.objData.content.articles = [this.objData.content.articles[0]]
  151. this.$message({
  152. showClose: true,
  153. message: '图文消息条数限制在1条以内,已默认发送第一条',
  154. type: 'success'
  155. })
  156. }
  157. this.sendLoading = true
  158. addObj(Object.assign({
  159. wxUserId: this.wxUserId
  160. },this.objData)).then(data => {
  161. this.sendLoading = false
  162. data = data.data
  163. this.tableData = [...this.tableData , ...[data] ]
  164. this.scrollToBottom()
  165. this.objData = {
  166. repType: 'text'
  167. }
  168. }).catch(() => {
  169. this.sendLoading = false
  170. })
  171. }
  172. },
  173. loadingMore() {
  174. this.page.pageNo++
  175. this.getPage(this.page)
  176. },
  177. getPage(page, params) {
  178. this.tableLoading = true
  179. getMessagePage(Object.assign({
  180. pageNo: page.pageNo,
  181. pageSize: page.pageSize,
  182. wxUserId: this.wxUserId
  183. }, params)).then(response => {
  184. // 计算当前的滚动高度
  185. const msgDiv = document.getElementById('msg-div' + this.nowStr);
  186. let scrollHeight = 0
  187. if(msgDiv){
  188. scrollHeight = msgDiv.scrollHeight
  189. }
  190. // 处理数据
  191. const data = response.data.list.reverse();
  192. this.tableData = [...data, ...this.tableData]
  193. this.tableLoading = false
  194. if (data.length < this.page.pageSize || data.length === 0){
  195. this.loadMore = false
  196. }
  197. this.page.pageNo = page.pageNo
  198. this.page.pageSize = page.pageSize
  199. // 滚动到原来的位置
  200. if(this.page.pageNo === 1) { // 定位到消息底部
  201. this.scrollToBottom()
  202. } else if (data.length !== 0) { // 定位滚动条
  203. this.$nextTick(() => {
  204. if (scrollHeight !== 0){
  205. msgDiv.scrollTop = document.getElementById('msg-div'+this.nowStr).scrollHeight - scrollHeight - 100
  206. }
  207. })
  208. }
  209. })
  210. },
  211. /**
  212. * 刷新回调
  213. */
  214. refreshChange() {
  215. this.getPage(this.page)
  216. },
  217. /** 定位到消息底部 */
  218. scrollToBottom: function () {
  219. this.$nextTick(() => {
  220. let div = document.getElementById('msg-div' + this.nowStr)
  221. div.scrollTop = div.scrollHeight
  222. })
  223. },
  224. }
  225. };
  226. </script>
  227. <style lang="scss" scoped>
  228. /* 因为 joolun 实现依赖 avue 组件,该页面使用了 comment.scss、card.scc */
  229. @import './comment.scss';
  230. @import './card.scss';
  231. .msg-main {
  232. margin-top: -30px;
  233. padding: 10px;
  234. }
  235. .msg-div {
  236. height: 50vh;
  237. overflow: auto;
  238. background-color: #eaeaea;
  239. }
  240. .msg-send {
  241. padding: 10px;
  242. }
  243. .avue-comment__main {
  244. flex: unset!important;
  245. border-radius: 5px!important;
  246. margin: 0 8px!important;
  247. }
  248. .avue-comment__header {
  249. border-top-left-radius: 5px;
  250. border-top-right-radius: 5px;
  251. }
  252. .avue-comment__body {
  253. border-bottom-right-radius: 5px;
  254. border-bottom-left-radius: 5px;
  255. }
  256. .avatar-div {
  257. text-align: center;
  258. width: 80px;
  259. }
  260. .send-but {
  261. float: right;
  262. margin-top: 8px!important;
  263. }
  264. </style>