main.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  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="loading"></div>
  13. <div v-if="!loading">
  14. <div class="el-table__empty-block" v-if="loadMore" @click="loadingMore"
  15. ><span class="el-table__empty-text">点击加载更多</span></div
  16. >
  17. <div class="el-table__empty-block" v-if="!loadMore"
  18. ><span class="el-table__empty-text">没有更多了</span></div
  19. >
  20. </div>
  21. <!-- 消息列表 -->
  22. <div class="execution" v-for="item in list" :key="item.id">
  23. <div class="avue-comment" :class="item.sendFrom === 2 ? 'avue-comment--reverse' : ''">
  24. <div class="avatar-div">
  25. <img
  26. :src="item.sendFrom === 1 ? user.avatar : mp.avatar"
  27. class="avue-comment__avatar"
  28. />
  29. <div class="avue-comment__author">{{
  30. item.sendFrom === 1 ? user.nickname : mp.nickname
  31. }}</div>
  32. </div>
  33. <div class="avue-comment__main">
  34. <div class="avue-comment__header">
  35. <div class="avue-comment__create_time">{{ parseTime(item.createTime) }}</div>
  36. </div>
  37. <div
  38. class="avue-comment__body"
  39. :style="item.sendFrom === 2 ? 'background: #6BED72;' : ''"
  40. >
  41. <!-- 【事件】区域 -->
  42. <div v-if="item.type === 'event' && item.event === 'subscribe'">
  43. <el-tag type="success" size="mini">关注</el-tag>
  44. </div>
  45. <div v-else-if="item.type === 'event' && item.event === 'unsubscribe'">
  46. <el-tag type="danger" size="mini">取消关注</el-tag>
  47. </div>
  48. <div v-else-if="item.type === 'event' && item.event === 'CLICK'">
  49. <el-tag size="mini">点击菜单</el-tag>【{{ item.eventKey }}】
  50. </div>
  51. <div v-else-if="item.type === 'event' && item.event === 'VIEW'">
  52. <el-tag size="mini">点击菜单链接</el-tag>【{{ item.eventKey }}】
  53. </div>
  54. <div v-else-if="item.type === 'event' && item.event === 'scancode_waitmsg'">
  55. <el-tag size="mini">扫码结果</el-tag>【{{ item.eventKey }}】
  56. </div>
  57. <div v-else-if="item.type === 'event' && item.event === 'scancode_push'">
  58. <el-tag size="mini">扫码结果</el-tag>【{{ item.eventKey }}】
  59. </div>
  60. <div v-else-if="item.type === 'event' && item.event === 'pic_sysphoto'">
  61. <el-tag size="mini">系统拍照发图</el-tag>
  62. </div>
  63. <div v-else-if="item.type === 'event' && item.event === 'pic_photo_or_album'">
  64. <el-tag size="mini">拍照或者相册</el-tag>
  65. </div>
  66. <div v-else-if="item.type === 'event' && item.event === 'pic_weixin'">
  67. <el-tag size="mini">微信相册</el-tag>
  68. </div>
  69. <div v-else-if="item.type === 'event' && item.event === 'location_select'">
  70. <el-tag size="mini">选择地理位置</el-tag>
  71. </div>
  72. <div v-else-if="item.type === 'event'">
  73. <el-tag type="danger" size="mini">未知事件类型</el-tag>
  74. </div>
  75. <!-- 【消息】区域 -->
  76. <div v-else-if="item.type === 'text'">{{ item.content }}</div>
  77. <div v-else-if="item.type === 'voice'">
  78. <wx-voice-player :url="item.mediaUrl" :content="item.recognition" />
  79. </div>
  80. <div v-else-if="item.type === 'image'">
  81. <a target="_blank" :href="item.mediaUrl">
  82. <img :src="item.mediaUrl" style="width: 100px" />
  83. </a>
  84. </div>
  85. <div
  86. v-else-if="item.type === 'video' || item.type === 'shortvideo'"
  87. style="text-align: center"
  88. >
  89. <wx-video-player :url="item.mediaUrl" />
  90. </div>
  91. <div v-else-if="item.type === 'link'" class="avue-card__detail">
  92. <el-link type="success" :underline="false" target="_blank" :href="item.url">
  93. <div class="avue-card__title"><i class="el-icon-link"></i>{{ item.title }}</div>
  94. </el-link>
  95. <div class="avue-card__info" style="height: unset">{{ item.description }}</div>
  96. </div>
  97. <!-- TODO 芋艿:待完善 -->
  98. <div v-else-if="item.type === 'location'">
  99. <wx-location
  100. :label="item.label"
  101. :location-y="item.locationY"
  102. :location-x="item.locationX"
  103. />
  104. </div>
  105. <div v-else-if="item.type === 'news'" style="width: 300px">
  106. <!-- TODO 芋艿:待测试;详情页也存在类似的情况 -->
  107. <wx-news :articles="item.articles" />
  108. </div>
  109. <div v-else-if="item.type === 'music'">
  110. <wx-music
  111. :title="item.title"
  112. :description="item.description"
  113. :thumb-media-url="item.thumbMediaUrl"
  114. :music-url="item.musicUrl"
  115. :hq-music-url="item.hqMusicUrl"
  116. />
  117. </div>
  118. </div>
  119. </div>
  120. </div>
  121. </div>
  122. </div>
  123. <div class="msg-send" v-loading="sendLoading">
  124. <wx-reply-select ref="replySelect" :objData="objData" />
  125. <el-button type="success" size="small" class="send-but" @click="sendMsg">发送(S)</el-button>
  126. </div>
  127. </div>
  128. </template>
  129. <script>
  130. import { getMessagePage, sendMessage } from '@/api/mp/message'
  131. import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
  132. import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue'
  133. import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue'
  134. import WxNews from '@/views/mp/components/wx-news/main.vue'
  135. import WxLocation from '@/views/mp/components/wx-location/main.vue'
  136. import WxMusic from '@/views/mp/components/wx-music/main.vue'
  137. import { getUser } from '@/api/mp/mpuser'
  138. export default {
  139. name: 'WxMsg',
  140. components: {
  141. WxReplySelect,
  142. WxVideoPlayer,
  143. WxVoicePlayer,
  144. WxNews,
  145. WxLocation,
  146. WxMusic
  147. },
  148. props: {
  149. userId: {
  150. type: Number,
  151. required: true
  152. }
  153. },
  154. data() {
  155. return {
  156. nowStr: new Date().getTime(), // 当前的时间戳,用于每次消息加载后,回到原位置;具体见 :id="'msg-div' + nowStr" 处
  157. loading: false, // 消息列表是否正在加载中
  158. loadMore: true, // 是否可以加载更多
  159. list: [], // 消息列表
  160. queryParams: {
  161. pageNo: 1, // 当前页数
  162. pageSize: 14, // 每页显示多少条
  163. accountId: undefined
  164. },
  165. user: {
  166. // 由于微信不再提供昵称,直接使用“用户”展示
  167. nickname: '用户',
  168. avatar: require('@/assets/images/profile.jpg'),
  169. accountId: 0 // 公众号账号编号
  170. },
  171. mp: {
  172. nickname: '公众号',
  173. avatar: require('@/assets/images/wechat.png')
  174. },
  175. // ========= 消息发送 =========
  176. sendLoading: false, // 发送消息是否加载中
  177. objData: {
  178. // 微信发送消息
  179. type: 'text'
  180. }
  181. }
  182. },
  183. created() {
  184. // 获得用户信息
  185. getUser(this.userId).then((response) => {
  186. this.user.nickname =
  187. response.data.nickname && response.data.nickname.length > 0
  188. ? response.data.nickname
  189. : this.user.nickname
  190. this.user.avatar =
  191. response.data.avatar && this.user.avatar.length > 0
  192. ? response.data.avatar
  193. : this.user.avatar
  194. this.user.accountId = response.data.accountId
  195. // 设置公众号账号编号
  196. this.queryParams.accountId = response.data.accountId
  197. this.objData.accountId = response.data.accountId
  198. // 加载消息
  199. console.log(this.queryParams)
  200. this.refreshChange()
  201. })
  202. },
  203. methods: {
  204. sendMsg() {
  205. if (!this.objData) {
  206. return
  207. }
  208. // 公众号限制:客服消息,公众号只允许发送一条
  209. if (this.objData.type === 'news' && this.objData.articles.length > 1) {
  210. this.objData.articles = [this.objData.articles[0]]
  211. this.$message({
  212. showClose: true,
  213. message: '图文消息条数限制在 1 条以内,已默认发送第一条',
  214. type: 'success'
  215. })
  216. }
  217. // 执行发送
  218. this.sendLoading = true
  219. sendMessage(
  220. Object.assign(
  221. {
  222. userId: this.userId
  223. },
  224. {
  225. ...this.objData
  226. }
  227. )
  228. )
  229. .then((response) => {
  230. this.sendLoading = false
  231. // 添加到消息列表,并滚动
  232. this.list = [...this.list, ...[response.data]]
  233. this.scrollToBottom()
  234. // 重置 objData 状态
  235. this.$refs['replySelect'].deleteObj() // 重置,避免 tab 的数据未清理
  236. })
  237. .catch(() => {
  238. this.sendLoading = false
  239. })
  240. },
  241. loadingMore() {
  242. this.queryParams.pageNo++
  243. this.getPage(this.queryParams)
  244. },
  245. getPage(page, params) {
  246. this.loading = true
  247. getMessagePage(
  248. Object.assign(
  249. {
  250. pageNo: page.pageNo,
  251. pageSize: page.pageSize,
  252. userId: this.userId,
  253. accountId: page.accountId
  254. },
  255. params
  256. )
  257. ).then((response) => {
  258. // 计算当前的滚动高度
  259. const msgDiv = document.getElementById('msg-div' + this.nowStr)
  260. let scrollHeight = 0
  261. if (msgDiv) {
  262. scrollHeight = msgDiv.scrollHeight
  263. }
  264. // 处理数据
  265. const data = response.data.list.reverse()
  266. this.list = [...data, ...this.list]
  267. this.loading = false
  268. if (data.length < this.queryParams.pageSize || data.length === 0) {
  269. this.loadMore = false
  270. }
  271. this.queryParams.pageNo = page.pageNo
  272. this.queryParams.pageSize = page.pageSize
  273. // 滚动到原来的位置
  274. if (this.queryParams.pageNo === 1) {
  275. // 定位到消息底部
  276. this.scrollToBottom()
  277. } else if (data.length !== 0) {
  278. // 定位滚动条
  279. this.$nextTick(() => {
  280. if (scrollHeight !== 0) {
  281. msgDiv.scrollTop =
  282. document.getElementById('msg-div' + this.nowStr).scrollHeight - scrollHeight - 100
  283. }
  284. })
  285. }
  286. })
  287. },
  288. /**
  289. * 刷新回调
  290. */
  291. refreshChange() {
  292. this.getPage(this.queryParams)
  293. },
  294. /** 定位到消息底部 */
  295. scrollToBottom: function () {
  296. this.$nextTick(() => {
  297. let div = document.getElementById('msg-div' + this.nowStr)
  298. div.scrollTop = div.scrollHeight
  299. })
  300. }
  301. }
  302. }
  303. </script>
  304. <style lang="scss" scoped>
  305. /* 因为 joolun 实现依赖 avue 组件,该页面使用了 comment.scss、card.scc */
  306. @import './comment.scss';
  307. @import './card.scss';
  308. .msg-main {
  309. margin-top: -30px;
  310. padding: 10px;
  311. }
  312. .msg-div {
  313. height: 50vh;
  314. overflow: auto;
  315. background-color: #eaeaea;
  316. margin-left: 10px;
  317. margin-right: 10px;
  318. }
  319. .msg-send {
  320. padding: 10px;
  321. }
  322. .avatar-div {
  323. text-align: center;
  324. width: 80px;
  325. }
  326. .send-but {
  327. float: right;
  328. margin-top: 8px !important;
  329. }
  330. </style>