Merge pull request 'feature-nxdev' (#49) from feature-nxdev into main
Reviewed-on: #49
This commit was merged in pull request #49.
This commit is contained in:
@@ -49,7 +49,7 @@ namespace IM_API.Controllers
|
|||||||
var userIdStr = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
var userIdStr = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
int userId = int.Parse(userIdStr);
|
int userId = int.Parse(userIdStr);
|
||||||
var list = await _friendService.GetFriendRequestListAsync(userId,page,limit,desc);
|
var list = await _friendService.GetFriendRequestListAsync(userId,page,limit,desc);
|
||||||
var res = new BaseResponse<List<FriendRequest>>(list);
|
var res = new BaseResponse<List<FriendRequestResDto>>(list);
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using IM_API.Models;
|
||||||
|
|
||||||
|
namespace IM_API.Dtos
|
||||||
|
{
|
||||||
|
public class FriendRequestResDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 申请人
|
||||||
|
/// </summary>
|
||||||
|
public int RequestUser { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 被申请人
|
||||||
|
/// </summary>
|
||||||
|
public int ResponseUser { get; set; }
|
||||||
|
public string Avatar { get; set; }
|
||||||
|
public string NickName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 申请时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime Created { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 申请附言
|
||||||
|
/// </summary>
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 申请状态(0:待通过,1:拒绝,2:同意,3:拉黑)
|
||||||
|
/// </summary>
|
||||||
|
public FriendRequestState State { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ namespace IM_API.Interface.Services
|
|||||||
/// <param name="page"></param>
|
/// <param name="page"></param>
|
||||||
/// <param name="limit"></param>
|
/// <param name="limit"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<List<FriendRequest>> GetFriendRequestListAsync(int userId,int page,int limit, bool desc);
|
Task<List<FriendRequestResDto>> GetFriendRequestListAsync(int userId,int page,int limit, bool desc);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 处理好友请求
|
/// 处理好友请求
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -73,15 +73,29 @@ namespace IM_API.Services
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
#region 获取好友请求列表
|
#region 获取好友请求列表
|
||||||
public async Task<List<FriendRequest>> GetFriendRequestListAsync(int userId, int page, int limit, bool desc)
|
public async Task<List<FriendRequestResDto>> GetFriendRequestListAsync(int userId, int page, int limit, bool desc)
|
||||||
{
|
{
|
||||||
var query = _context.FriendRequests
|
var query = _context.FriendRequests
|
||||||
|
.Include(x => x.ResponseUserNavigation)
|
||||||
|
.Include(x => x.RequestUserNavigation)
|
||||||
.Where(
|
.Where(
|
||||||
x => (x.ResponseUser == userId) ||
|
x => (x.ResponseUser == userId) ||
|
||||||
x.RequestUser == userId
|
x.RequestUser == userId
|
||||||
);
|
)
|
||||||
|
.Select(s => new FriendRequestResDto
|
||||||
|
{
|
||||||
|
Id = s.Id,
|
||||||
|
RequestUser = s.RequestUser,
|
||||||
|
ResponseUser = s.ResponseUser,
|
||||||
|
Avatar = s.RequestUser == userId ? s.ResponseUserNavigation.Avatar : s.RequestUserNavigation.Avatar,
|
||||||
|
Created = s.Created,
|
||||||
|
NickName = s.RequestUser == userId ? s.ResponseUserNavigation.NickName : s.RequestUserNavigation.NickName,
|
||||||
|
Description = s.Description,
|
||||||
|
State = (FriendRequestState)s.State
|
||||||
|
})
|
||||||
|
;
|
||||||
query = query.OrderByDescending(x => x.Id);
|
query = query.OrderByDescending(x => x.Id);
|
||||||
var friendRequestList = await query.Skip((page - 1 * limit)).Take(limit).ToListAsync();
|
var friendRequestList = await query.Skip(((page - 1) * limit)).Take(limit).ToListAsync();
|
||||||
return friendRequestList;
|
return friendRequestList;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ export const messageHandler = (msg) => {
|
|||||||
} else {
|
} else {
|
||||||
conversation.unreadCount += 1;
|
conversation.unreadCount += 1;
|
||||||
}
|
}
|
||||||
console.log(conversation)
|
|
||||||
conversation.dateTime = new Date().toISOString();
|
conversation.dateTime = new Date().toISOString();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -28,5 +28,5 @@ export const friendService = {
|
|||||||
* @param {*} limit
|
* @param {*} limit
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
getFriendRequests: (page = 1, limit = 100) => request.get(`/friend/request?page=${page}&limit=${limit}`)
|
getFriendRequests: (page = 1, limit = 100) => request.get(`/friend/requests?page=${page}&limit=${limit}`)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
export function useBrowserNotification() {
|
||||||
|
const requestPermission = async () => {
|
||||||
|
if ("Notification" in window && Notification.permission === "default") {
|
||||||
|
await Notification.requestPermission();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const send = (title, options = {}) => {
|
||||||
|
if ("Notification" in window && Notification.permission === "granted") {
|
||||||
|
// 如果页面正处于激活状态,通常不需要弹窗提醒,以免干扰用户
|
||||||
|
/*
|
||||||
|
if (document.visibilityState === 'visible' && document.hasFocus()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return new Notification(title, options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { requestPermission, send };
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ import { useChatStore } from "./chat";
|
|||||||
import { authService } from "@/services/auth";
|
import { authService } from "@/services/auth";
|
||||||
import { generateSessionId } from "@/utils/sessionIdTools";
|
import { generateSessionId } from "@/utils/sessionIdTools";
|
||||||
import { messageHandler } from "@/handler/messageHandler";
|
import { messageHandler } from "@/handler/messageHandler";
|
||||||
|
import { useBrowserNotification } from "@/services/useBrowserNotification";
|
||||||
|
import { useConversationStore } from "./conversation";
|
||||||
|
|
||||||
export const useSignalRStore = defineStore('signalr', {
|
export const useSignalRStore = defineStore('signalr', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -40,11 +42,17 @@ export const useSignalRStore = defineStore('signalr', {
|
|||||||
},
|
},
|
||||||
registerHandlers() {
|
registerHandlers() {
|
||||||
const chatStore = useChatStore()
|
const chatStore = useChatStore()
|
||||||
|
const browserNotification = useBrowserNotification();
|
||||||
|
|
||||||
this.connection.on('ReceiveMessage', (msg) => {
|
this.connection.on('ReceiveMessage', (msg) => {
|
||||||
const sessionId = generateSessionId(msg.senderId, msg.receiverId);
|
const sessionId = generateSessionId(msg.senderId, msg.receiverId);
|
||||||
messageHandler(msg);
|
messageHandler(msg);
|
||||||
chatStore.addMessage(msg, sessionId);
|
chatStore.addMessage(msg, sessionId);
|
||||||
|
const conversation = useConversationStore().conversations.find(x => x.targetId == msg.senderId);
|
||||||
|
browserNotification.send(`${conversation.targetName}发来一条消息`, {
|
||||||
|
body: msg.content,
|
||||||
|
icon: conversation.targetAvatar
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.connection.onclose(() => { this.isConnected = false });
|
this.connection.onclose(() => { this.isConnected = false });
|
||||||
|
|||||||
@@ -10,19 +10,31 @@
|
|||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="title-row">
|
<div class="title-row">
|
||||||
<span class="name">{{ item.nickName }}</span>
|
<span class="name">{{ item.nickName }}</span>
|
||||||
<span class="date">{{ item.time }}</span>
|
<span class="date">{{ formatDate(item.created) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="sub-text">{{ item.desc }}</p>
|
<p class="sub-text">{{ item.description }}</p>
|
||||||
<p v-if="item.remark" class="remark-text">{{ item.remark }}</p>
|
<p v-if="item.remark" class="remark-text">{{ item.remark }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<template v-if="item.status === 0">
|
<template v-if="item.state === 0 && item.requestUser != authStore.userInfo.id">
|
||||||
<button class="btn-text btn-reject" @click="item.status = 2">拒绝</button>
|
<button class="btn-text btn-reject" @click="item.status = 2">拒绝</button>
|
||||||
<button class="btn-text btn-accept" @click="item.status = 1">接受</button>
|
<button class="btn-text btn-accept" @click="item.status = 1">接受</button>
|
||||||
</template>
|
</template>
|
||||||
|
<span v-else-if="item.state === 0" class="status-label">
|
||||||
|
待对方同意
|
||||||
|
</span>
|
||||||
|
<span v-else-if="item.state === 1" class="status-label">
|
||||||
|
{{item.requestUser != authStore.userInfo.id ? '已拒绝' : '对方拒绝'}}
|
||||||
|
</span>
|
||||||
|
<span v-else-if="item.state === 2" class="status-label">
|
||||||
|
已添加
|
||||||
|
</span>
|
||||||
|
<span v-else-if="item.state === 3 && item.requestUser != authStore.userInfo.id" class="status-label">
|
||||||
|
已拉黑
|
||||||
|
</span>
|
||||||
<span v-else class="status-label">
|
<span v-else class="status-label">
|
||||||
{{ item.status === 1 ? '已添加' : '已忽略' }}
|
已忽略
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,7 +44,29 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { friendService } from '@/services/friend';
|
||||||
|
import { useMessage } from '@/components/messages/useAlert';
|
||||||
|
import { formatDate } from '@/utils/formatDate';
|
||||||
|
import { useAuthStore } from '@/stores/auth';
|
||||||
|
|
||||||
|
const message = useMessage();
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
const requests = ref([]);
|
||||||
|
|
||||||
|
const loadFriendRequests = async () => {
|
||||||
|
const res = await friendService.getFriendRequests();
|
||||||
|
if(res.code != 0){
|
||||||
|
message.error(res.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requests.value = res.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await loadFriendRequests();
|
||||||
|
})
|
||||||
|
|
||||||
const requests = ref([
|
const requests = ref([
|
||||||
{ id: 1, nickName: '猫之使者', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Felix', desc: '你好,我是你在桌游局认识的朋友。', remark: '来自桌面游戏组', time: '14:20', status: 0 },
|
{ id: 1, nickName: '猫之使者', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Felix', desc: '你好,我是你在桌游局认识的朋友。', remark: '来自桌面游戏组', time: '14:20', status: 0 },
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
<AddMenu :menu-list="addMenuList" @action-active="actionHandler"/>
|
<AddMenu :menu-list="addMenuList" @action-active="actionHandler"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="msgTitleShow" class="showMsg" @click="requestNotificationPermission">
|
||||||
|
<i style="color: red;line-height:0;" v-html="feather.icons['alert-circle'].toSvg({width:14})"></i>
|
||||||
|
<span>新消息无法通知,点我授予通知权限</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="scroll-area">
|
<div class="scroll-area">
|
||||||
<div v-for="s in filteredSessions" :key="s.id"
|
<div v-for="s in filteredSessions" :key="s.id"
|
||||||
@@ -44,14 +48,17 @@ import { useConversationStore } from '@/stores/conversation'
|
|||||||
import AddMenu from '@/components/addMenu.vue'
|
import AddMenu from '@/components/addMenu.vue'
|
||||||
import feather from 'feather-icons'
|
import feather from 'feather-icons'
|
||||||
import SearchUser from '@/components/user/SearchUser.vue'
|
import SearchUser from '@/components/user/SearchUser.vue'
|
||||||
|
import { useBrowserNotification } from '@/services/useBrowserNotification'
|
||||||
|
|
||||||
const conversationStore = useConversationStore();
|
const conversationStore = useConversationStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const browserNotification = useBrowserNotification();
|
||||||
|
|
||||||
const searchQuery = ref('')
|
const searchQuery = ref('')
|
||||||
const activeId = ref(1)
|
const activeId = ref(1)
|
||||||
const conversations = ref([]);
|
const conversations = ref([]);
|
||||||
const searchUserModal = ref(false);
|
const searchUserModal = ref(false);
|
||||||
|
const msgTitleShow = ref(false);
|
||||||
const addMenuList = [
|
const addMenuList = [
|
||||||
{
|
{
|
||||||
text: '发起群聊',
|
text: '发起群聊',
|
||||||
@@ -90,6 +97,11 @@ function actionHandler(type){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function requestNotificationPermission(){
|
||||||
|
await browserNotification.requestPermission();
|
||||||
|
if(Notification.permission === "granted") msgTitleShow.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
async function loadConversation() {
|
async function loadConversation() {
|
||||||
const res = await messageService.getConversations();
|
const res = await messageService.getConversations();
|
||||||
conversations.value = res.data;
|
conversations.value = res.data;
|
||||||
@@ -97,6 +109,7 @@ async function loadConversation() {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await conversationStore.loadUserConversations();
|
await conversationStore.loadUserConversations();
|
||||||
|
if(Notification.permission != "granted") msgTitleShow.value = true;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -118,6 +131,20 @@ onMounted(async () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
.showMsg {
|
||||||
|
/* width: 10px; */
|
||||||
|
height: 20px;
|
||||||
|
background: #e3f98d;
|
||||||
|
font-size: 12px;
|
||||||
|
display: flex;
|
||||||
|
/* text-align: center; */
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: red;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 修复:搜索框美化 */
|
/* 修复:搜索框美化 */
|
||||||
|
|||||||
Reference in New Issue
Block a user