diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfo.cs b/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfo.cs index 568afa5..cf44fd0 100644 --- a/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfo.cs +++ b/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfo.cs @@ -14,11 +14,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("IMTest")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -<<<<<<< HEAD -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+eb8455e141ea496a2134ad7c7d9b759b6029dd75")] -======= -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f7223dc5904286fdf8e117b56c37f58dc431d68b")] ->>>>>>> eb8455e141ea496a2134ad7c7d9b759b6029dd75 +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f7772a9c5a6c1a00fd55a667dd7644fe3debe9d7")] [assembly: System.Reflection.AssemblyProductAttribute("IMTest")] [assembly: System.Reflection.AssemblyTitleAttribute("IMTest")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfoInputs.cache b/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfoInputs.cache index c1ab4c0..a4adafc 100644 --- a/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfoInputs.cache +++ b/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfoInputs.cache @@ -1,5 +1 @@ -<<<<<<< HEAD -546570633bb9288fc2957cbb29a807a45f3e48ba127ec13bc3956d28f5e6ed5b -======= -3c0a5335719f892c65744a9df7eae9fd7b12de9067131f5c9f4cb65f2b27b8d4 ->>>>>>> eb8455e141ea496a2134ad7c7d9b759b6029dd75 +c6e01bb72d85599aa024524b725bc3ddb7e3c1cc42c37b8b46561df20748cbf5 diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.AssemblyReference.cache b/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.AssemblyReference.cache index 56fb82e..03566ff 100644 Binary files a/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.AssemblyReference.cache and b/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.AssemblyReference.cache differ diff --git a/backend/IM_API/.idea/.idea.IM_API/.idea/.gitignore b/backend/IM_API/.idea/.idea.IM_API/.idea/.gitignore new file mode 100644 index 0000000..01c334b --- /dev/null +++ b/backend/IM_API/.idea/.idea.IM_API/.idea/.gitignore @@ -0,0 +1,15 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/projectSettingsUpdater.xml +/.idea.IM_API.iml +/contentModel.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/backend/IM_API/.idea/.idea.IM_API/.idea/encodings.xml b/backend/IM_API/.idea/.idea.IM_API/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/backend/IM_API/.idea/.idea.IM_API/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/backend/IM_API/.idea/.idea.IM_API/.idea/indexLayout.xml b/backend/IM_API/.idea/.idea.IM_API/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/backend/IM_API/.idea/.idea.IM_API/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/backend/IM_API/.idea/.idea.IM_API/.idea/vcs.xml b/backend/IM_API/.idea/.idea.IM_API/.idea/vcs.xml new file mode 100644 index 0000000..b2bdec2 --- /dev/null +++ b/backend/IM_API/.idea/.idea.IM_API/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/backend/IM_API/Application/EventHandlers/GroupInviteActionUpdateHandler/RequestDbHandler.cs b/backend/IM_API/Application/EventHandlers/GroupInviteActionUpdateHandler/RequestDbHandler.cs index c7f2321..eec57dd 100644 --- a/backend/IM_API/Application/EventHandlers/GroupInviteActionUpdateHandler/RequestDbHandler.cs +++ b/backend/IM_API/Application/EventHandlers/GroupInviteActionUpdateHandler/RequestDbHandler.cs @@ -15,7 +15,7 @@ namespace IM_API.Application.EventHandlers.GroupInviteActionUpdateHandler public async Task Consume(ConsumeContext context) { var @event = context.Message; - if(@event.Action == Models.GroupRequestState.Passed) + if(@event.Action == Models.GroupRequestState.TargetPassed) { await _groupService.MakeGroupRequestAsync(@event.UserId, @event.InviteUserId,@event.GroupId); } diff --git a/backend/IM_API/IM_API.sln.DotSettings.user b/backend/IM_API/IM_API.sln.DotSettings.user new file mode 100644 index 0000000..3802d92 --- /dev/null +++ b/backend/IM_API/IM_API.sln.DotSettings.user @@ -0,0 +1,4 @@ + + True + /home/nanxun/dotnet/dotnet + /home/nanxun/dotnet/sdk/10.0.201/MSBuild.dll \ No newline at end of file diff --git a/backend/IM_API/Models/GroupRequestState.cs b/backend/IM_API/Models/GroupRequestState.cs index 31a389d..89cef21 100644 --- a/backend/IM_API/Models/GroupRequestState.cs +++ b/backend/IM_API/Models/GroupRequestState.cs @@ -21,6 +21,10 @@ /// /// 对方拒绝 /// - TargetDeclined = 4 + TargetDeclined = 4, + /// + /// 对方同意 + /// + TargetPassed = 5 } } diff --git a/backend/IM_API/Services/GroupService.cs b/backend/IM_API/Services/GroupService.cs index d83665a..ff89ac7 100644 --- a/backend/IM_API/Services/GroupService.cs +++ b/backend/IM_API/Services/GroupService.cs @@ -167,7 +167,7 @@ namespace IM_API.Services var inviteInfo = await _context.GroupRequests .FirstOrDefaultAsync(x => x.UserId == userid && x.StateEnum == GroupRequestState.TargetPending) ?? throw new BaseException(CodeDefine.INVALID_ACTION); - if (!(dto.Action == GroupRequestState.TargetPending || + if (!(dto.Action == GroupRequestState.TargetPassed || dto.Action == GroupRequestState.TargetDeclined)) return; inviteInfo.StateEnum = dto.Action; diff --git a/frontend/app/android/settings.gradle.kts b/frontend/app/android/settings.gradle.kts index 2507983..f3cfa80 100644 --- a/frontend/app/android/settings.gradle.kts +++ b/frontend/app/android/settings.gradle.kts @@ -10,6 +10,9 @@ pluginManagement { includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") repositories { + // 👇 添加这一行,用于下载 Flutter 插件相关的依赖 + maven { url = uri("https://storage.flutter-io.cn/download.flutter.io") } + maven { url = uri("https://maven.aliyun.com/repository/google") } maven { url = uri("https://maven.aliyun.com/repository/public") } gradlePluginPortal() @@ -21,6 +24,9 @@ pluginManagement { dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) repositories { + // 👇 添加这一行,这是解决你报错的核心!强制项目从这里下载 Flutter 引擎 + maven { url = uri("https://storage.flutter-io.cn/download.flutter.io") } + maven { url = uri("https://maven.aliyun.com/repository/google") } maven { url = uri("https://maven.aliyun.com/repository/public") } gradlePluginPortal() @@ -31,8 +37,10 @@ dependencyResolutionManagement { plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" - id("com.android.application") version "8.1.1" apply false - id("org.jetbrains.kotlin.android") version "1.8.22" apply false + // 👇 升级到 8.2.1 修复 Java 21 兼容性 bug + id("com.android.application") version "8.2.1" apply false + // 👇 顺便把 Kotlin 版本也稍微升一下,避免后续出现旧版本警告 + id("org.jetbrains.kotlin.android") version "1.9.22" apply false } include(":app") \ No newline at end of file diff --git a/frontend/app/lib/core/network/auth_interceptor.dart b/frontend/app/lib/core/network/auth_interceptor.dart new file mode 100644 index 0000000..5cf8b08 --- /dev/null +++ b/frontend/app/lib/core/network/auth_interceptor.dart @@ -0,0 +1,9 @@ +import 'package:dio/dio.dart'; + +class AuthInterceptor extends Interceptor{ + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + // TODO: implement onRequest + super.onRequest(options, handler); + } +} \ No newline at end of file diff --git a/frontend/app/lib/core/network/request.dart b/frontend/app/lib/core/network/request.dart new file mode 100644 index 0000000..3c3e5fd --- /dev/null +++ b/frontend/app/lib/core/network/request.dart @@ -0,0 +1,21 @@ +import 'package:app/core/network/auth_interceptor.dart'; +import 'package:dio/dio.dart'; + +class Request { + static final Request _instance = Request._internal(); + factory Request() => _instance; + + late Dio dio; + + Request._internal(){ + BaseOptions options = BaseOptions( + baseUrl: "", + responseType: ResponseType.json, + contentType: 'application/json; charset=utf-8', + ); + dio = Dio(options); + dio.interceptors.addAll([ + AuthInterceptor() + ]); + } +} \ No newline at end of file diff --git a/frontend/app/lib/core/router/app_router.dart b/frontend/app/lib/core/router/app_router.dart index 1e8e01e..538751c 100644 --- a/frontend/app/lib/core/router/app_router.dart +++ b/frontend/app/lib/core/router/app_router.dart @@ -1,6 +1,7 @@ import 'package:app/features/auth/pages/login_page.dart'; +import 'package:app/features/auth/pages/register_page.dart'; import 'package:app/features/home/pages/index_page.dart'; import 'package:go_router/go_router.dart'; @@ -10,6 +11,7 @@ final appRouter = GoRouter( initialLocation: '/auth/login', routes: [ GoRoute(path: '/auth/login', builder: (context, state) => const LoginPage()), + GoRoute(path: '/auth/register', builder: (context, state) => const RegisterPage()), ShellRoute( builder: (context, state, child) { return MainPage(child: child); diff --git a/frontend/app/lib/features/auth/bloc/login_page_state.dart b/frontend/app/lib/features/auth/bloc/login_page_state.dart index 7a410df..1a90032 100644 --- a/frontend/app/lib/features/auth/bloc/login_page_state.dart +++ b/frontend/app/lib/features/auth/bloc/login_page_state.dart @@ -1,6 +1,7 @@ import 'package:app/core/constants/app_colors.dart'; import 'package:app/features/auth/pages/login_page.dart'; import 'package:flutter/material.dart'; +import 'package:app/core/router/app_router.dart'; class LoginPageState extends State { @override @@ -11,10 +12,10 @@ class LoginPageState extends State { backgroundColor: Colors.transparent, elevation: 0, actions: [ - TextButton( - onPressed: () {}, - child: const Text("找回密码", style: TextStyle(color: Colors.grey)), - ), + // TextButton( + // onPressed: () {}, + // child: const Text("找回密码", style: TextStyle(color: Colors.grey)), + // ), ], ), body: SingleChildScrollView( @@ -24,23 +25,9 @@ class LoginPageState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(height: 20), - // 1. 品牌Logo/头像区域 - Container( - width: 100, - height: 100, - decoration: BoxDecoration( - color: AppColors.primaryColor.withOpacity(0.1), - shape: BoxShape.circle, - ), - child: const Icon( - Icons.person_rounded, - size: 60, - color: AppColors.primaryColor, - ), - ), const SizedBox(height: 24), const Text( - "登录您的聊天账号", + "登陆账号", style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, @@ -109,7 +96,9 @@ class LoginPageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( - onPressed: () {}, + onPressed: () { + appRouter.push("/auth/register"); + }, child: const Text( "注册账号", style: TextStyle(color: Color(0xFF576B95)), @@ -126,6 +115,13 @@ class LoginPageState extends State { style: TextStyle(color: Color(0xFF576B95)), ), ), + TextButton( + onPressed: () {}, + child: const Text( + "找回密码", + style: TextStyle(color: Color(0xFF576B95)), + ), + ), ], ), diff --git a/frontend/app/lib/features/auth/bloc/register_page_state.dart b/frontend/app/lib/features/auth/bloc/register_page_state.dart new file mode 100644 index 0000000..65439cc --- /dev/null +++ b/frontend/app/lib/features/auth/bloc/register_page_state.dart @@ -0,0 +1,156 @@ +import 'package:app/features/auth/pages/register_page.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import '../../../core/constants/app_colors.dart'; + +class RegisterPageState extends State { + @override + Widget build(BuildContext context) { + // TODO: implement build + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + actions: [ + // TextButton( + // onPressed: () {}, + // child: const Text("找回密码", style: TextStyle(color: Colors.grey)), + // ), + ], + ), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 20), + const SizedBox(height: 24), + const Text( + "注册账号", + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + const SizedBox(height: 60), + + // 2. 账号输入框 (极简下划线风格) + TextField( + decoration: InputDecoration( + labelText: "用户名", + labelStyle: const TextStyle(color: Colors.grey, fontSize: 14), + floatingLabelStyle: const TextStyle(color: AppColors.primaryColor), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.grey.shade300), + ), + focusedBorder: const UnderlineInputBorder( + borderSide: BorderSide(color: AppColors.primaryColor, width: 2), + ), + ), + ), + const SizedBox(height: 25), + + // 3. 密码输入框 + TextField( + obscureText: true, + decoration: InputDecoration( + labelText: "请输入密码", + labelStyle: const TextStyle(color: Colors.grey, fontSize: 14), + floatingLabelStyle: const TextStyle(color: AppColors.primaryColor), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.grey.shade300), + ), + focusedBorder: const UnderlineInputBorder( + borderSide: BorderSide(color: AppColors.primaryColor, width: 2), + ), + ), + ), + const SizedBox(height: 60), + + // 4. 登录按钮 (圆润大按钮) + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: () {}, + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.primaryColor, + foregroundColor: Colors.white, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: const Text( + "注 册", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500), + ), + ), + ), + const SizedBox(height: 20), + + // 5. 注册/切换登录方式 + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + onPressed: () {}, + child: const Text( + "已有账号?点我登录", + style: TextStyle(color: Color(0xFF576B95)), + ), // 经典的链接蓝 + ), + const SizedBox( + height: 20, + child: VerticalDivider(color: Colors.grey), + ), + TextButton( + onPressed: () {}, + child: const Text( + "找回密码", + style: TextStyle(color: Color(0xFF576B95)), + ), + ), + ], + ), + + const SizedBox(height: 80), + // 6. 底部协议 (社交App必有) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Checkbox( + value: true, + activeColor: AppColors.primaryColor, + onChanged: (v) {}, + ), + const Text( + "我已阅读并同意", + style: TextStyle(color: Colors.grey, fontSize: 12), + ), + const Text( + "《用户协议》", + style: TextStyle(color: Color(0xFF576B95), fontSize: 12), + ), + const Text( + "与", + style: TextStyle(color: Colors.grey, fontSize: 12), + ), + const Text( + "《隐私政策》", + style: TextStyle(color: Color(0xFF576B95), fontSize: 12), + ), + ], + ), + ], + ), + ), + ), + ); + } + +} \ No newline at end of file diff --git a/frontend/app/lib/features/auth/pages/register_page.dart b/frontend/app/lib/features/auth/pages/register_page.dart new file mode 100644 index 0000000..a923907 --- /dev/null +++ b/frontend/app/lib/features/auth/pages/register_page.dart @@ -0,0 +1,12 @@ +import 'package:app/features/auth/bloc/register_page_state.dart'; +import 'package:flutter/cupertino.dart'; + +class RegisterPage extends StatefulWidget { + const RegisterPage({super.key}); + @override + State createState() { + // TODO: implement createState + return RegisterPageState(); + } + +} \ No newline at end of file diff --git a/frontend/app/pubspec.lock b/frontend/app/pubspec.lock index 7d348ac..e0a0e52 100644 --- a/frontend/app/pubspec.lock +++ b/frontend/app/pubspec.lock @@ -5,16 +5,16 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.dev" + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.flutter-io.cn" source: hosted - version: "2.13.0" + version: "2.13.1" boolean_selector: dependency: transitive description: name: boolean_selector sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.2" characters: @@ -22,7 +22,7 @@ packages: description: name: characters sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.1" clock: @@ -30,7 +30,7 @@ packages: description: name: clock sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.2" collection: @@ -38,23 +38,39 @@ packages: description: name: collection sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.19.1" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.8" + version: "1.0.9" + dio: + dependency: "direct main" + description: + name: dio + sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.9.2" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.2" fake_async: dependency: transitive description: name: fake_async sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.3" flutter: @@ -67,7 +83,7 @@ packages: description: name: flutter_lints sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.0.0" flutter_test: @@ -85,15 +101,23 @@ packages: description: name: go_router sha256: "7974313e217a7771557add6ff2238acb63f635317c35fa590d348fb238f00896" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "17.1.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.1.2" leak_tracker: dependency: transitive description: name: leak_tracker sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "11.0.2" leak_tracker_flutter_testing: @@ -101,7 +125,7 @@ packages: description: name: leak_tracker_flutter_testing sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.10" leak_tracker_testing: @@ -109,7 +133,7 @@ packages: description: name: leak_tracker_testing sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.2" lints: @@ -117,7 +141,7 @@ packages: description: name: lints sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.1.0" logging: @@ -125,7 +149,7 @@ packages: description: name: logging sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.0" matcher: @@ -133,7 +157,7 @@ packages: description: name: matcher sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.12.18" material_color_utilities: @@ -141,7 +165,7 @@ packages: description: name: material_color_utilities sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.13.0" meta: @@ -149,15 +173,23 @@ packages: description: name: meta sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.17.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.0" path: dependency: transitive description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.9.1" sky_engine: @@ -170,7 +202,7 @@ packages: description: name: source_span sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.10.2" stack_trace: @@ -178,7 +210,7 @@ packages: description: name: stack_trace sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.12.1" stream_channel: @@ -186,7 +218,7 @@ packages: description: name: stream_channel sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.4" string_scanner: @@ -194,7 +226,7 @@ packages: description: name: string_scanner sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.1" term_glyph: @@ -202,7 +234,7 @@ packages: description: name: term_glyph sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.2" test_api: @@ -210,15 +242,23 @@ packages: description: name: test_api sha256: "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.7.8" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.0" vector_math: dependency: transitive description: name: vector_math sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" vm_service: @@ -226,9 +266,17 @@ packages: description: name: vm_service sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "15.0.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" sdks: dart: ">=3.10.4 <4.0.0" flutter: ">=3.35.0" diff --git a/frontend/app/pubspec.yaml b/frontend/app/pubspec.yaml index f953d81..da559f8 100644 --- a/frontend/app/pubspec.yaml +++ b/frontend/app/pubspec.yaml @@ -38,6 +38,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 go_router: ^17.0.1 + dio: ^5.9.2 dev_dependencies: flutter_test: diff --git a/frontend/pc/IM/.vscode/launch.json b/frontend/pc/IM/.vscode/launch.json index 0b6b9a6..f7a071a 100644 --- a/frontend/pc/IM/.vscode/launch.json +++ b/frontend/pc/IM/.vscode/launch.json @@ -11,6 +11,7 @@ "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" }, "runtimeArgs": ["--sourcemap"], + "console": "integratedTerminal", "env": { "REMOTE_DEBUGGING_PORT": "9222" } diff --git a/frontend/pc/IM/.vscode/settings.json b/frontend/pc/IM/.vscode/settings.json index 4c05394..e879dfd 100644 --- a/frontend/pc/IM/.vscode/settings.json +++ b/frontend/pc/IM/.vscode/settings.json @@ -3,7 +3,7 @@ "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "vscode.typescript-language-features" }, "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" diff --git a/frontend/pc/IM/electron.vite.config.mjs b/frontend/pc/IM/electron.vite.config.mjs index 444f622..f16f179 100644 --- a/frontend/pc/IM/electron.vite.config.mjs +++ b/frontend/pc/IM/electron.vite.config.mjs @@ -7,6 +7,9 @@ export default defineConfig({ main: {}, preload: {}, renderer: { + server: { + host: true + }, resolve: { alias: { '@': resolve('src/renderer/src') diff --git a/frontend/pc/IM/src/renderer/src/components/Dropdown.vue b/frontend/pc/IM/src/renderer/src/components/Dropdown.vue new file mode 100644 index 0000000..71552d6 --- /dev/null +++ b/frontend/pc/IM/src/renderer/src/components/Dropdown.vue @@ -0,0 +1,252 @@ + + + + + diff --git a/frontend/pc/IM/src/renderer/src/components/groups/CreateGroup.vue b/frontend/pc/IM/src/renderer/src/components/groups/CreateGroup.vue index 0ae98f1..b02680b 100644 --- a/frontend/pc/IM/src/renderer/src/components/groups/CreateGroup.vue +++ b/frontend/pc/IM/src/renderer/src/components/groups/CreateGroup.vue @@ -4,6 +4,7 @@ import { friendService } from '../../services/friend'; import { groupService } from '@/services/group'; import { SYSTEM_BASE_STATUS } from '@/constants/systemBaseStatus'; import { useMessage } from '../messages/useAlert'; +import AsyncImage from '../AsyncImage.vue'; const message = useMessage(); @@ -62,7 +63,7 @@ onMounted(async () =>{
- + {{ f.remarkName }}
@@ -111,7 +112,7 @@ main { padding: 12px; } } .item:hover { background: #f5f5f5; } -.avatar { width: 32px; height: 32px; border-radius: 4px; margin-right: 10px; } +:deep(.avatar) { width: 32px; height: 32px; border-radius: 4px; margin-right: 10px; } .name { flex: 1; font-size: 14px; } footer { padding: 12px; } diff --git a/frontend/pc/IM/src/renderer/src/components/messages/InfoSidebar.vue b/frontend/pc/IM/src/renderer/src/components/messages/InfoSidebar.vue index 82b7dc6..1e8bb47 100644 --- a/frontend/pc/IM/src/renderer/src/components/messages/InfoSidebar.vue +++ b/frontend/pc/IM/src/renderer/src/components/messages/InfoSidebar.vue @@ -43,7 +43,7 @@ class="member-item" >
- +
{{ member.nickname }} @@ -85,6 +85,7 @@ import { SYSTEM_BASE_STATUS } from '../../constants/systemBaseStatus'; import { useMessage } from './useAlert'; import { getFileHash } from '../../utils/uploadTools'; import CreateGroup from '../groups/CreateGroup.vue'; +import AsyncImage from '../AsyncImage.vue'; const props = defineProps({ chatType: { @@ -339,7 +340,7 @@ onMounted(async () => { height: 48px; } -.member-img { +:deep(.member-img) { width: 100%; height: 100%; border-radius: 12px; diff --git a/frontend/pc/IM/src/renderer/src/constants/GroupDefine.js b/frontend/pc/IM/src/renderer/src/constants/GroupDefine.js index 3e91b0d..1db830d 100644 --- a/frontend/pc/IM/src/renderer/src/constants/GroupDefine.js +++ b/frontend/pc/IM/src/renderer/src/constants/GroupDefine.js @@ -18,7 +18,12 @@ export const GROUP_REQUEST_STATUS = Object.freeze({ TARGET_PENDING: 'TargetPending', /** 对方拒绝 */ TARGET_DECLINED: 'TargetDeclined' -}); +}) + +export const GROUP_REQUEST_ACTION = Object.freeze({ + ACCEPT: 'Accept', + REJECT: 'Reject' +}) export const getGroupRequestStatusTxt = (status) => { @@ -36,4 +41,5 @@ export const getGroupRequestStatusTxt = (status) => { default: return '未知状态'; } -}; +} + diff --git a/frontend/pc/IM/src/renderer/src/handler/messageHandler.js b/frontend/pc/IM/src/renderer/src/handler/messageHandler.js index f7422db..3798772 100644 --- a/frontend/pc/IM/src/renderer/src/handler/messageHandler.js +++ b/frontend/pc/IM/src/renderer/src/handler/messageHandler.js @@ -3,10 +3,23 @@ import { MESSAGE_TYPE } from "../constants/MessageType"; export const messageHandler = (msg) => { const conversationStore = useConversationStore(); - const conversation = conversationStore.conversations.find(x => - ((x.targetId == msg.senderId || x.targetId == msg.receiverId) && msg.chatType == MESSAGE_TYPE.PRIVATE) || - (x.targetId == msg.receiverId && msg.chatType == MESSAGE_TYPE.GROUP) - ); + const conversation = conversationStore.conversations.find(x => { + // 1. 如果是私聊:目标 ID 必须是对方(可能是发送者,也可能是接收者) + if (msg.chatType === MESSAGE_TYPE.PRIVATE) { + return x.chatType === MESSAGE_TYPE.PRIVATE && + (x.targetId === msg.senderId || x.targetId === msg.receiverId); + } + + // 2. 如果是群聊:目标 ID 必须是群 ID(即消息的 receiverId) + if (msg.chatType === MESSAGE_TYPE.GROUP) { + return x.chatType === MESSAGE_TYPE.GROUP && + x.targetId === msg.receiverId; + } + + return false; + }); + + if (!conversation) return; // 容错处理:如果没找到会话,不执行后续逻辑 conversation.lastMessage = msg.content; if (conversation.targetId == msg.receiverId) { conversation.unreadCount = 0; diff --git a/frontend/pc/IM/src/renderer/src/services/group.js b/frontend/pc/IM/src/renderer/src/services/group.js index 471d884..f407716 100644 --- a/frontend/pc/IM/src/renderer/src/services/group.js +++ b/frontend/pc/IM/src/renderer/src/services/group.js @@ -42,5 +42,21 @@ export const groupService = { * 获取群聊通知 * @returns */ - getGroupNotification: () => request.get('/Group/GetGroupNotification') + getGroupNotification: () => request.get('/Group/GetGroupNotification'), + /** + * 处理入群邀请 + * @param {*} inviteId + * @param {*} action + * @returns + */ + handleGroupInvite: (inviteId, action) => + request.post('/Group/HandleGroupInvite', { inviteId: inviteId, action: action }), + /** + * 处理入群请求 + * @param {*} requestId + * @param {*} action + * @returns + */ + handleGroupRequest: (requestId, action) => + request.post('/Group/HandleGroupRequest', { requestId: requestId, action: action }) } diff --git a/frontend/pc/IM/src/renderer/src/stores/chat.js b/frontend/pc/IM/src/renderer/src/stores/chat.js index 67a53ec..30e015a 100644 --- a/frontend/pc/IM/src/renderer/src/stores/chat.js +++ b/frontend/pc/IM/src/renderer/src/stores/chat.js @@ -50,7 +50,6 @@ export const useChatStore = defineStore('chat', { this.isEnded = false; //先从浏览器缓存加载一部分消息列表 const localHistory = await messagesDb.getLatestMessages(sessionId, this.pageSize); - console.log(localHistory) if (localHistory.length > 0) { this.messages = localHistory; this.maxSequenceId = this.messages.reduce((max, m) => diff --git a/frontend/pc/IM/src/renderer/src/utils/sessionIdTools.js b/frontend/pc/IM/src/renderer/src/utils/sessionIdTools.js index 57b9fde..48e5fc1 100644 --- a/frontend/pc/IM/src/renderer/src/utils/sessionIdTools.js +++ b/frontend/pc/IM/src/renderer/src/utils/sessionIdTools.js @@ -10,5 +10,5 @@ export const generateSessionId = (id1, id2, isGroup = false) => { if (isGroup) { return `g:${id2}`; } - return [String(id1), String(id2)].sort().join('_'); + return 'p:' + [String(id1), String(id2)].sort().join('_'); }; \ No newline at end of file diff --git a/frontend/pc/IM/src/renderer/src/views/Test.vue b/frontend/pc/IM/src/renderer/src/views/Test.vue index be34c8a..d0b6581 100644 --- a/frontend/pc/IM/src/renderer/src/views/Test.vue +++ b/frontend/pc/IM/src/renderer/src/views/Test.vue @@ -1,6 +1,7 @@ @@ -9,6 +10,7 @@ import { ref } from 'vue'; import { useCacheStore } from '../stores/cache'; import { FILE_TYPE } from '../constants/fileTypeDefine'; import AsyncImage from '../components/AsyncImage.vue'; +import Dropdown from '../components/Dropdown.vue'; const url = ref('') diff --git a/frontend/pc/IM/src/renderer/src/views/contact/GroupRequest.vue b/frontend/pc/IM/src/renderer/src/views/contact/GroupRequest.vue index 436906c..67fed8b 100644 --- a/frontend/pc/IM/src/renderer/src/views/contact/GroupRequest.vue +++ b/frontend/pc/IM/src/renderer/src/views/contact/GroupRequest.vue @@ -9,30 +9,25 @@
- + ? 'is-group' + : 'is-user' + ]" />
{{ item.name }} - + ? 'tag-orange' + : 'tag-green' + ]"> {{ getTypeText(item.type) }} 14:20 @@ -42,25 +37,23 @@ 目标群聊: {{ item.groupName }}
-
+
目标用户: {{ myInfo.id === item.userId ? item.inviteUserNickname : item.nickName - }} + }}

入群描述:{{ item.description }}

-
+
- +
@@ -78,18 +71,26 @@