feat: 新增消息通知模块

This commit is contained in:
橙子
2024-05-23 23:40:55 +08:00
parent 695989969d
commit ef220a5b36
20 changed files with 528 additions and 85 deletions

View File

@@ -8,6 +8,7 @@
<script setup>
import mainHub from "@/hubs/mainHub.js";
import noticeSignalR from "@/hubs/noticeHub.js";
import bbsNoticeSignalR from "@/hubs/bbsNoticeHub.js";
import useConfigStore from "@/stores/config";
import { ElConfigProvider } from "element-plus";
import useUserStore from "@/stores/user.js";
@@ -26,7 +27,12 @@ if (loading !== null) {
//加载全局信息
onMounted(async () => {
await configStore.getConfig();
//如果登录了,再连接消息通知
bbsNoticeSignalR();
noticeSignalR();
});
watch(

View File

@@ -0,0 +1,19 @@
import request from "@/config/axios/service";
export function getList(data) {
return request({
url: "/bbs-notice",
method: "get",
params: data,
});
}
export function read(id) {
return request({
url: `/bbs-notice/read/${id??''}`,
method: "put"
});
}

View File

@@ -0,0 +1,20 @@
import signalR from "@/utils/signalR";
import useNoticeStore from "@/stores/notice";
import { dayjs } from 'element-plus'
const receiveMsg=(connection)=> {
const noticeStore = useNoticeStore();
connection.on("Personal", (message) => {
noticeStore.addNotice({
message:message,
isRead:false,
creationTime:dayjs().format()
});
});
};
export default ()=>{
signalR.start(`bbs-notice`,receiveMsg);
}

View File

@@ -7,41 +7,30 @@
<div class="text">{{ configStore.name }}</div>
</div>
<div class="tab">
<el-menu
:default-active="activeIndex"
mode="horizontal"
:ellipsis="false"
@select="handleSelect"
>
<el-menu :default-active="activeIndex" mode="horizontal" :ellipsis="false" @select="handleSelect">
<el-menu-item index="1" @click="enterIndex">主页</el-menu-item>
<el-sub-menu index="2">
<template #title>学习</template>
<el-menu-item index="2-1">学习 one</el-menu-item>
<el-menu-item index="2-2">学习 two</el-menu-item>
<el-menu-item index="2-3">学习 three</el-menu-item>
<el-menu-item index="2-1">前端</el-menu-item>
<el-menu-item index="2-2">后端</el-menu-item>
<el-menu-item index="2-3">运维</el-menu-item>
</el-sub-menu>
<el-sub-menu index="3">
<template #title>资源</template>
<el-menu-item index="3-1">资源 one</el-menu-item>
<el-menu-item index="3-2">资源 two</el-menu-item>
<el-menu-item index="3-3">资源 three</el-menu-item>
<el-menu-item index="3-1">前端</el-menu-item>
<el-menu-item index="3-2">后端</el-menu-item>
<el-menu-item index="3-3">运维</el-menu-item>
</el-sub-menu>
<el-sub-menu index="4">
<template #title>问答</template>
<el-menu-item index="4-1">问答 one</el-menu-item>
<el-menu-item index="4-2">问答 two</el-menu-item>
<el-menu-item index="4-3">问答 three</el-menu-item>
<el-menu-item index="4-1">前端</el-menu-item>
<el-menu-item index="4-2">后端</el-menu-item>
<el-menu-item index="4-3">运维</el-menu-item>
</el-sub-menu>
</el-menu>
</div>
<div class="search-bar">
<el-input
style="width: 300px"
v-model="searchText"
placeholder="全站搜索"
clearable
prefix-icon="Search"
>
<el-input style="width: 300px" v-model="searchText" placeholder="全站搜索" clearable prefix-icon="Search">
<template #append>
<el-button type="primary" plain @click="search">搜索</el-button>
</template>
@@ -50,21 +39,17 @@
<div class="user">
<div class="money" v-if="isLogin">钱钱<span>{{money}}</span></div>
<div class="money" v-if="isLogin">钱钱<span>{{ money }}</span></div>
<el-dropdown trigger="click">
<AvatarInfo :size="30" :isSelf="true" />
<template #dropdown>
<el-dropdown-menu v-if="isLogin">
<el-dropdown-item>你的钱钱{{money}}</el-dropdown-item>
<el-dropdown-item @click="enterProfile"
>进入个人中心</el-dropdown-item
>
<el-dropdown-item @click="enterActivity"
>进入活动页面</el-dropdown-item
>
<el-dropdown-item>你的钱钱{{ money }}</el-dropdown-item>
<el-dropdown-item @click="enterProfile">进入个人中心</el-dropdown-item>
<el-dropdown-item @click="enterActivity">进入活动页面</el-dropdown-item>
<el-dropdown-item @click="logout">登出</el-dropdown-item>
</el-dropdown-menu>
<el-dropdown-menu v-else>
@@ -73,7 +58,42 @@
</template>
</el-dropdown>
<div class="notice">
<el-dropdown trigger="click" :max-height="500">
<el-badge :value="noticeStore.noticeForNoReadCount">
<el-button type="primary">
<el-icon :size="15">
<Bell />
</el-icon>
</el-button>
</el-badge>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item class="notice-oper" style="justify-content: space-between;">
<el-button type="primary" @click="fetchNoticeData">刷新</el-button>
<el-button type="warning" @click="hanldeReadClick">一键已读</el-button>
</el-dropdown-item>
<el-dropdown-item v-for="(item, index) in noticeList" :key="index">
<div v-if="item.isRead" class="notice-msg" v-html="item.message"></div>
<el-badge is-dot v-else >
<div class="notice-msg" v-html="item.message"></div>
</el-badge>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div class="gitee" @click="handleGitClick">
<el-tooltip effect="dark" content="在gitee找到我们" placement="bottom">
@@ -89,15 +109,22 @@
</div>
</template>
<script setup>
import { Bell } from '@element-plus/icons-vue'
import AvatarInfo from "@/components/AvatarInfo.vue";
import { ref } from "vue";
import { computed, onMounted, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import useUserStore from "@/stores/user.js";
import useConfigStore from "@/stores/config";
import useAuths from "@/hooks/useAuths";
import { Session } from "@/utils/storage";
import { storeToRefs } from 'pinia'
import useNoticeStore from "@/stores/notice";
import { ElMessage } from "element-plus";
import { getList as getNoticeList, read as noticeRead } from "@/apis/bbsNoticeApi"
const { isLogin, clearStorage } = useAuths();
//消息通知存储
const noticeStore = useNoticeStore();
const { noticeList } = storeToRefs(noticeStore);
const configStore = useConfigStore();
const router = useRouter();
const route = useRoute();
@@ -105,6 +132,19 @@ const userStore = useUserStore();
const { money } = storeToRefs(userStore)
const activeIndex = ref("1");
const searchText = ref("");
const noticeForNoReadCount=computed(()=>{
return noticeList.value.filter(x => x.isRead ==false).length;
})
//加载初始化离线消息
onMounted(async () => {
await fetchNoticeData();
})
const fetchNoticeData = async () => {
const { data } = await getNoticeList({maxResultCount:20});
noticeStore.setNotices(data.items);
}
const handleSelect = (key, keyPath) => {
console.log(key, keyPath);
};
@@ -130,7 +170,7 @@ const enterIndex = () => {
const enterProfile = () => {
router.push(`/profile/${userStore.userName}`);
};
const enterActivity=()=>{
const enterActivity = () => {
router.push(`/activity`);
}
const toLogin = () => {
@@ -150,19 +190,33 @@ const handleGitClick = () => {
const handleGithubClick = () => {
window.open("https://github.com/ccnetcore/Yi.Abp.Admin");
};
///一键已读
const hanldeReadClick=async ()=>{
await noticeRead();
await fetchNoticeData();
ElMessage({
message: `全部已读`,
type: "success",
});
}
</script>
<style scoped lang="scss">
.money
{
.money {
font-size: small;
color: #FED055;
margin: 0 5px;
span{
span {
font-weight: 600;
}
}
.header {
width: 1300px;
display: flex;
@@ -179,12 +233,14 @@ const handleGithubClick = () => {
display: flex;
align-items: center;
}
.gitee,
.github {
cursor: pointer;
width: 25px;
height: 25px;
margin-left: 15px;
img {
width: 100%;
height: 100%;
@@ -196,31 +252,55 @@ const handleGithubClick = () => {
cursor: pointer;
display: flex;
align-items: center;
.image {
width: 25px;
height: 25px;
img {
width: 100%;
height: 100%;
}
}
.text {
font-weight: bold;
margin-left: 10px;
}
}
.tab {
.el-menu {
height: 90%;
}
:deep(.el-menu--horizontal) {
border-bottom: none;
}
}
.flex-grow {
flex-grow: 1;
}
.img-icon {
margin-right: 0.5rem;
}
.notice {
margin: 0 5px;
&-oper {
display: flex;
justify-content: space-between;
width: 100%;
}
&-msg {
white-space: wrap !important;
width: 400px;
padding: 6px;
font-size: 14px;
line-height: 24px;
border-bottom: 1px solid #f0e9e9;
}
}
</style>

View File

@@ -0,0 +1,33 @@
import { defineStore } from "pinia";
const chatStore = defineStore("notice", {
state: () => ({
noticeList: []
}),
getters: {
noticeForNoReadCount:(state)=>{
return state.noticeList.filter(x => x.isRead ==false).length;
}
},
actions:
{
addNotice(msg) {
this.noticeList.unshift(msg);
},
addNotices(msgs) {
msgs.forEach(item => {
this.addNotice(item);
});
},
setNotices(msgs) {
this.noticeList=msgs;
},
removeNotice(id)
{
this.noticeList = this.noticeList.filter(obj => obj.id != id);
}
},
});
export default chatStore;