feat: header上的通知及个人站内信功能
parent
8b3b842655
commit
0e44af89b9
|
@ -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