From fe4a9173a4e0af9fa022e373a547e63dc87fd109 Mon Sep 17 00:00:00 2001 From: nanxun Date: Fri, 12 Dec 2025 22:42:47 +0800 Subject: [PATCH] =?UTF-8?q?add(components):=20=E6=B7=BB=E5=8A=A0MyInput?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/IM_API/Configs/MapperConfig.cs | 6 +- .../Configs/ServiceCollectionExtensions.cs | 1 + backend/IM_API/Dtos/ClearConversationsDto.cs | 4 +- backend/IM_API/Dtos/MessageDto.cs | 2 +- backend/IM_API/Dtos/SignalRResponseDto.cs | 66 ++ backend/IM_API/Hubs/ChatHub.cs | 25 +- backend/IM_API/Models/ImContext.Custom.cs | 1 + backend/IM_API/Services/MessageService.cs | 74 +++ backend/IM_API/appsettings.json | 2 +- frontend/web/package-lock.json | 46 +- frontend/web/package.json | 1 + frontend/web/src/App.vue | 19 +- frontend/web/src/components/MyInput.vue | 155 +++++ frontend/web/src/components/Window.vue | 181 +++--- frontend/web/src/router/index.js | 2 + frontend/web/src/views/Test.vue | 20 + frontend/web/src/views/auth/Login.vue | 614 +++++++----------- 17 files changed, 721 insertions(+), 498 deletions(-) create mode 100644 backend/IM_API/Dtos/SignalRResponseDto.cs create mode 100644 backend/IM_API/Services/MessageService.cs create mode 100644 frontend/web/src/components/MyInput.vue create mode 100644 frontend/web/src/views/Test.vue diff --git a/backend/IM_API/Configs/MapperConfig.cs b/backend/IM_API/Configs/MapperConfig.cs index 1b8b0af..6a41699 100644 --- a/backend/IM_API/Configs/MapperConfig.cs +++ b/backend/IM_API/Configs/MapperConfig.cs @@ -54,12 +54,14 @@ namespace IM_API.Configs ; CreateMap() .ForMember(dest => dest.Sender, opt => opt.MapFrom(src => src.SenderId)) - .ForMember(dest => dest.ChatTypeEnum,opt => opt.MapFrom(src => src.ChatType)) - .ForMember(dest => dest.MsgTypeEnum, opt => opt.MapFrom(src => src.Type)) + .ForMember(dest => dest.ChatTypeEnum,opt => opt.MapFrom(src => Enum.Parse(src.ChatType,true))) + .ForMember(dest => dest.MsgTypeEnum, opt => opt.MapFrom(src => Enum.Parse(src.Type,true))) .ForMember(dest => dest.Created, opt => opt.MapFrom(src => src.TimeStamp)) .ForMember(dest => dest.Content, opt => opt.MapFrom(src => src.Content)) .ForMember(dest => dest.Recipient, opt => opt.MapFrom(src => src.ReceiverId)) .ForMember(dest => dest.StateEnum, opt => opt.MapFrom(src => MessageState.Sent)) + .ForMember(dest => dest.ChatType, opt => opt.Ignore()) + .ForMember(dest => dest.MsgType, opt => opt.Ignore()) ; } diff --git a/backend/IM_API/Configs/ServiceCollectionExtensions.cs b/backend/IM_API/Configs/ServiceCollectionExtensions.cs index e24c0e7..bbb43c4 100644 --- a/backend/IM_API/Configs/ServiceCollectionExtensions.cs +++ b/backend/IM_API/Configs/ServiceCollectionExtensions.cs @@ -11,6 +11,7 @@ namespace IM_API.Configs services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddSingleton(); services.AddSingleton(); return services; diff --git a/backend/IM_API/Dtos/ClearConversationsDto.cs b/backend/IM_API/Dtos/ClearConversationsDto.cs index 0d8c5c9..34cb3df 100644 --- a/backend/IM_API/Dtos/ClearConversationsDto.cs +++ b/backend/IM_API/Dtos/ClearConversationsDto.cs @@ -6,13 +6,13 @@ /// /// 聊天类型 /// - public ChatType ChatType { get; set; } + public MsgChatType ChatType { get; set; } /// /// 目标ID,聊天类型为群则为群id,私聊为用户id /// public int TargetId { get; set; } } - public enum ChatType + public enum MsgChatType { /// /// 私聊 diff --git a/backend/IM_API/Dtos/MessageDto.cs b/backend/IM_API/Dtos/MessageDto.cs index 6a4359a..5953a7c 100644 --- a/backend/IM_API/Dtos/MessageDto.cs +++ b/backend/IM_API/Dtos/MessageDto.cs @@ -1,5 +1,5 @@ namespace IM_API.Dtos { public record MessageBaseDto( - string Type,string ChatType, string MsgId,int SenderId,int ReceiverId,string Content,DateTime TimeStamp); + string Type,string ChatType, string? MsgId,int SenderId,int ReceiverId,string Content,DateTime TimeStamp); } diff --git a/backend/IM_API/Dtos/SignalRResponseDto.cs b/backend/IM_API/Dtos/SignalRResponseDto.cs new file mode 100644 index 0000000..f34f09e --- /dev/null +++ b/backend/IM_API/Dtos/SignalRResponseDto.cs @@ -0,0 +1,66 @@ +using IM_API.Tools; + +namespace IM_API.Dtos +{ + public class SignalRResponseDto + { + public string Type { get; set; } + public string Message { get; set; } + public int Code { get; set; } + public string Status { get; set; } + public SignalRResponseDto(SignalRResponseType type,CodeDefine codeDefine) + { + this.Type = type.ToString(); + this.Code = codeDefine.Code; + this.Message = codeDefine.Message; + this.Status = codeDefine.Message; + } + public SignalRResponseDto(SignalRResponseType type) + { + this.Type = type.ToString(); + this.Code = CodeDefine.SUCCESS.Code; + this.Message = CodeDefine.SUCCESS.Message; + this.Status = CodeDefine.SUCCESS.Message; + } + } + + public enum SignalRResponseType + { + /// + /// 消息 + /// + MESSAGE = 0, + /// + /// 鉴权 + /// + AUTH = 1, + /// + /// 心跳 + /// + HEARTBEAT = 2, + /// + /// 消息回执 + /// + MESSAGE_ACK = 3, + /// + /// 消息撤回 + /// + MESSAGE_RECALL = 4, + /// + /// 好友请求 + /// + FRIEND_REQUEST = 5, + /// + /// 群邀请 + /// + GROUP_INVITE = 6, + /// + /// 系统通知 + /// + SYSTEM_NOTICE = 7, + /// + /// 错误 + /// + ERROR = 8 + } +} diff --git a/backend/IM_API/Hubs/ChatHub.cs b/backend/IM_API/Hubs/ChatHub.cs index b7588b8..b0c7bc3 100644 --- a/backend/IM_API/Hubs/ChatHub.cs +++ b/backend/IM_API/Hubs/ChatHub.cs @@ -8,28 +8,35 @@ namespace IM_API.Hubs { public class ChatHub:Hub { - private IJWTService _JWTService; - public ChatHub(IJWTService jWTService) + private IMessageSevice _messageService; + public ChatHub(IMessageSevice messageService) { - _JWTService = jWTService; + _messageService = messageService; } public async override Task OnConnectedAsync() { - var userIdStr = Context.User.FindFirstValue(ClaimTypes.NameIdentifier); - if(userIdStr is null) + if (!Context.User.Identity.IsAuthenticated) { await Clients.Caller.SendAsync("ReceiveMessage",new BaseResponse(CodeDefine.AUTH_FAILED)); Context.Abort(); return; } - int userId = int.Parse(userIdStr); await base.OnConnectedAsync(); } - public async Task SendMessage(MessageBaseDto dto) + public async Task SendPrivateMessage(MessageBaseDto dto) { - - await Clients.Caller.SendAsync("ReceiveMessage", "qwfqwfqw","test"); + if (!Context.User.Identity.IsAuthenticated) + { + await Clients.Caller.SendAsync("ReceiveMessage", new BaseResponse(CodeDefine.AUTH_FAILED)); + Context.Abort(); + return; + } + var userIdStr = Context.User.FindFirstValue(ClaimTypes.NameIdentifier); + await _messageService.SendPrivateMessageAsync(int.Parse(userIdStr),dto.ReceiverId,dto); + await Clients.Caller.SendAsync("ReceiveMessage",userIdStr,"qfqwfqwfqw"); + await Clients.Users(dto.ReceiverId.ToString()).SendAsync("ReceiveMessage", userIdStr, dto.Content); + return; } } } diff --git a/backend/IM_API/Models/ImContext.Custom.cs b/backend/IM_API/Models/ImContext.Custom.cs index dc91a0e..93d9546 100644 --- a/backend/IM_API/Models/ImContext.Custom.cs +++ b/backend/IM_API/Models/ImContext.Custom.cs @@ -73,6 +73,7 @@ namespace IM_API.Models { entity.Ignore(e => e.StateEnum); entity.Ignore(e => e.MsgTypeEnum); + entity.Ignore(e => e.ChatTypeEnum); }); modelBuilder.Entity(entity => diff --git a/backend/IM_API/Services/MessageService.cs b/backend/IM_API/Services/MessageService.cs new file mode 100644 index 0000000..d3e8b16 --- /dev/null +++ b/backend/IM_API/Services/MessageService.cs @@ -0,0 +1,74 @@ +using AutoMapper; +using IM_API.Dtos; +using IM_API.Exceptions; +using IM_API.Interface.Services; +using IM_API.Models; +using IM_API.Tools; +using Microsoft.EntityFrameworkCore; + +namespace IM_API.Services +{ + public class MessageService : IMessageSevice + { + private readonly ImContext _context; + private readonly ILogger _logger; + private readonly IMapper _mapper; + public MessageService(ImContext context, ILogger logger, IMapper mapper) + { + _context = context; + _logger = logger; + _mapper = mapper; + } + + public Task> GetGroupMessagesAsync(int groupId, int page, int pageSize, bool desc) + { + throw new NotImplementedException(); + } + + public Task> GetPrivateMessagesAsync(int userAId, int userBId, int page, int pageSize, bool desc) + { + throw new NotImplementedException(); + } + + public Task GetUnreadCountAsync(int userId) + { + throw new NotImplementedException(); + } + + public Task> GetUnreadMessagesAsync(int userId) + { + throw new NotImplementedException(); + } + + public Task MarkAsReadAsync(int userId, long messageId) + { + throw new NotImplementedException(); + } + + public Task MarkConversationAsReadAsync(int userId, int? userBId, int? groupId) + { + throw new NotImplementedException(); + } + + public Task RecallMessageAsync(int userId, int messageId) + { + throw new NotImplementedException(); + } + + public Task SendGroupMessageAsync(int senderId, int groupId, MessageBaseDto dto) + { + throw new NotImplementedException(); + } + + public async Task SendPrivateMessageAsync(int senderId, int receiverId, MessageBaseDto dto) + { + bool isExist = await _context.Friends.AnyAsync(x => x.FriendId == receiverId); + if (!isExist) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND); + var message = _mapper.Map(dto); + message.Sender = senderId; + _context.Messages.Add(message); + await _context.SaveChangesAsync(); + return true; + } + } +} diff --git a/backend/IM_API/appsettings.json b/backend/IM_API/appsettings.json index 0ea93f2..9da907c 100644 --- a/backend/IM_API/appsettings.json +++ b/backend/IM_API/appsettings.json @@ -10,7 +10,7 @@ "Key": "YourSuperSecretKey123456784124214190!", "Issuer": "IMDemo", "Audience": "IMClients", - "AccessTokenMinutes": 15, + "AccessTokenMinutes": 30, "RefreshTokenDays": 30 }, "ConnectionStrings": { diff --git a/frontend/web/package-lock.json b/frontend/web/package-lock.json index 8837169..58ee184 100644 --- a/frontend/web/package-lock.json +++ b/frontend/web/package-lock.json @@ -8,6 +8,7 @@ "name": "web", "version": "0.0.0", "dependencies": { + "feather-icons": "^4.29.2", "pinia": "^3.0.3", "vue": "^3.5.22", "vue-router": "^4.5.1" @@ -2826,6 +2827,12 @@ "node": ">= 16" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2896,6 +2903,17 @@ "url": "https://github.com/sponsors/mesqueeb" } }, + "node_modules/core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3600,6 +3618,16 @@ "reusify": "^1.0.4" } }, + "node_modules/feather-icons": { + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/feather-icons/-/feather-icons-4.29.2.tgz", + "integrity": "sha512-0TaCFTnBTVCz6U+baY2UJNKne5ifGh7sMG4ZC2LoBWCZdIyPa+y6UiR4lEYGws1JOFWdee8KAsAIvu0VcXqiqA==", + "license": "MIT", + "dependencies": { + "classnames": "^2.2.5", + "core-js": "^3.1.3" + } + }, "node_modules/figures": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", @@ -3740,9 +3768,9 @@ } }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -4125,9 +4153,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -5587,9 +5615,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.1.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", - "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", + "version": "7.2.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz", + "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/frontend/web/package.json b/frontend/web/package.json index 65de14c..7343fe9 100644 --- a/frontend/web/package.json +++ b/frontend/web/package.json @@ -15,6 +15,7 @@ "format": "prettier --write src/" }, "dependencies": { + "feather-icons": "^4.29.2", "pinia": "^3.0.3", "vue": "^3.5.22", "vue-router": "^4.5.1" diff --git a/frontend/web/src/App.vue b/frontend/web/src/App.vue index 4550d52..7b3d726 100644 --- a/frontend/web/src/App.vue +++ b/frontend/web/src/App.vue @@ -2,9 +2,13 @@ @@ -12,4 +16,17 @@ import WindowLayout from './components/Window.vue' - + diff --git a/frontend/web/src/components/MyInput.vue b/frontend/web/src/components/MyInput.vue new file mode 100644 index 0000000..1e4d5af --- /dev/null +++ b/frontend/web/src/components/MyInput.vue @@ -0,0 +1,155 @@ + + + + + \ No newline at end of file diff --git a/frontend/web/src/components/Window.vue b/frontend/web/src/components/Window.vue index f8678e5..2fee5bf 100644 --- a/frontend/web/src/components/Window.vue +++ b/frontend/web/src/components/Window.vue @@ -1,155 +1,166 @@ + \ No newline at end of file diff --git a/frontend/web/src/router/index.js b/frontend/web/src/router/index.js index 7fef57a..d108158 100644 --- a/frontend/web/src/router/index.js +++ b/frontend/web/src/router/index.js @@ -1,10 +1,12 @@ import { createRouter, createWebHistory } from 'vue-router' import MainView from '@/views/Main.vue' +import TestView from '@/views/Test.vue' const routes = [ { path: '/auth/login', component: () => import('@/views/auth/Login.vue') }, { path: '/', component: MainView }, { path: '/index', component: MainView }, + { path: '/test', component: TestView }, ] const router = createRouter({ diff --git a/frontend/web/src/views/Test.vue b/frontend/web/src/views/Test.vue new file mode 100644 index 0000000..6745c7f --- /dev/null +++ b/frontend/web/src/views/Test.vue @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/frontend/web/src/views/auth/Login.vue b/frontend/web/src/views/auth/Login.vue index b2c7d98..e6c0022 100644 --- a/frontend/web/src/views/auth/Login.vue +++ b/frontend/web/src/views/auth/Login.vue @@ -1,454 +1,292 @@ +.register-hint a { + color: #4f46e5; + font-weight: 600; + text-decoration: none; +} + +/* 响应式调整 */ +@media (max-width: 768px) { + .login-layout { + flex-direction: column; + } + .side-visual { + width: 100%; + height: 160px; + padding: 24px; + } + .hero-title { font-size: 28px; } + .visual-footer { display: none; } + .side-form { flex: 1; padding: 24px; } +} + \ No newline at end of file