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

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Yi.Framework.Ddd.Application.Contracts;
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Notice
{
public class BbsNoticeGetListInputVo:PagedAllResultRequestDto
{
}
}

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Yi.Framework.Bbs.Domain.Shared.Enums;
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Notice
{
public class BbsNoticeGetListOutputDto
{
/// <summary>
/// 消息,支持html
/// </summary>
public string Message { get; set; }
/// <summary>
/// 消息类型
/// </summary>
public NoticeTypeEnum NoticeType { get; }
/// <summary>
/// 是否已读
/// </summary>
public bool IsRead { get; private set; }
/// <summary>
/// 已读时间
/// </summary>
public DateTime? ReadTime { get; private set; }
/// <summary>
/// 消息创建时间
/// </summary>
public DateTime CreationTime { get; }
}
}

View File

@@ -0,0 +1,72 @@
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Yi.Framework.Bbs.Application.Contracts.Dtos.Notice;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Application.Services
{
public class BbsNoticeService : ApplicationService
{
private ISqlSugarRepository<BbsNoticeAggregateRoot, Guid> _repository;
public BbsNoticeService(ISqlSugarRepository<BbsNoticeAggregateRoot, Guid> repository)
{
_repository = repository;
}
/// <summary>
/// 查询用户的消息,需登录
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
[Authorize]
public async Task<PagedResultDto<BbsNoticeGetListOutputDto>> GetListAsync(BbsNoticeGetListInputVo input)
{
RefAsync<int> total = 0;
var entities = await _repository._DbQueryable.Where(x => x.AcceptUserId == CurrentUser.Id)
.OrderByDescending(x => x.CreationTime)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
var output = entities.Adapt<List<BbsNoticeGetListOutputDto>>();
return new PagedResultDto<BbsNoticeGetListOutputDto>(total, output);
}
/// <summary>
/// 已读消息,不传guid代表一键已读需登录
/// </summary>
/// <returns></returns>
[Authorize]
[Route("bbs-notice/read/{noticeId?}")]
public async Task PutReadAsync(Guid? noticeId)
{
//一键已读
if (noticeId is null)
{
await _repository._Db.Updateable<BbsNoticeAggregateRoot>()
.SetColumns(it => it.IsRead == true)
.Where(x => x.AcceptUserId == CurrentUser.Id)
.Where(x => x.IsRead == false)
.ExecuteCommandAsync();
}
//已读一条
else
{
await _repository._Db.Updateable<BbsNoticeAggregateRoot>()
.SetColumns(it => it.IsRead == true)
.Where(x => x.AcceptUserId == CurrentUser.Id)
.Where(x => x.IsRead == false)
.Where(x => x.Id == noticeId)
.ExecuteCommandAsync();
}
}
}
}

View File

