SystemMessage.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <script setup lang="ts">
  2. import NotificationMessageCard from '@/components/NotificationMessageCard/src/NotificationMessageCard.vue'
  3. import { useNotificationMessage } from '@/stores/modules/notificationMessage'
  4. import { isMacOS } from '@/utils/tools'
  5. const isMac = isMacOS()
  6. const activeCardTypeName = ref(sessionStorage.getItem('activeCardTypeName') || 'Milestone Update')
  7. const notificationMsgStore = useNotificationMessage()
  8. const collapseVModel = ref<string[]>(['1'])
  9. const tabCountList = ref([0, 0, 0, 0, 0])
  10. const curTabCount = ref([])
  11. const handleCount = (count: number) => {
  12. if (!count) return ''
  13. return count > 99 ? '99+' : count
  14. }
  15. const handleShowCount = (typeName: string, index: number) => {
  16. // 在切换type类型时,防止点击的类型值为点击之前类型的值
  17. if (curTabCount.value?.[index] > -1) {
  18. return curTabCount.value[index]
  19. }
  20. const count = tabCountList.value[index]
  21. if (typeName === activeCardTypeName.value) {
  22. return handleCount(unreadNotificationList.value.length)
  23. }
  24. return handleCount(count)
  25. }
  26. const navList = [
  27. 'Milestone Update',
  28. 'Container Status Update',
  29. 'Departure/Arrival Delay',
  30. 'ETD/ETA Change'
  31. ]
  32. const notificationTypeList = ref({
  33. Milestone_Update: 'Milestone Update',
  34. Container_Status_Update: 'Container Status Update',
  35. 'Departure/Arrival_Delay': 'Departure/Arrival Delay',
  36. 'ETD/ETA_Change': 'ETD/ETA Change',
  37. Feature_Update: 'Feature Update'
  38. })
  39. const setActiveItem = (item: string) => {
  40. navList.forEach((navItem, index) => {
  41. curTabCount.value[index] = handleShowCount(navItem, index)
  42. })
  43. curTabCount.value[tabCountList.value.length - 1] = handleShowCount(
  44. 'Feature Update',
  45. tabCountList.value.length - 1
  46. )
  47. activeCardTypeName.value = item
  48. sessionStorage.setItem('activeCardTypeName', item)
  49. activeTabName.value = 'All Notifications'
  50. getNotificationList()
  51. }
  52. const loading = ref(false)
  53. const notificationList = ref<any[]>([])
  54. const unreadNotificationList = computed(() => {
  55. return notificationList.value.filter((item) => !item.info.isRead)
  56. })
  57. const readNotificationList = computed(() => {
  58. return notificationList.value.filter((item) => item.info.isRead)
  59. })
  60. const getNotificationList = async () => {
  61. loading.value = true
  62. const rulesType = Object.entries(notificationTypeList.value).find(
  63. (item) => item[1] === activeCardTypeName.value
  64. )?.[0]
  65. try {
  66. await notificationMsgStore.markMessageAsRead()
  67. $api
  68. .getSystemMessageData({
  69. rules_type: rulesType
  70. })
  71. .then((res) => {
  72. if (res.code === 200) {
  73. const data = res.data
  74. notificationList.value = data.cardList
  75. tabCountList.value = data.countList
  76. }
  77. })
  78. .finally(() => {
  79. loading.value = false
  80. curTabCount.value = []
  81. })
  82. } catch (error) {
  83. console.error(error)
  84. loading.value = false
  85. curTabCount.value = []
  86. }
  87. }
  88. const changeCardRead = () => {
  89. const readCardMap = notificationMsgStore.readCardMap
  90. notificationList.value.forEach((item) => {
  91. if (readCardMap.includes(item.info.id)) {
  92. item.info.isRead = true
  93. }
  94. })
  95. }
  96. const activeTabName = ref('All Notifications')
  97. const handleTabChange = () => {
  98. // 当前tab页切换时,更新数据
  99. const readCardMap = notificationMsgStore.readCardMap
  100. notificationList.value.forEach((item) => {
  101. if (readCardMap.includes(item.info.id)) {
  102. item.info.isRead = true
  103. }
  104. })
  105. }
  106. onMounted(() => {
  107. getNotificationList()
  108. })
  109. </script>
  110. <template>
  111. <div v-vloading="loading" style="height: 100%; width: 100%">
  112. <div class="Title">System Message</div>
  113. <div class="system-message">
  114. <div class="left-nav">
  115. <el-collapse v-model="collapseVModel">
  116. <el-collapse-item title="Event Notifications" name="1">
  117. <div
  118. @click="setActiveItem(item)"
  119. class="collapse-item"
  120. :class="{ 'is-active': item === activeCardTypeName }"
  121. v-for="(item, index) in navList"
  122. :key="item"
  123. >
  124. <div v-if="item === activeCardTypeName" class="active-sign"></div>
  125. <span>{{ item }}</span>
  126. <div
  127. class="count"
  128. :style="{ 'padding-top': isMac ? 0 : '1px' }"
  129. v-if="handleShowCount(item, index)"
  130. >
  131. <span>{{ handleShowCount(item, index) }}</span>
  132. </div>
  133. </div>
  134. </el-collapse-item>
  135. </el-collapse>
  136. <div
  137. @click="setActiveItem('Feature Update')"
  138. class="collapse-item"
  139. style="margin-top: 4px; font-weight: 700"
  140. :class="{ 'is-active': activeCardTypeName === 'Feature Update' }"
  141. >
  142. <div v-if="activeCardTypeName === 'Feature Update'" class="active-sign"></div>
  143. <span>Feature Update</span>
  144. <div
  145. class="count"
  146. :style="{ 'padding-top': isMac ? 0 : '1px' }"
  147. v-if="handleShowCount('Feature Update', tabCountList.length - 1)"
  148. >
  149. <span>{{ handleShowCount('Feature Update', tabCountList.length - 1) }}</span>
  150. </div>
  151. </div>
  152. </div>
  153. <div class="right-content">
  154. <el-tabs v-model="activeTabName" @tab-change="handleTabChange" class="demo-tabs">
  155. <el-tab-pane label="All Notifications" name="All Notifications">
  156. <template #label>
  157. <span style="margin-right: 4px">All Notifications</span>
  158. </template>
  159. <div style="padding-bottom: 20px" v-if="activeTabName === 'All Notifications'">
  160. <NotificationMessageCard
  161. v-if="activeTabName === 'All Notifications'"
  162. :data="notificationList"
  163. @hasCardRead="changeCardRead"
  164. :isScrollPadding="true"
  165. :isShowInsertionTime="true"
  166. :updateReadCardsOnChange="false"
  167. ></NotificationMessageCard>
  168. </div>
  169. </el-tab-pane>
  170. <el-tab-pane label="Unread" name="Unread">
  171. <template #label>
  172. <span style="margin-right: 4px">Unread</span>
  173. <div
  174. class="count"
  175. :style="{ 'padding-top': isMac ? 0 : '1px' }"
  176. v-if="unreadNotificationList.length"
  177. >
  178. <span>{{ handleCount(unreadNotificationList.length) }}</span>
  179. </div>
  180. </template>
  181. <div style="padding-bottom: 20px" v-if="activeTabName === 'Unread'">
  182. <NotificationMessageCard
  183. v-if="activeTabName === 'Unread'"
  184. :data="unreadNotificationList"
  185. :isScrollPadding="true"
  186. :isShowInsertionTime="true"
  187. :updateReadCardsOnChange="false"
  188. ></NotificationMessageCard>
  189. </div>
  190. </el-tab-pane>
  191. <el-tab-pane label="Read" name="Read">
  192. <template #label><span style="margin-right: 4px">Read</span> </template>
  193. <div style="padding-bottom: 20px" v-if="activeTabName === 'Read'">
  194. <NotificationMessageCard
  195. v-if="activeTabName === 'Read'"
  196. :updateReadCardsOnChange="false"
  197. :isScrollPadding="true"
  198. :isShowInsertionTime="true"
  199. :data="readNotificationList"
  200. >
  201. </NotificationMessageCard>
  202. </div>
  203. </el-tab-pane>
  204. </el-tabs>
  205. </div>
  206. </div>
  207. </div>
  208. </template>
  209. <style lang="scss" scoped>
  210. .Title {
  211. display: flex;
  212. height: 68px;
  213. border-bottom: 1px solid var(--color-border);
  214. font-size: var(--font-size-6);
  215. font-weight: 700;
  216. padding: 0 24px;
  217. align-items: center;
  218. }
  219. .system-message {
  220. display: flex;
  221. height: calc(100% - 68px);
  222. .count {
  223. display: inline-flex;
  224. height: 18px;
  225. padding-left: 5px;
  226. padding-right: 5px;
  227. background-color: var(--color-theme);
  228. border-radius: 9px;
  229. font-size: 12px;
  230. line-height: 18px;
  231. text-align: center;
  232. span {
  233. color: var(--color-white);
  234. font-weight: 700;
  235. }
  236. }
  237. }
  238. .left-nav {
  239. width: 280px;
  240. padding: 24px;
  241. padding-right: 0;
  242. border-right: 1px solid var(--color-border);
  243. .el-collapse {
  244. padding-right: 16px;
  245. border-top: none;
  246. :deep(.el-collapse-item__header) {
  247. font-weight: 700;
  248. }
  249. }
  250. .collapse-item {
  251. display: flex;
  252. align-items: center;
  253. justify-content: space-between;
  254. position: relative;
  255. width: 240px;
  256. height: 48px;
  257. margin-bottom: 4px;
  258. padding: 0 16px;
  259. border-radius: 12px;
  260. &:hover {
  261. background-color: var(--color-system-message-nav-bg);
  262. }
  263. .active-sign {
  264. position: absolute;
  265. top: 50%;
  266. left: 0;
  267. transform: translateY(-50%);
  268. width: 4px;
  269. height: 21px;
  270. border-radius: 12px;
  271. background-color: var(--color-theme);
  272. }
  273. &.is-active {
  274. background-color: var(--color-system-message-nav-bg);
  275. & > span {
  276. font-weight: 700;
  277. color: var(--color-theme);
  278. }
  279. }
  280. &:last-child {
  281. margin-bottom: 8px;
  282. }
  283. }
  284. :deep(.el-collapse-item__header) {
  285. width: 240px;
  286. padding: 16px;
  287. }
  288. }
  289. .right-content {
  290. flex: 1;
  291. padding-top: 24px;
  292. :deep(.el-tabs__nav-scroll) {
  293. padding-left: 16px;
  294. border-bottom: 1px solid var(--color-border);
  295. .el-tabs__item {
  296. font-weight: 400;
  297. color: var(--color-neutral-1);
  298. &.is-active {
  299. font-weight: 700;
  300. }
  301. }
  302. }
  303. :deep(.el-tabs) {
  304. height: 100%;
  305. .el-tabs__content {
  306. overflow-y: auto;
  307. height: 100%;
  308. }
  309. }
  310. :deep {
  311. .scroller {
  312. padding-top: 10px;
  313. }
  314. }
  315. }
  316. </style>