From 0e44af89b9b47c5e3f955b9208ff2c2f1f0d24af Mon Sep 17 00:00:00 2001
From: dap1 <15891557205@163.com>
Date: Fri, 19 May 2023 18:07:38 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20header=E4=B8=8A=E7=9A=84=E9=80=9A?=
 =?UTF-8?q?=E7=9F=A5=E5=8F=8A=E4=B8=AA=E4=BA=BA=E7=AB=99=E5=86=85=E4=BF=A1?=
 =?UTF-8?q?=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/api/system/notify/message.ts              |  2 +-
 src/enums/pageEnum.ts                         |  3 +-
 .../header/components/notify/index.vue        | 93 +++++-------------
 src/store/modules/userMessage.ts              | 24 +++++
 src/utils/dict.ts                             |  1 +
 src/views/infra/fileConfig/index.vue          |  1 +
 .../MessageInfoModal.vue                      |  2 +-
 .../system/notify/components/message.data.ts  | 97 +++++++++++++++++++
 src/views/system/notify/message/index.vue     |  2 +-
 src/views/system/notify/my/index.vue          | 78 ++++++++++++---
 src/views/system/notify/my/my.data.ts         |  2 +-
 11 files changed, 220 insertions(+), 85 deletions(-)
 create mode 100644 src/store/modules/userMessage.ts
 rename src/views/system/notify/{message => components}/MessageInfoModal.vue (90%)
 create mode 100644 src/views/system/notify/components/message.data.ts

diff --git a/src/api/system/notify/message.ts b/src/api/system/notify/message.ts
index 07cda56e..2852fe29 100644
--- a/src/api/system/notify/message.ts
+++ b/src/api/system/notify/message.ts
@@ -28,5 +28,5 @@ export function getUnreadNotifyMessageList() {
 
 // 获得当前用户的未读站内信数量
 export function getUnreadNotifyMessageCount() {
-  return defHttp.get({ url: '/system/notify-message/get-unread-count' })
+  return defHttp.get<number>({ url: '/system/notify-message/get-unread-count' })
 }
diff --git a/src/enums/pageEnum.ts b/src/enums/pageEnum.ts
index d329ec4a..e1da8e39 100644
--- a/src/enums/pageEnum.ts
+++ b/src/enums/pageEnum.ts
@@ -6,5 +6,6 @@ export enum PageEnum {
   // error page path
   ERROR_PAGE = '/exception',
   // error log page path
-  ERROR_LOG_PAGE = '/error-log/list'
+  ERROR_LOG_PAGE = '/error-log/list',
+  MESSAGE_PAGE = '/profile/notify-message'
 }
diff --git a/src/layouts/default/header/components/notify/index.vue b/src/layouts/default/header/components/notify/index.vue
index 646f52c3..51fdbfa2 100644
--- a/src/layouts/default/header/components/notify/index.vue
+++ b/src/layouts/default/header/components/notify/index.vue
@@ -1,80 +1,35 @@
 <template>
-  <div :class="prefixCls">
-    <Popover title="" trigger="click" :overlayClassName="`${prefixCls}__overlay`">
-      <Badge :count="count" dot :numberStyle="numberStyle">
+  <div>
+    <Tooltip :title="tips">
+      <Badge :count="unreadCount" :offset="[0, 15]" size="small" @click="go({ path: PageEnum.MESSAGE_PAGE })">
         <BellOutlined />
       </Badge>
-      <template #content>
-        <Tabs>
-          <template v-for="item in listData" :key="item.key">
-            <TabPane>
-              <template #tab>
-                {{ item.name }}
-                <span v-if="item.list.length !== 0">({{ item.list.length }})</span>
-              </template>
-              <!-- 绑定title-click事件的通知列表中标题是“可点击”的-->
-              <NoticeList :list="item.list" v-if="item.key === '1'" @title-click="onNoticeClick" />
-              <NoticeList :list="item.list" v-else />
-            </TabPane>
-          </template>
-        </Tabs>
-      </template>
-    </Popover>
+    </Tooltip>
   </div>
 </template>
 <script lang="ts" setup>
-import { computed, ref } from 'vue'
-import { Popover, Tabs, Badge } from 'ant-design-vue'
+import { onMounted, computed } from 'vue'
+import { Badge, Tooltip } from 'ant-design-vue'
 import { BellOutlined } from '@ant-design/icons-vue'
-import { tabListData, ListItem } from './data'
-import NoticeList from './NoticeList.vue'
-import { useDesign } from '@/hooks/web/useDesign'
-import { useMessage } from '@/hooks/web/useMessage'
-
-const TabPane = Tabs.TabPane
-const numberStyle = ref({})
-const { prefixCls } = useDesign('header-notify')
-const { createMessage } = useMessage()
-const listData = ref(tabListData)
-
-const count = computed(() => {
-  let count = 0
-  for (let i = 0; i < tabListData.length; i++) {
-    count += tabListData[i].list.length
+import { useGo } from '@/hooks/web/usePage'
+import { PageEnum } from '@/enums/pageEnum'
+import { useUserMessageStore } from '@/store/modules/userMessage'
+import { storeToRefs } from 'pinia'
+
+const go = useGo()
+
+const store = useUserMessageStore()
+const { unreadCount } = storeToRefs(store)
+const tips = computed<string>(() => {
+  if (unreadCount.value === 0) {
+    return '查看站内信'
   }
-  return count
+  return `查看站内信: 未读 ${unreadCount.value} 条`
 })
 
-function onNoticeClick(record: ListItem) {
-  createMessage.success('你点击了通知,ID=' + record.id)
-  // 可以直接将其标记为已读(为标题添加删除线),此处演示的代码会切换删除线状态
-  record.titleDelete = !record.titleDelete
-}
+onMounted(async () => {
+  // 通过store进行更新
+  store.updateUnreadCount()
+})
 </script>
-<style lang="less">
-@prefix-cls: ~'@{namespace}-header-notify';
-
-.@{prefix-cls} {
-  padding-top: 2px;
-
-  &__overlay {
-    max-width: 360px;
-  }
-
-  .ant-tabs-content {
-    width: 300px;
-  }
-
-  .ant-badge {
-    font-size: 18px;
-
-    .ant-badge-multiple-words {
-      padding: 0 4px;
-    }
-
-    svg {
-      width: 0.9em;
-    }
-  }
-}
-</style>
+<style lang="less"></style>
diff --git a/src/store/modules/userMessage.ts b/src/store/modules/userMessage.ts
new file mode 100644
index 00000000..4a24041e
--- /dev/null
+++ b/src/store/modules/userMessage.ts
@@ -0,0 +1,24 @@
+import { defineStore } from 'pinia'
+import { getUnreadNotifyMessageCount } from '@/api/system/notify/message'
+
+type MessageState = {
+  unreadCount: number // 未读消息数量
+}
+
+export const useUserMessageStore = defineStore('userMessage', {
+  state: (): MessageState => ({
+    unreadCount: 0
+  }),
+  getters: {
+    getUnreadCount(state) {
+      return state.unreadCount
+    }
+  },
+  actions: {
+    // 更新未读消息的数量
+    async updateUnreadCount() {
+      const count = await getUnreadNotifyMessageCount()
+      this.unreadCount = count
+    }
+  }
+})
diff --git a/src/utils/dict.ts b/src/utils/dict.ts
index 853d8bac..7088efec 100644
--- a/src/utils/dict.ts
+++ b/src/utils/dict.ts
@@ -53,6 +53,7 @@ export function getDictOpts(dictType: string) {
 export function getDictOptions(dictType: string, valueType?: 'string' | 'number' | 'boolean') {
   const dictOption: DictDataType[] = []
   const dictOptions: DictDataType[] = getDictDatas(dictType)
+  console.log(dictOptions)
   if (dictOptions && dictOptions.length > 0) {
     dictOptions.forEach((dict: DictDataType) => {
       dictOption.push({
diff --git a/src/views/infra/fileConfig/index.vue b/src/views/infra/fileConfig/index.vue
index 8ed8659d..77f5b557 100644
--- a/src/views/infra/fileConfig/index.vue
+++ b/src/views/infra/fileConfig/index.vue
@@ -93,6 +93,7 @@ function handleMaster(record: Recordable) {
     async onOk() {
       await updateFileConfigMaster(record.id)
       createMessage.success('配置成功')
+      reload()
     }
   })
 }
diff --git a/src/views/system/notify/message/MessageInfoModal.vue b/src/views/system/notify/components/MessageInfoModal.vue
similarity index 90%
rename from src/views/system/notify/message/MessageInfoModal.vue
rename to src/views/system/notify/components/MessageInfoModal.vue
index 7dbff81b..887ebd3f 100644
--- a/src/views/system/notify/message/MessageInfoModal.vue
+++ b/src/views/system/notify/components/MessageInfoModal.vue
@@ -1,5 +1,5 @@
 <template>
-  <BasicModal title="详情" @register="innerRegister">
+  <BasicModal title="站内信详情" @register="innerRegister">
     <Description @register="descriptionRegister" />
   </BasicModal>
 </template>
diff --git a/src/views/system/notify/components/message.data.ts b/src/views/system/notify/components/message.data.ts
new file mode 100644
index 00000000..4f1c07ea
--- /dev/null
+++ b/src/views/system/notify/components/message.data.ts
@@ -0,0 +1,97 @@
+import { useRender } from '@/components/Table'
+import { DICT_TYPE } from '@/utils/dict'
+import { JsonPreview } from '@/components/CodeEditor'
+import { DescItem } from '@/components/Description/index'
+import { h } from 'vue'
+
+// 站内信详情modal
+export const infoSchema: DescItem[] = [
+  {
+    field: 'id',
+    label: '编号',
+    labelMinWidth: 50
+  },
+  {
+    field: 'readStatus',
+    label: '是否已读',
+    render: (value) => {
+      return useRender.renderDict(value, DICT_TYPE.INFRA_BOOLEAN_STRING)
+    }
+  },
+  {
+    field: 'userType',
+    label: '用户类型',
+    render: (value) => {
+      console.log(value)
+      return useRender.renderDict(value, DICT_TYPE.USER_TYPE)
+    }
+  },
+  {
+    field: 'userType',
+    label: '用户编号'
+  },
+  {
+    field: 'templateId',
+    label: '模板编号'
+  },
+  {
+    field: 'templateCode',
+    label: '模板编码'
+  },
+  {
+    field: 'templateNickname',
+    label: '发送人名称'
+  },
+  {
+    field: 'templateContent',
+    label: '模板内容'
+  },
+  {
+    field: 'templateParams',
+    label: '模板参数',
+    render: (value) => {
+      return h(JsonPreview, { data: value })
+    }
+  },
+  {
+    field: 'templateType',
+    label: '模板类型',
+    render: (value) => {
+      return useRender.renderDict(value, DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE)
+    }
+  },
+  {
+    field: 'readTime',
+    label: '阅读时间',
+    render: (value) => {
+      if (!value) {
+        return useRender.renderTag('未阅读')
+      }
+      return useRender.renderDate(value)
+    }
+  },
+  {
+    field: 'createTime',
+    label: '创建时间',
+    render: (value) => {
+      return useRender.renderDate(value)
+    }
+  }
+]
+
+// 站内信详情
+export interface MessageInfo {
+  userId: number
+  userType: number
+  templateId: number
+  templateCode: string
+  templateNickname: string
+  templateContent: string
+  templateType: number
+  templateParams: { [key: string]: string }
+  readStatus: boolean
+  readTime?: any
+  id: number
+  createTime: number
+  key: string
+}
diff --git a/src/views/system/notify/message/index.vue b/src/views/system/notify/message/index.vue
index 25d48c44..8f7a0d0e 100644
--- a/src/views/system/notify/message/index.vue
+++ b/src/views/system/notify/message/index.vue
@@ -24,7 +24,7 @@ import { useI18n } from '@/hooks/web/useI18n'
 import { BasicTable, useTable, TableAction } from '@/components/Table'
 import { getNotifyMessagePage } from '@/api/system/notify/message'
 import { columns, searchFormSchema } from './message.data'
-import MessageInfoModal from './MessageInfoModal.vue'
+import MessageInfoModal from '@/views/system/notify/components/MessageInfoModal.vue'
 import { useModal } from '@/components/Modal'
 
 defineOptions({ name: 'SystemMessage' })
diff --git a/src/views/system/notify/my/index.vue b/src/views/system/notify/my/index.vue
index 60ea3aa7..072654ea 100644
--- a/src/views/system/notify/my/index.vue
+++ b/src/views/system/notify/my/index.vue
@@ -1,16 +1,38 @@
 <template>
   <div>
-    <BasicTable @register="registerTable">
+    <BasicTable @register="registerTable" bordered>
       <template #toolbar>
-        <a-button type="primary" @click="handleUpdateList"> 标记已读 </a-button>
-        <a-button type="primary" @click="handleUpdateAll"> 全部已读 </a-button>
+        <a-button preIcon="solar:check-read-line-duotone" type="primary" @click="handleUpdateList" :disabled="readedDisabled">
+          标记已读
+        </a-button>
+        <a-button preIcon="solar:check-read-linear" type="primary" @click="handleUpdateAll"> 全部已读 </a-button>
       </template>
       <template #bodyCell="{ column, record }">
         <template v-if="column.key === 'action'">
-          <TableAction :actions="[{ icon: IconEnum.EDIT, label: '已读', onClick: handleUpdateSingle.bind(null, record) }]" />
+          <!--阻止事件冒泡 勾选框 -->
+          <TableAction
+            stopButtonPropagation
+            :actions="[
+              {
+                icon: IconEnum.EDIT,
+                label: '已读',
+                color: 'warning',
+                ifShow: () => {
+                  return !record.readStatus
+                },
+                onClick: handleUpdateSingle.bind(null, record)
+              },
+              {
+                icon: IconEnum.LOG,
+                label: '详情',
+                onClick: handleInfo.bind(null, record)
+              }
+            ]"
+          />
         </template>
       </template>
     </BasicTable>
+    <MessageInfoModal @register="registerModal" />
   </div>
 </template>
 <script lang="ts" setup>
@@ -20,18 +42,32 @@ import { IconEnum } from '@/enums/appEnum'
 import { BasicTable, useTable, TableAction } from '@/components/Table'
 import { getMyNotifyMessagePage, updateAllNotifyMessageRead, updateNotifyMessageRead } from '@/api/system/notify/message'
 import { columns, searchFormSchema } from './my.data'
+import MessageInfoModal from '@/views/system/notify/components/MessageInfoModal.vue'
+import { useModal } from '@/components/Modal'
+import { computed } from 'vue'
+import { useUserMessageStore } from '@/store/modules/userMessage'
 
 defineOptions({ name: 'SystemMyMessage' })
 
 const { t } = useI18n()
 const { createMessage } = useMessage()
+const [registerModal, { openModal }] = useModal()
+const store = useUserMessageStore()
 
-const [registerTable, { getSelectRowKeys, reload }] = useTable({
+const [registerTable, { getSelectRowKeys, clearSelectedRowKeys, reload }] = useTable({
   title: '我的站内信列表',
   api: getMyNotifyMessagePage,
   columns,
-  formConfig: { labelWidth: 120, schemas: searchFormSchema },
-  rowSelection: { type: 'checkbox' },
+  formConfig: { labelWidth: 130, schemas: searchFormSchema },
+  rowSelection: {
+    type: 'checkbox',
+    getCheckboxProps: (record: Recordable) => {
+      return {
+        // 已读的消息disabled 不可选
+        disabled: record.readStatus
+      }
+    }
+  },
   rowKey: 'id',
   useSearchForm: true,
   showTableSetting: true,
@@ -44,6 +80,13 @@ const [registerTable, { getSelectRowKeys, reload }] = useTable({
   }
 })
 
+/**
+ * 已读按钮的disabled  未选中则disabled
+ */
+const readedDisabled = computed<boolean>(() => {
+  return getSelectRowKeys().length === 0
+})
+
 function handleUpdateList() {
   const ids = getSelectRowKeys()
   handleUpdate(ids)
@@ -53,15 +96,28 @@ async function handleUpdateSingle(record: Recordable) {
   await handleUpdate([record.id])
 }
 
+function afterRead(msg: string) {
+  createMessage.success(msg)
+  // 更新未读消息
+  store.updateUnreadCount()
+  // 重加载表格
+  reload()
+  // 清除选中的行
+  clearSelectedRowKeys()
+}
+
 async function handleUpdate(ids) {
   await updateNotifyMessageRead(ids)
-  createMessage.success('标记已读成功!')
-  reload()
+  afterRead('标记已读成功!')
 }
 
 async function handleUpdateAll() {
   await updateAllNotifyMessageRead()
-  createMessage.success('全部已读成功!')
-  reload()
+  afterRead('全部已读成功!')
+}
+
+const handleInfo = (record: any) => {
+  console.log(JSON.stringify(record, Object.keys(record), 2))
+  openModal(true, record)
 }
 </script>
diff --git a/src/views/system/notify/my/my.data.ts b/src/views/system/notify/my/my.data.ts
index c9533f6e..1a5e9251 100644
--- a/src/views/system/notify/my/my.data.ts
+++ b/src/views/system/notify/my/my.data.ts
@@ -31,7 +31,7 @@ export const columns: BasicColumn[] = [
   {
     title: '是否已读',
     dataIndex: 'readStatus',
-    width: 180,
+    width: 100,
     customRender: ({ text }) => {
       return useRender.renderDict(text, DICT_TYPE.INFRA_BOOLEAN_STRING)
     }