Message.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. <template>
  2. <message-component>
  3. <div ref="messageIcon" v-bind="$attrs" class="message-icon">
  4. <span v-show="unReadMessages?.newCount" class="un-read-num">
  5. {{ (unReadMessages?.newCount || 0) > 99 ? '99+' : unReadMessages?.newCount }}
  6. </span>
  7. <svg-icon name="message" class="bell"></svg-icon>
  8. </div>
  9. <el-popover
  10. placement="bottom-start"
  11. :width="'400px'"
  12. :show-arrow="false"
  13. :virtual-ref="messageIcon"
  14. trigger="click"
  15. virtual-triggering
  16. >
  17. <div class="message-popover-content">
  18. <div class="title">
  19. <!-- <span class="unread-count">{{ unReadMessages?.newCount || 0 }}</span>
  20. <span>条消息</span> -->
  21. 最近消息
  22. </div>
  23. <div class="message-list">
  24. <div
  25. v-for="message in unReadMessages?.messages?.slice(0, 2)"
  26. :key="message.sendUserId"
  27. class="message-row"
  28. @click="onReceiveMessage(message)"
  29. >
  30. <!-- <div class="message-send-user">
  31. <div class="user-name" :title="message.sendUserName">{{ message.sendUserName }}</div>
  32. <div class="message-time">{{ dayjs(message.sendTime).format('HH:mm') }}</div>
  33. </div>
  34. <pre class="flex-1 message-content" v-html="message.content"></pre> -->
  35. <div class="send-user truncate">{{ message.sendUserName }}</div>
  36. <!-- <div class="send-content truncate">{{ message.content }}</div> -->
  37. <div class="send-content truncate">{{ transContent(message.content) }}</div>
  38. </div>
  39. </div>
  40. <confirm-button
  41. size="small"
  42. between
  43. ok-text="收消息"
  44. cancel-text="发消息"
  45. @confirm="onReceiveMessage"
  46. @cancel="onSendMessage"
  47. ></confirm-button>
  48. </div>
  49. </el-popover>
  50. <message-window
  51. v-model="visibleMessageWindow"
  52. v-model:type="messageWindowType"
  53. v-model:replyUserId="replyUserId"
  54. :paper-path="props.paperPath"
  55. ></message-window>
  56. </message-component>
  57. </template>
  58. <script setup lang="ts" name="Message">
  59. /** 头部消息组件 */
  60. import { defineComponent, withDefaults, ref, useSlots, watch, inject, ShallowRef } from 'vue'
  61. import { ElPopover } from 'element-plus'
  62. import dayjs from 'dayjs'
  63. import useVW from '@/hooks/useVW'
  64. import useVModel from '@/hooks/useVModel'
  65. import useMessageLoop, { lastMsgs, setLastMsgs } from '@/hooks/useMessageLoop'
  66. import useMainStore from '@/store/main'
  67. import MessageWindow from '@/components/shared/message/MessageWindow.vue'
  68. import ConfirmButton from '@/components/common/ConfirmButton.vue'
  69. import SvgIcon from '@/components/common/SvgIcon.vue'
  70. const props = withDefaults(
  71. defineProps<{
  72. type?: 'view' | 'send'
  73. replyUserId?: number | null
  74. messageVisible?: boolean | null
  75. paperPath?: string | null
  76. }>(),
  77. { type: 'view', replyUserId: null, paperPath: null, messageVisible: false }
  78. )
  79. const transContent = (content: any) => {
  80. return content.replaceAll(/<[^>]+>/gim, ' ')
  81. // return content.replaceAll('<div>', ' ').replaceAll('<br>', ' ').replaceAll('<\/div>', '').replaceAll('<\/br>', '')
  82. }
  83. const MessageComponent = defineComponent({
  84. setup() {
  85. const mainStore = useMainStore()
  86. return {
  87. mainStore,
  88. }
  89. },
  90. render() {
  91. const slots = useSlots()
  92. return this.mainStore?.myUserInfo?.role === 'ADMIN' ? null : slots.default?.()
  93. },
  94. })
  95. const setMessageVisible = inject<(visible: boolean) => void>('setMessageVisible')
  96. const setReplyUserId = inject<(replyUserId: number | null) => void>('setReplyUserId')
  97. const messageIcon = ref<HTMLDivElement>()
  98. const unReadMessages = useMessageLoop()
  99. watch(
  100. () => (unReadMessages as ShallowRef).value,
  101. (val) => {
  102. if (!visibleMessageWindow.value) {
  103. let newMsgs = val.messages
  104. if (!newMsgs.length) {
  105. return
  106. }
  107. let newMsgTime = Math.max.apply(
  108. Math,
  109. newMsgs.map((item: any) => {
  110. return new Date(item.sendTime).getTime()
  111. })
  112. )
  113. // let hasRecentSixSecondsMsg = new Date(newMsgs[0].sendTime).getTime() > Date.now() - 6000
  114. let hasRecentSixSecondsMsg = newMsgTime > Date.now() - 6000
  115. if (!lastMsgs.messages.length && hasRecentSixSecondsMsg) {
  116. messageWindowType.value = 'view'
  117. visibleMessageWindow.value = true
  118. return
  119. }
  120. let recentMsgTime = Math.max.apply(
  121. Math,
  122. lastMsgs.messages.map((item) => {
  123. return new Date(item.sendTime).getTime()
  124. })
  125. )
  126. // let newMsgTime = Math.max.apply(
  127. // Math,
  128. // newMsgs.map((item: any) => {
  129. // return new Date(item.sendTime).getTime()
  130. // })
  131. // )
  132. if (newMsgTime > recentMsgTime && hasRecentSixSecondsMsg) {
  133. messageWindowType.value = 'view'
  134. visibleMessageWindow.value = true
  135. }
  136. }
  137. }
  138. )
  139. const messageWindowType = useVModel(props, 'type')
  140. const replyUserId = useVModel(props, 'replyUserId')
  141. /** 发送/查看消息Modal */
  142. const visibleMessageWindow = ref<boolean>(false)
  143. watch(
  144. () => props.messageVisible,
  145. () => {
  146. if (props.messageVisible) {
  147. visibleMessageWindow.value = true
  148. messageWindowType.value = 'send'
  149. }
  150. }
  151. )
  152. watch(visibleMessageWindow, () => {
  153. if (!visibleMessageWindow.value) {
  154. setMessageVisible?.(false)
  155. }
  156. })
  157. watch(replyUserId, () => {
  158. setReplyUserId?.(replyUserId.value)
  159. })
  160. /** 收消息 */
  161. const onReceiveMessage = (message: any) => {
  162. messageWindowType.value = 'view'
  163. visibleMessageWindow.value = true
  164. }
  165. /** 发消息 */
  166. const onSendMessage = () => {
  167. messageWindowType.value = 'send'
  168. visibleMessageWindow.value = true
  169. }
  170. </script>
  171. <style scoped lang="scss">
  172. .message-icon {
  173. display: grid;
  174. place-items: center;
  175. font-size: $LargeFont;
  176. position: relative;
  177. .bell {
  178. cursor: pointer;
  179. }
  180. .un-read-num {
  181. position: absolute;
  182. width: 24px;
  183. height: 24px;
  184. line-height: 24px;
  185. right: -15px;
  186. top: -15px;
  187. text-align: center;
  188. color: $color--white;
  189. font-size: 10px;
  190. border-radius: 50%;
  191. background-color: $DangerColor;
  192. }
  193. }
  194. .message-popover-content {
  195. color: $NormalColor;
  196. .title {
  197. .unread-count {
  198. font-size: $MediumFont;
  199. color: $DangerColor;
  200. margin-right: 4px;
  201. }
  202. }
  203. .message-list {
  204. border-bottom: $OnePixelLine;
  205. // margin: 10px 0 40px;
  206. height: 80px;
  207. margin: 10px 0;
  208. .message-row {
  209. padding: 0 4px;
  210. font-size: $BaseFont;
  211. font-weight: 400;
  212. border-top: $OnePixelLine;
  213. height: 40px;
  214. line-height: 40px;
  215. overflow: hidden;
  216. white-space: nowrap;
  217. text-overflow: ellipsis;
  218. display: flex;
  219. align-items: center;
  220. justify-content: space-between;
  221. .send-user {
  222. width: 60px;
  223. }
  224. .send-content {
  225. width: calc(100% - 70px);
  226. }
  227. &:hover {
  228. background-color: #f5f5f5;
  229. }
  230. .message-send-user {
  231. width: 52px;
  232. margin-right: 0.5em;
  233. font-size: $SmallFont;
  234. overflow: hidden;
  235. .user-name {
  236. overflow: hidden;
  237. text-overflow: ellipsis;
  238. white-space: nowrap;
  239. font-weight: 500;
  240. text-align: justify;
  241. text-align-last: justify;
  242. }
  243. .message-time {
  244. text-align: center;
  245. font-weight: 300;
  246. }
  247. }
  248. .message-content {
  249. overflow: hidden;
  250. text-overflow: ellipsis;
  251. display: -webkit-box;
  252. -webkit-line-clamp: 2;
  253. -webkit-box-orient: vertical;
  254. font-size: $SmallFont;
  255. white-space: pre-wrap;
  256. }
  257. }
  258. }
  259. }
  260. </style>