Message.vue 6.4 KB

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