@@ -29,7 +29,6 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
/// Todo: 可放入领域层
/// </summary>
/// <returns></returns>
[UnitOfWork]
[Authorize]
public async Task<AgreeDto> PostOperateAsync(Guid discussId)
{
@@ -53,9 +52,8 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
}
else
{
//点赞过,删除即可,修改总点赞数量
await _repository.DeleteByIdAsync(entity.Id);
await _repository.DeleteAsync(entity);
var discussEntity = await _discssRepository.GetByIdAsync(discussId);
if (discussEntity is null)
{

View File

@@ -15,5 +15,9 @@ namespace Yi.Framework.Bbs.Domain.Shared.Consts
public const string No_Exist = "传入的主题id不存在";
public const string Privacy = "【私密】您无该主题权限,可联系作者申请开放";
public const string AgreeNotice = "您的主题[{0}]被[{1}]用户点赞!得到一致认可,发现宝藏内容!";
public const string CommentNotice = "您的主题[{0}]被[{1}]用户评论!评论内容:[{2}]。。。";
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Yi.Framework.Bbs.Domain.Shared.Model
{
/// <summary>
/// 消息通知存储用户信息
/// </summary>
public class HubUserModel
{
public HubUserModel(string connnectionId,Guid userId)
{
ConnnectionId = connnectionId;
UserId = userId;
}
/// <summary>
/// 客户端连接Id
/// </summary>
public string? ConnnectionId { get; }
/// <summary>
/// 用户id
/// </summary>
public Guid? UserId { get; set; }
}
}

View File

@@ -8,6 +8,10 @@ namespace Yi.Framework.Bbs.Domain.Entities
[SugarTable("BbsNotice")]
public class BbsNoticeAggregateRoot : AggregateRoot<Guid>, IHasCreationTime
{
public BbsNoticeAggregateRoot()
{
}
public BbsNoticeAggregateRoot(NoticeTypeEnum noticeType, string message, Guid? acceptUserId = null)
{
this.NoticeType = noticeType;
@@ -47,7 +51,7 @@ namespace Yi.Framework.Bbs.Domain.Entities
/// </summary>
public DateTime? ReadTime { get; private set; }
public DateTime CreationTime { get; }
public DateTime CreationTime { get; set; }
}

View File

@@ -1,8 +1,12 @@
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Local;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.Bbs.Domain.Entities.Forum;
using Yi.Framework.Bbs.Domain.Shared.Consts;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Domain.EventHandlers
@@ -13,49 +17,70 @@ namespace Yi.Framework.Bbs.Domain.EventHandlers
public class AgreeCreatedEventHandler : ILocalEventHandler<EntityCreatedEventData<AgreeEntity>>,
ITransientDependency
{
private ISqlSugarRepository<BbsUserExtraInfoEntity> _userRepository;
private ISqlSugarRepository<UserAggregateRoot> _userRepository;
private ISqlSugarRepository<BbsUserExtraInfoEntity> _userInfoRepository;
private ISqlSugarRepository<AgreeEntity> _agreeRepository;
public AgreeCreatedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userRepository, ISqlSugarRepository<AgreeEntity> agreeRepository)
private ILocalEventBus _localEventBus;
public AgreeCreatedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userInfoRepository, ISqlSugarRepository<AgreeEntity> agreeRepository, ILocalEventBus localEventBus, ISqlSugarRepository<UserAggregateRoot> userRepository)
{
_userRepository = userRepository;
_userInfoRepository = userInfoRepository;
_agreeRepository = agreeRepository;
_localEventBus = localEventBus;
_userRepository = userRepository;
}
public async Task HandleEventAsync(EntityCreatedEventData<AgreeEntity> eventData)
{
var agreeEntity = eventData.Entity;
var userId = await _agreeRepository._DbQueryable.LeftJoin<DiscussAggregateRoot>((agree, discuss) => agree.DiscussId == discuss.Id)
.Select((agree, discuss) => discuss.CreatorId).FirstAsync();
//给创建者发布数量+1
await _userRepository._Db.Updateable<BbsUserExtraInfoEntity>()
//查询主题的信息
var discussAndAgreeDto = await _agreeRepository._DbQueryable
.LeftJoin<DiscussAggregateRoot>((agree, discuss) => agree.DiscussId == discuss.Id)
.Select((agree, discuss) =>
new
{
DiscussTitle = discuss.Title,
DiscussCreatorId = discuss.CreatorId,
})
.FirstAsync();
//查询点赞者用户
var agreeUser = await _userRepository.GetFirstAsync(x => x.Id == agreeEntity.CreatorId);
//给创建者点赞数量+1
await _userInfoRepository._Db.Updateable<BbsUserExtraInfoEntity>()
.SetColumns(it => it.AgreeNumber == it.AgreeNumber + 1)
.Where(it => it.UserId == userId)
.Where(it => it.UserId == discussAndAgreeDto.DiscussCreatorId)
.ExecuteCommandAsync();
//通知主题作者,有人点赞
await _localEventBus.PublishAsync(new BbsNoticeEventArgs(discussAndAgreeDto.DiscussCreatorId!.Value, string.Format(DiscussConst.AgreeNotice, discussAndAgreeDto.DiscussTitle, agreeUser.UserName)), false);
}
}
/// <summary>
/// 取消点赞
/// </summary>
public class AgreeDeletedEventHandler : ILocalEventHandler<EntityCreatedEventData<AgreeEntity>>,
public class AgreeDeletedEventHandler : ILocalEventHandler<EntityDeletedEventData<AgreeEntity>>,
ITransientDependency
{
private ISqlSugarRepository<BbsUserExtraInfoEntity> _userRepository;
private ISqlSugarRepository<AgreeEntity> _agreeRepository;
public AgreeDeletedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userRepository, ISqlSugarRepository<AgreeEntity> agreeRepository)
private ILocalEventBus _localEventBus;
public AgreeDeletedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userRepository, ISqlSugarRepository<AgreeEntity> agreeRepository, ILocalEventBus localEventBus)
{
_userRepository = userRepository;
_agreeRepository = agreeRepository;
_localEventBus = localEventBus;
}
public async Task HandleEventAsync(EntityCreatedEventData<AgreeEntity> eventData)
public async Task HandleEventAsync(EntityDeletedEventData<AgreeEntity> eventData)
{
var agreeEntity = eventData.Entity;
var userId = await _agreeRepository._DbQueryable.LeftJoin<DiscussAggregateRoot>((agree, discuss) => agree.DiscussId == discuss.Id)
.Select((agree, discuss) => discuss.CreatorId).FirstAsync();
//给创建者发布数量-1
//给创建者点赞数量-1
await _userRepository._Db.Updateable<BbsUserExtraInfoEntity>()
.SetColumns(it => it.DiscussNumber == it.DiscussNumber - 1)
.SetColumns(it => it.AgreeNumber == it.AgreeNumber - 1)
.Where(it => it.UserId == userId)
.ExecuteCommandAsync();
}

View File

@@ -1,16 +0,0 @@
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Yi.Framework.Bbs.Domain.Entities;
namespace Yi.Framework.Bbs.Domain.EventHandlers
{
public class BbsNoticeCreatedEventHandler : ILocalEventHandler<EntityCreatedEventData<BbsNoticeAggregateRoot>>,
ITransientDependency
{
public Task HandleEventAsync(EntityCreatedEventData<BbsNoticeAggregateRoot> eventData)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,47 @@
using Microsoft.AspNetCore.SignalR;
using SqlSugar;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.Bbs.Domain.SignalRHubs;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Domain.EventHandlers
{
/// <summary>
/// bbs消息推送处理
/// </summary>
public class BbsNoticeSendEventHandler : ILocalEventHandler<BbsNoticeEventArgs>,
ITransientDependency
{
private IHubContext<BbsNoticeHub> _hubContext;
private ISqlSugarRepository<BbsNoticeAggregateRoot, Guid> _repository;
public BbsNoticeSendEventHandler(IHubContext<BbsNoticeHub> hubContext, ISqlSugarRepository<BbsNoticeAggregateRoot, Guid> sugarRepository)
{
_hubContext = hubContext;
_repository = sugarRepository;
}
public async Task HandleEventAsync(BbsNoticeEventArgs eventData)
{
//离线存储
await _repository.InsertAsync(new BbsNoticeAggregateRoot(eventData.NoticeType, eventData.Message, eventData.AcceptUserId));
switch (eventData.NoticeType)
{
case Shared.Enums.NoticeTypeEnum.Personal:
if (BbsNoticeHub.HubUserModels.TryGetValue(eventData.AcceptUserId.ToString(), out var hubUserModel))
{
_hubContext.Clients.Client(hubUserModel.ConnnectionId).SendAsync(NoticeTypeEnum.Personal.ToString(), eventData.Message);
}
break;
case Shared.Enums.NoticeTypeEnum.Broadcast:
_hubContext.Clients.All.SendAsync(NoticeTypeEnum.Broadcast.ToString(), eventData.Message);
break;
default:
break;
}
}
}
}

View File

@@ -1,8 +1,13 @@
using Volo.Abp.DependencyInjection;
using TencentCloud.Tbm.V20180129.Models;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Local;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.Bbs.Domain.Entities.Forum;
using Yi.Framework.Bbs.Domain.Shared.Consts;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Domain.EventHandlers
@@ -13,10 +18,14 @@ namespace Yi.Framework.Bbs.Domain.EventHandlers
public class CommentCreatedEventHandler : ILocalEventHandler<EntityCreatedEventData<CommentAggregateRoot>>,
ITransientDependency
{
private ISqlSugarRepository<BbsUserExtraInfoEntity> _userRepository;
public CommentCreatedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userRepository)
private ILocalEventBus _localEventBus;
private ISqlSugarRepository<DiscussAggregateRoot> _discussRepository;
private ISqlSugarRepository<UserAggregateRoot> _userRepository;
public CommentCreatedEventHandler(ILocalEventBus localEventBus, ISqlSugarRepository<DiscussAggregateRoot> discussRepository, ISqlSugarRepository<UserAggregateRoot> userRepository)
{
_userRepository = userRepository;
_localEventBus = localEventBus;
_discussRepository = discussRepository;
}
public async Task HandleEventAsync(EntityCreatedEventData<CommentAggregateRoot> eventData)
{
@@ -27,6 +36,24 @@ namespace Yi.Framework.Bbs.Domain.EventHandlers
.SetColumns(it => it.CommentNumber == it.CommentNumber + 1)
.Where(it => it.UserId == commentEntity.CreatorId)
.ExecuteCommandAsync();
var disucssDto = await _discussRepository._DbQueryable
.Where(x => x.Id == commentEntity.DiscussId)
.LeftJoin<UserAggregateRoot>((dicuss, user) => dicuss.CreatorId == user.Id)
.Select((dicuss, user) =>
new
{
DiscussId = user.Id,
DiscussTitle = dicuss.Title,
})
.FirstAsync();
var commentUser = await _userRepository.GetFirstAsync(x => x.Id == commentEntity.CreatorId);
//截取10个长度
var content = commentEntity.Content.Length >= 10 ? commentEntity.Content.Substring(0, 10) : commentEntity.Content;
//通知主题作者,有人评论
await _localEventBus.PublishAsync(new BbsNoticeEventArgs(disucssDto.DiscussId, string.Format(DiscussConst.CommentNotice, disucssDto.DiscussTitle, commentUser.UserName, content)), false);
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.AspNetCore.SignalR;
using Yi.Framework.Bbs.Domain.Shared.Model;
namespace Yi.Framework.Bbs.Domain.SignalRHubs
{
[HubRoute("/hub/bbs-notice")]
[Authorize]
public class BbsNoticeHub : AbpHub
{
/// <summary>
/// hub连接与用户id的映射关系存储
/// </summary>
public static ConcurrentDictionary<string, HubUserModel> HubUserModels = new ConcurrentDictionary<string, HubUserModel>();
/// <summary>
/// 连接添加用户信息
/// </summary>
/// <returns></returns>
public override Task OnConnectedAsync()
{
var hubUser = new HubUserModel(Context.ConnectionId, CurrentUser.Id!.Value);
HubUserModels.TryAdd(CurrentUser.Id.Value.ToString(), hubUser);
return Task.CompletedTask;
}
/// <summary>
/// 断开连接,去除用户连接信息
/// </summary>
/// <param name="exception"></param>
/// <returns></returns>
public override Task OnDisconnectedAsync(Exception exception)
{
HubUserModels.TryRemove(CurrentUser.Id.Value.ToString(), out _);
return Task.CompletedTask;
}
}
}

View File

@@ -12,7 +12,7 @@
"@lucky-canvas/vue": "^0.1.11",
"@microsoft/signalr": "^8.0.0",
"axios": "^1.3.4",
"dayjs": "^1.11.10",
"dayjs": "^1.11.11",
"echarts": "^5.4.2",
"element-plus": "^2.2.32",
"highlight": "^0.2.4",
@@ -1894,9 +1894,9 @@
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
},
"node_modules/dayjs": {
"version": "1.11.10",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz",
"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
"version": "1.11.11",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.11.tgz",
"integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg=="
},
"node_modules/debug": {
"version": "4.3.4",
@@ -5551,9 +5551,9 @@
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
},
"dayjs": {
"version": "1.11.10",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz",
"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
"version": "1.11.11",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.11.tgz",
"integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg=="
},
"debug": {
"version": "4.3.4",

View File

@@ -13,7 +13,7 @@
"@lucky-canvas/vue": "^0.1.11",
"@microsoft/signalr": "^8.0.0",
"axios": "^1.3.4",
"dayjs": "^1.11.10",
"dayjs": "^1.11.11",
"echarts": "^5.4.2",
"element-plus": "^2.2.32",
"highlight": "^0.2.4",

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,7 +39,7 @@
<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" />
@@ -58,13 +47,9 @@
<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;

View File

@@ -955,10 +955,10 @@ csstype@^2.6.8:
resolved "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz"
integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==
dayjs@^1.11.10, dayjs@^1.11.3:
version "1.11.10"
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz"
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
dayjs@^1.11.11, dayjs@^1.11.3:
version "1.11.11"
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.11.tgz"
integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==
debug@^4.1.0, debug@^4.3.1, debug@^4.3.4, debug@4:
version "4.3.4"