feat: header上的通知及个人站内信功能

pull/18/head
dap1 2023-05-19 18:07:38 +08:00
parent 8b3b842655
commit 0e44af89b9
11 changed files with 218 additions and 83 deletions

View File

@ -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' })
}

View File

@ -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'
}

View File

@ -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>

View File

@ -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
}
}
})

View File

@ -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({

View File

@ -93,6 +93,7 @@ function handleMaster(record: Recordable) {
async onOk() {
await updateFileConfigMaster(record.id)
createMessage.success('配置成功')
reload()
}
})
}

View File

@ -1,5 +1,5 @@
<template>
<BasicModal title="详情" @register="innerRegister">
<BasicModal title="站内信详情" @register="innerRegister">
<Description @register="descriptionRegister" />
</BasicModal>
</template>

View File

@ -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
}

View File

@ -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' })

View File

@ -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>

View File

@ -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)
}