Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vben
						commit
						f4346619d8
					
				| 
						 | 
				
			
			@ -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' })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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'
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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'
 | 
			
		||||
import { useGo } from '@/hooks/web/usePage'
 | 
			
		||||
import { PageEnum } from '@/enums/pageEnum'
 | 
			
		||||
import { useUserMessageStore } from '@/store/modules/userMessage'
 | 
			
		||||
import { storeToRefs } from 'pinia'
 | 
			
		||||
 | 
			
		||||
const TabPane = Tabs.TabPane
 | 
			
		||||
const numberStyle = ref({})
 | 
			
		||||
const { prefixCls } = useDesign('header-notify')
 | 
			
		||||
const { createMessage } = useMessage()
 | 
			
		||||
const listData = ref(tabListData)
 | 
			
		||||
const go = useGo()
 | 
			
		||||
 | 
			
		||||
const count = computed(() => {
 | 
			
		||||
  let count = 0
 | 
			
		||||
  for (let i = 0; i < tabListData.length; i++) {
 | 
			
		||||
    count += tabListData[i].list.length
 | 
			
		||||
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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -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({
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -93,6 +93,7 @@ function handleMaster(record: Recordable) {
 | 
			
		|||
    async onOk() {
 | 
			
		||||
      await updateFileConfigMaster(record.id)
 | 
			
		||||
      createMessage.success('配置成功')
 | 
			
		||||
      reload()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <BasicModal title="详情" @register="innerRegister">
 | 
			
		||||
  <BasicModal title="站内信详情" @register="innerRegister">
 | 
			
		||||
    <Description @register="descriptionRegister" />
 | 
			
		||||
  </BasicModal>
 | 
			
		||||
</template>
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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' })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue