From 54df3eb276091c74bfea9cf5fa4e5dc8506d8774 Mon Sep 17 00:00:00 2001 From: liyi Date: Wed, 3 Sep 2025 09:39:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=BB=E9=A1=B5=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 3 +- assets/icon/icon_one_key_door.png | Bin 0 -> 199 bytes assets/icon/icon_one_key_door_key.png | Bin 0 -> 462 bytes assets/images/bg_one_key_door.png | Bin 0 -> 10476 bytes assets/images/mockImage.jpg | Bin 0 -> 14799 bytes lib/app.dart | 22 +- lib/base/app_permission.dart | 26 ++ lib/common/constant/app_images.dart | 6 + lib/flavors.dart | 2 +- lib/main.dart | 16 +- lib/views/home/home_controller.dart | 30 ++ lib/views/home/home_view.dart | 224 ++++++++++---- .../home_attendance_chart_area_widget.dart | 243 +++++++++++++++ .../widget/home_carousel_area_widget.dart | 96 ++++++ .../home_function_list_area_widget.dart | 245 +++++++++++++++ .../home/widget/home_left_drawer_widget.dart | 280 ++++++++++++++++++ .../home_one_button_door_opening_widget.dart | 194 ++++++++++++ .../widget/home_statistics_row_widget.dart | 128 ++++++++ .../widget/home_team_notice_row_widget.dart | 101 +++++++ .../input_verification_code_controller.dart | 21 +- pubspec.lock | 8 + pubspec.yaml | 6 + 22 files changed, 1579 insertions(+), 72 deletions(-) create mode 100644 assets/icon/icon_one_key_door.png create mode 100644 assets/icon/icon_one_key_door_key.png create mode 100644 assets/images/bg_one_key_door.png create mode 100644 assets/images/mockImage.jpg create mode 100644 lib/common/constant/app_images.dart create mode 100644 lib/views/home/widget/home_attendance_chart_area_widget.dart create mode 100644 lib/views/home/widget/home_carousel_area_widget.dart create mode 100644 lib/views/home/widget/home_function_list_area_widget.dart create mode 100644 lib/views/home/widget/home_left_drawer_widget.dart create mode 100644 lib/views/home/widget/home_one_button_door_opening_widget.dart create mode 100644 lib/views/home/widget/home_statistics_row_widget.dart create mode 100644 lib/views/home/widget/home_team_notice_row_widget.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index dce4021..533f5bb 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -9,7 +9,8 @@ - + + z#yx)=D!l-8sR$~UjK~lPOVuqTf%Wic7NOMcgLQt sd-3;g;{5e(1v^@O8yFawSU56lw0kN#&mNI)0vgZY>FVdQ&MBb@02b9p6aWAK literal 0 HcmV?d00001 diff --git a/assets/icon/icon_one_key_door_key.png b/assets/icon/icon_one_key_door_key.png new file mode 100644 index 0000000000000000000000000000000000000000..0488296f60bacac370b4ee7d3fac683036d9abea GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^DnRVS!3-ohid)_SslWiA5LY0b{QPnD$9I$d|4(`T z7=n_YJx+P?B<0n!v=@)F-o6F0fogzKsn4IJ0Hr|aQOfg2KxIJj)EAF4oz}jYMCXU~iggBV{7O{hW7=0t>sDx9z!}(l;EC(s zuRG@bsCp=Kvq_&L_g>nn*U5pF>~pF^Gd#?l?|uFHEW~)f{Yt}oJy!1@-&tk&NyfEl yp8xg5nd_zcP6j;vf21$udQra5`P}6mKiDpLv`d|q+PMuFh76vrelF{r5}E*UgWyd7 literal 0 HcmV?d00001 diff --git a/assets/images/bg_one_key_door.png b/assets/images/bg_one_key_door.png new file mode 100644 index 0000000000000000000000000000000000000000..347e068be9465ab646b4f580d8d6dbcbcd5e45f0 GIT binary patch literal 10476 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 literal 0 HcmV?d00001 diff --git a/lib/app.dart b/lib/app.dart index e422ba3..d4f03dd 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -10,9 +10,16 @@ import 'package:starwork_flutter/i18n/app_i18n.dart'; import 'package:starwork_flutter/routes/app_pages.dart'; import 'package:starwork_flutter/routes/app_routes.dart'; -class App extends StatelessWidget { - const App({super.key}); +class App extends StatefulWidget { + App({super.key, required this.initialRoute}); + String initialRoute; + + @override + State createState() => _AppState(); +} + +class _AppState extends State { @override Widget build(BuildContext context) { return ScreenUtilInit( @@ -48,20 +55,11 @@ class App extends StatelessWidget { fallbackLocale: const Locale('zh', 'CN'), // 必须设置 fallback 没有该语言时使用 getPages: AppPages.pages, - initialRoute: _checkIsLogin(), + initialRoute: widget.initialRoute, debugShowCheckedModeBanner: false, builder: EasyLoading.init(), ); }, ); } - - String _checkIsLogin() { - var token = SharedPreferencesUtils.getString(CacheKeys.token); - if (token != null && token.isNotEmpty) { - return AppRoutes.main; - } else { - return AppRoutes.login; - } - } } diff --git a/lib/base/app_permission.dart b/lib/base/app_permission.dart index 57a990c..2d02f33 100644 --- a/lib/base/app_permission.dart +++ b/lib/base/app_permission.dart @@ -20,4 +20,30 @@ class AppPermission { } return true; } + + static Future requestPermission({ + required Permission permission, + }) async { + var status = await permission.request(); + return status == PermissionStatus.granted; + } + + static Future requestNotificationPermission() async { + final PermissionStatus status = await Permission.notification.request(); + + if (status == PermissionStatus.granted) { + print("通知权限已授予"); + return true; + } else if (status == PermissionStatus.denied) { + // 第一次被拒绝,可以再次请求 + return false; + } else if (status.isPermanentlyDenied) { + // 用户勾选了“不再提示”或被永久拒绝 + print("权限被永久拒绝,跳转到设置页面"); + openAppSettings(); // 跳转到应用设置页,手动开启 + return false; + } + + return false; + } } diff --git a/lib/common/constant/app_images.dart b/lib/common/constant/app_images.dart new file mode 100644 index 0000000..c464f2d --- /dev/null +++ b/lib/common/constant/app_images.dart @@ -0,0 +1,6 @@ +class AppImages{ + + static const String iconOneKeyDoor = 'assets/icon/icon_one_key_door.png'; + static const String iconOneKeyDoorKey = 'assets/icon/icon_one_key_door_key.png'; + static const String bgOneKeyDoor = 'assets/images/bg_one_key_door.png'; +} \ No newline at end of file diff --git a/lib/flavors.dart b/lib/flavors.dart index 99d932f..e64b804 100644 --- a/lib/flavors.dart +++ b/lib/flavors.dart @@ -32,7 +32,7 @@ class F { static String get apiHost { switch (appFlavor) { case Flavor.skyDev: - return 'http://192.168.1.136/api'; + return 'http://10.0.2.2/api'; case Flavor.skyPre: return 'https://loacl.work.star-lock.cn/api'; case Flavor.skyRelease: diff --git a/lib/main.dart b/lib/main.dart index 6bc3646..2606b1a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:starwork_flutter/base/app_initialization.dart'; +import 'package:starwork_flutter/common/constant/cache_keys.dart'; +import 'package:starwork_flutter/common/utils/shared_preferences_utils.dart'; +import 'package:starwork_flutter/routes/app_routes.dart'; import 'app.dart'; import 'flavors.dart'; @@ -14,5 +17,16 @@ void main() async { await AppInitialization.initializeApp(); - runApp(const App()); + var initRoute = await _handleInitialRoute(); + + runApp(App(initialRoute: initRoute)); +} + +Future _handleInitialRoute() async { + var token = await SharedPreferencesUtils.getString(CacheKeys.token); + if (token != null && token.isNotEmpty) { + return AppRoutes.main; + } else { + return AppRoutes.login; + } } diff --git a/lib/views/home/home_controller.dart b/lib/views/home/home_controller.dart index a7f7638..69c0622 100644 --- a/lib/views/home/home_controller.dart +++ b/lib/views/home/home_controller.dart @@ -1,3 +1,5 @@ +import 'package:carousel_slider/carousel_controller.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:starwork_flutter/base/app_permission.dart'; @@ -6,11 +8,39 @@ import 'package:starwork_flutter/base/base_controller.dart'; class HomeController extends BaseController { final isOpenNotificationPermission = false.obs; + var carouselCurrentIndex = 0.obs; + + // 渐变颜色列表 + final List gradientColors = [ + const Color(0xFFBFCBEF), // #bfcbef + const Color(0xFFECBE9B), // 原来的颜色 + const Color(0xFFD5F3D5), // 清新绿色 + const Color(0xFFFFB6C1), // 温柔粉色 + ]; + + // 当前渐变颜色 + var currentGradientColor = const Color(0xFFBFCBEF).obs; + @override void onInit() async { super.onInit(); isOpenNotificationPermission.value = await AppPermission.checkPermission( permission: Permission.notification, ); + + // 监听轮播图切换,更新渐变颜色 + carouselCurrentIndex.listen((index) { + updateGradientColor(index); + }); + } + + // 根据轮播图索引更新渐变颜色 + void updateGradientColor(int index) { + if (index < gradientColors.length) { + currentGradientColor.value = gradientColors[index]; + } else { + // 如果索引超出颜色数量,使用模运算轮环 + currentGradientColor.value = gradientColors[index % gradientColors.length]; + } } } diff --git a/lib/views/home/home_view.dart b/lib/views/home/home_view.dart index c43d8cb..e7e120b 100644 --- a/lib/views/home/home_view.dart +++ b/lib/views/home/home_view.dart @@ -1,86 +1,200 @@ +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; -import 'package:permission_handler/permission_handler.dart'; import 'package:starwork_flutter/base/app_permission.dart'; +import 'package:starwork_flutter/views/home/widget/home_attendance_chart_area_widget.dart'; +import 'package:starwork_flutter/views/home/widget/home_carousel_area_widget.dart'; +import 'package:starwork_flutter/views/home/widget/home_function_list_area_widget.dart'; +import 'package:starwork_flutter/views/home/widget/home_left_drawer_widget.dart'; +import 'package:starwork_flutter/views/home/widget/home_one_button_door_opening_widget.dart'; +import 'package:starwork_flutter/views/home/widget/home_statistics_row_widget.dart'; +import 'package:starwork_flutter/views/home/widget/home_team_notice_row_widget.dart'; import 'home_controller.dart'; class HomeView extends GetView { const HomeView({super.key}); + static final GlobalKey _scaffoldKey = + GlobalKey(); + @override Widget build(BuildContext context) { - return Scaffold( - body: SafeArea( - child: Container( - width: 1.sw, - padding: EdgeInsets.symmetric(horizontal: 15.w), - child: Column( - children: [ - _buildPageHead(), - _buildSystemNotificationPermissionRow(), + return Obx( + () => Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, // 渐变起点:顶部中心 + end: Alignment.bottomCenter, // 渐变终点:底部中心 + colors: [ + controller.currentGradientColor.value, // 动态颜色 + const Color(0xFFF6F7FB), // 底部颜色保持不变 ], + stops: const [0.1, 1.0], // 第一个颜色到10%,然后第二个颜色开始直到100% + ), + ), + child: Scaffold( + key: _scaffoldKey, + backgroundColor: Colors.transparent, + body: SafeArea( + child: Container( + width: 1.sw, + padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 4.h), + child: Column( + children: [ + // 固定的上半部分 + _buildPageHead(context), + SizedBox( + height: 10.h, + ), + _buildSystemNotificationPermissionRow(), + + // 可滚动的下半部分 + Expanded( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + HomeCarouselAreaWidget( + carouselCurrentIndex: + controller.carouselCurrentIndex, + ), + HomeTeamNoticeRowWidget(), + HomeStatisticsRowWidget( + personCount: 12, + deviceCount: 1, + ), + SizedBox(height: 10.h), + HomeFunctionListAreaWidget(), + SizedBox( + height: 10.h, + ), + HomeOnButtonDoorOpeningWidget( + doorList: const [ + '主门门禁', + '车库门禁', + '后门门禁', + '单元门门禁', + ], + ), + SizedBox( + height: 10.h, + ), + HomeAttendanceChartAreaWidget(), + SizedBox( + height: 10.h, + ), + ], + ), + ), + ), + ], + ), + ), + ), + drawer: HomeLeftDrawerWidget( + teamList: ['家庭群组', '测试团队1', '测试团队2'], ), ), ), ); } - _buildPageHead() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '19104656的互联', - style: TextStyle( - fontSize: 18.sp, - fontWeight: FontWeight.w500, + _buildPageHead(BuildContext context) { + return GestureDetector( + onTap: () { + _scaffoldKey.currentState?.openDrawer(); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '19104656的互联', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w500, + ), ), - ), - Icon( - Icons.arrow_right_rounded, - size: 24.sp, - ) - ], - ), - Icon( - Icons.add_circle_outline_rounded, - size: 24.sp, - ), - ], + Icon( + Icons.arrow_right_rounded, + size: 22.sp, + ) + ], + ), + Icon( + Icons.add_circle_outline, + size: 22.sp, + ), + ], + ), ); } _buildSystemNotificationPermissionRow() { - return Visibility( - visible: !controller.isOpenNotificationPermission.value, - child: Container( - decoration: const BoxDecoration( - color: Color(0xFFfdefdf), - ), - child: Row( - children: [ - Text( - '系统通知未开启,报警消息无法通知'.tr, - style: TextStyle( - color: Color(0xFFea8720), - ), + return Obx( + () => Visibility( + visible: !controller.isOpenNotificationPermission.value, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.h), + decoration: BoxDecoration( + color: const Color(0xFFFEF2E5), + borderRadius: BorderRadius.all( + Radius.circular(8.r), ), - Container( - child: Text( - '去开启'.tr, + ), + margin: EdgeInsets.symmetric(vertical: 8.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + '系统通知未开启,报警消息无法通知'.tr, style: TextStyle( - color: Colors.white, + color: const Color(0xFFEE9846), + fontSize: 12.sp, ), ), - ) - ], + const Spacer(), + GestureDetector( + onTap: () async { + controller.isOpenNotificationPermission.value = + await AppPermission.requestNotificationPermission(); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 4.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all( + Radius.circular(4.r), + ), + ), + child: Text( + '去开启'.tr, + style: TextStyle( + color: const Color(0xFFEE9846), + fontSize: 12.sp, + ), + ), + ), + ), + SizedBox( + width: 14.w, + ), + Icon( + Icons.cancel, + color: const Color(0xFFEE9846), + size: 18.sp, + ) + ], + ), ), ), ); diff --git a/lib/views/home/widget/home_attendance_chart_area_widget.dart b/lib/views/home/widget/home_attendance_chart_area_widget.dart new file mode 100644 index 0000000..612a5e3 --- /dev/null +++ b/lib/views/home/widget/home_attendance_chart_area_widget.dart @@ -0,0 +1,243 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/common/constant/app_images.dart'; +import 'dart:math' as math; + +class HomeAttendanceChartAreaWidget extends StatefulWidget { + const HomeAttendanceChartAreaWidget({super.key}); + + @override + State createState() => + _HomeAttendanceChartAreaWidgetState(); +} + +class _HomeAttendanceChartAreaWidgetState + extends State { + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.r), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.1), + spreadRadius: 1, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + children: [ + _buildTitle(), + SizedBox(height: 16.h), + _buildContent(), + SizedBox(height: 16.h), + ], + ), + ); + } + + Widget _buildTitle() { + return Row( + children: [ + Image.asset( + AppImages.iconOneKeyDoor, + width: 16.w, + height: 16.h, + ), + SizedBox(width: 8.w), + Text( + '考勤'.tr, + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w500, + ), + ), + const Spacer(), + Icon( + Icons.arrow_forward_ios, + size: 14.sp, + color: const Color(0xFFBFBFBF), + ), + ], + ); + } + + Widget _buildContent() { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // 左侧圆形进度图表 + _buildCircularChart(), + SizedBox(width: 24.w), + // 右侧统计信息 + _buildStatistics(), + ], + ); + } + + Widget _buildCircularChart() { + return SizedBox( + width: 80.w, + height: 80.w, + child: Stack( + alignment: Alignment.center, + children: [ + // 背景圆环 + CustomPaint( + size: Size(80.w, 80.w), + painter: CircleProgressPainter( + progress: 0.5, // 0% 进度 + strokeWidth: 8.w, + backgroundColor: const Color(0xFFF5F5F5), + progressColor: const Color(0xFF4A90E2), + ), + ), + // 中心文字 + Text( + '50%', + style: TextStyle( + fontSize: 20.sp, + fontWeight: FontWeight.w600, + color: const Color(0xFF333333), + ), + ), + ], + ), + ); + } + + Widget _buildStatistics() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 今日出勤率 + Row( + children: [ + Text( + '今日出勤率:', + style: TextStyle( + fontSize: 14.sp, + color: const Color(0xFF333333), + ), + ), + Text( + '0/14', + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w600, + color: const Color(0xFF333333), + ), + ), + ], + ), + SizedBox(height: 12.h), + // 未打卡和迟到统计 + Row( + children: [ + // 未打卡 + Row( + children: [ + Text( + '未打卡:', + style: TextStyle( + fontSize: 14.sp, + color: const Color(0xFF666666), + ), + ), + Text( + '14', + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w600, + color: const Color(0xFFFF9500), + ), + ), + ], + ), + SizedBox(width: 24.w), + // 迟到 + Row( + children: [ + Text( + '迟到:', + style: TextStyle( + fontSize: 14.sp, + color: const Color(0xFF666666), + ), + ), + Text( + '0', + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w600, + color: const Color(0xFFFF4D4F), + ), + ), + ], + ), + ], + ), + ], + ); + } +} + +// 自定义圆形进度画笔 +class CircleProgressPainter extends CustomPainter { + final double progress; + final double strokeWidth; + final Color backgroundColor; + final Color progressColor; + + CircleProgressPainter({ + required this.progress, + required this.strokeWidth, + required this.backgroundColor, + required this.progressColor, + }); + + @override + void paint(Canvas canvas, Size size) { + final center = Offset(size.width / 2, size.height / 2); + final radius = (size.width - strokeWidth) / 2; + + // 绘制背景圆环 + final backgroundPaint = Paint() + ..color = backgroundColor + ..strokeWidth = strokeWidth + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + canvas.drawCircle(center, radius, backgroundPaint); + + // 绘制进度圆弧 + if (progress > 0) { + final progressPaint = Paint() + ..color = progressColor + ..strokeWidth = strokeWidth + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + final sweepAngle = 2 * math.pi * progress; + canvas.drawArc( + Rect.fromCircle(center: center, radius: radius), + -math.pi / 2, // 从顶部开始 + sweepAngle, + false, + progressPaint, + ); + } + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return oldDelegate != this; + } +} diff --git a/lib/views/home/widget/home_carousel_area_widget.dart b/lib/views/home/widget/home_carousel_area_widget.dart new file mode 100644 index 0000000..41511f4 --- /dev/null +++ b/lib/views/home/widget/home_carousel_area_widget.dart @@ -0,0 +1,96 @@ +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; + +class HomeCarouselAreaWidget extends StatefulWidget { + const HomeCarouselAreaWidget({ + super.key, + required this.carouselCurrentIndex, + }); + + final RxInt carouselCurrentIndex; + + @override + State createState() => _HomeCarouselAreaWidgetState(); +} + +class _HomeCarouselAreaWidgetState extends State { + final List imgList = [ + 'assets/images/mockImage.jpg', + 'assets/images/mockImage.jpg', + 'assets/images/mockImage.jpg', + ]; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + CarouselSlider( + options: CarouselOptions( + height: 68.h, + autoPlay: true, + autoPlayInterval: const Duration(seconds: 5), + autoPlayAnimationDuration: const Duration(milliseconds: 800), + viewportFraction: 1.0, + onPageChanged: (index, reason) { + widget.carouselCurrentIndex.value = index; + }, + ), + items: imgList.map( + (i) { + return Builder( + builder: (BuildContext context) { + return ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: Image.asset( + width: double.infinity, + height: 68.h, + i, + fit: BoxFit.fill, + ), + ); + }, + ); + }, + ).toList(), + ), + // 分段横线指示器 + Positioned( + left: 10.w, + bottom: 6.h, + child: Obx( + () => Row( + mainAxisAlignment: MainAxisAlignment.center, + children: imgList.asMap().entries.map((entry) { + int index = entry.key; + int length = imgList.length; + + // 判断是否为第一个或最后一个 + bool isFirst = index == 0; + bool isLast = index == length - 1; + return Container( + width: 8.w, + height: 2.0.h, + decoration: BoxDecoration( + color: widget.carouselCurrentIndex.value == index + ? Colors.white + : Colors.grey[400], + // 精确控制圆角 + borderRadius: BorderRadius.only( + topLeft: Radius.circular(isFirst ? 2.0 : 0), + bottomLeft: Radius.circular(isFirst ? 2.0 : 0), + topRight: Radius.circular(isLast ? 2.0 : 0), + bottomRight: Radius.circular(isLast ? 2.0 : 0), + ), + ), + ); + }).toList(), + ), + ), + ), + ], + ); + } +} diff --git a/lib/views/home/widget/home_function_list_area_widget.dart b/lib/views/home/widget/home_function_list_area_widget.dart new file mode 100644 index 0000000..79dea13 --- /dev/null +++ b/lib/views/home/widget/home_function_list_area_widget.dart @@ -0,0 +1,245 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class HomeFunctionListAreaWidget extends StatefulWidget { + HomeFunctionListAreaWidget({super.key}); + + @override + State createState() => + _HomeFunctionListAreaWidgetState(); +} + +class _HomeFunctionListAreaWidgetState extends State + with SingleTickerProviderStateMixin { + late TabController _tabController; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 6, vsync: this); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 10.w), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all( + Radius.circular(8.r), + ), + ), + child: Column( + children: [ + _buildTabBar(), + SizedBox(height: 12.h), + _buildTabBarView(), + ], + ), + ); + } + + Widget _buildTabBar() { + final List functionOptionList = [ + '人员通行', + '考勤', + '审批', + '可视对讲', + '信息发布', + '其他应用', + ]; + + return Row( + children: [ + Expanded( + child: TabBar( + controller: _tabController, + isScrollable: true, + labelColor: Colors.black87, + unselectedLabelColor: Colors.grey, + labelStyle: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + ), + unselectedLabelStyle: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w400, + ), + indicatorColor: Colors.black87, + indicatorWeight: 0.5.h, + indicatorSize: TabBarIndicatorSize.label, + labelPadding: EdgeInsets.only(right: 16.w), + // 只保留右边距,左边紧贴 + tabAlignment: TabAlignment.start, + // 从左边开始排列 + padding: EdgeInsets.zero, + // 移除TabBar的默认内边距 + dividerColor: Colors.transparent, + // 移除灰色下边框 + tabs: functionOptionList.map((title) => Tab(text: title)).toList(), + ), + ), + // 右侧固定菜单图标 + Container( + padding: EdgeInsets.only( + left: 8.w, + ), + child: GestureDetector( + onTap: () { + // 处理菜单点击事件 + print('菜单被点击'); + }, + child: Icon( + Icons.menu, + size: 18.sp, + color: Colors.grey[600], + ), + ), + ), + ], + ); + } + + Widget _buildTabBarView() { + final List functionOptionList = [ + '人员通行', + '考勤', + '审批', + '可视对讲', + '信息发布', + '其他应用', + ]; + + // 每个分类对应的功能列表 + final Map>> functionData = { + '人员通行': [ + {'icon': Icons.door_front_door, 'title': '门禁管理'}, + {'icon': Icons.people, 'title': '门禁授权'}, + {'icon': Icons.access_time, 'title': '一键开门'}, + {'icon': Icons.card_membership, 'title': '密码开门'}, + {'icon': Icons.face, 'title': '我的访客'}, + {'icon': Icons.fingerprint, 'title': '访客统计'}, + {'icon': Icons.security, 'title': '访客邀约'}, + ], + '考勤': [ + {'icon': Icons.schedule, 'title': '考勤设置'}, + {'icon': Icons.assignment, 'title': '审批记录'}, + {'icon': Icons.broadcast_on_personal, 'title': '广播'}, + {'icon': Icons.announcement, 'title': '信息发布'}, + {'icon': Icons.bar_chart, 'title': '统计报表'}, + {'icon': Icons.calendar_today, 'title': '排班管理'}, + {'icon': Icons.access_alarm, 'title': '打卡记录'}, + {'icon': Icons.person_add, 'title': '添加常用'}, + ], + '审批': [ + {'icon': Icons.approval, 'title': '待审批'}, + {'icon': Icons.check_circle, 'title': '已审批'}, + {'icon': Icons.pending, 'title': '审批中'}, + {'icon': Icons.history, 'title': '审批历史'}, + {'icon': Icons.rule, 'title': '审批规则'}, + {'icon': Icons.group, 'title': '审批流程'}, + {'icon': Icons.notifications, 'title': '审批提醒'}, + {'icon': Icons.analytics, 'title': '审批统计'}, + ], + '可视对讲': [ + {'icon': Icons.call, 'title': '对讲设备'}, + {'icon': Icons.call_received, 'title': '呼叫记录'}, + {'icon': Icons.video_call, 'title': '视频通话'}, + {'icon': Icons.volume_up, 'title': '广播通知'}, + {'icon': Icons.settings_phone, 'title': '设备设置'}, + {'icon': Icons.group_add, 'title': '群组管理'}, + {'icon': Icons.record_voice_over, 'title': '语音留言'}, + {'icon': Icons.emergency, 'title': '紧急呼叫'}, + ], + '信息发布': [ + {'icon': Icons.campaign, 'title': '发布信息'}, + {'icon': Icons.list_alt, 'title': '信息列表'}, + {'icon': Icons.schedule_send, 'title': '定时发布'}, + {'icon': Icons.group, 'title': '目标群体'}, + {'icon': Icons.image, 'title': '多媒体'}, + {'icon': Icons.analytics, 'title': '发布统计'}, + {'icon': Icons.edit, 'title': '编辑模板'}, + {'icon': Icons.history, 'title': '发布历史'}, + ], + '其他应用': [ + {'icon': Icons.apps, 'title': '应用中心'}, + {'icon': Icons.extension, 'title': '插件管理'}, + {'icon': Icons.download, 'title': '下载中心'}, + {'icon': Icons.update, 'title': '应用更新'}, + {'icon': Icons.settings, 'title': '应用设置'}, + {'icon': Icons.help, 'title': '帮助中心'}, + {'icon': Icons.feedback, 'title': '意见反馈'}, + {'icon': Icons.info, 'title': '关于我们'}, + ], + }; + + return SizedBox( + height: 100.h, // 设置适合的高度 + child: TabBarView( + controller: _tabController, + children: functionOptionList.map((category) { + final currentFunctions = functionData[category] ?? []; + return GridView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 4, + childAspectRatio: 1.6, + ), + itemCount: currentFunctions.length, + itemBuilder: (context, index) { + final function = currentFunctions[index]; + return _buildFunctionItem( + icon: function['icon'], + title: function['title'], + ); + }, + ); + }).toList(), + ), + ); + } + + Widget _buildFunctionItem({required IconData icon, required String title}) { + return GestureDetector( + onTap: () { + print('点击了: $title'); + // 处理功能项点击事件 + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, // 重要:让Column最小化 + children: [ + Icon( + icon, + size: 20.sp, // 稍微减小图标 + color: Colors.blue.shade600, + ), + SizedBox(height: 4.h), // 减少间距 + Flexible( + // 使用Flexible而不是Expanded + child: Text( + title, + style: TextStyle( + fontSize: 12.sp, // 稍微减小字体 + color: Colors.black87, + fontWeight: FontWeight.w400, + ), + textAlign: TextAlign.center, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + } +} diff --git a/lib/views/home/widget/home_left_drawer_widget.dart b/lib/views/home/widget/home_left_drawer_widget.dart new file mode 100644 index 0000000..5661320 --- /dev/null +++ b/lib/views/home/widget/home_left_drawer_widget.dart @@ -0,0 +1,280 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class HomeLeftDrawerWidget extends StatefulWidget { + HomeLeftDrawerWidget({ + super.key, + required this.teamList, + this.selectedTeam, + this.onTeamSelected, + }); + + final List teamList; + final String? selectedTeam; + final Function(String)? onTeamSelected; + + @override + State createState() => _HomeLeftDrawerWidgetState(); +} + +class _HomeLeftDrawerWidgetState extends State { + @override + Widget build(BuildContext context) { + return Drawer( + width: 0.85.sw, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.zero, // 去掉圆角 + ), + child: Container( + color: const Color(0xFFF6F7FB), + child: SafeArea( + child: Column( + children: [ + // 头部标题栏 + Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '我的团队', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + width: 28.w, + height: 28.w, + decoration: BoxDecoration( + color: Colors.grey[300], + shape: BoxShape.circle, + ), + child: Icon( + Icons.refresh, + size: 18.sp, + color: Colors.grey[600], + ), + ), + ), + ], + ), + ), + + // 团队列表 + Expanded( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 16.w), + child: ListView.builder( + itemCount: widget.teamList.length, + itemBuilder: (context, index) { + final team = widget.teamList[index]; + final isSelected = team == widget.selectedTeam; + + return Container( + margin: EdgeInsets.only(bottom: 8.h), + padding: EdgeInsets.all(16.w), + decoration: BoxDecoration( + color: isSelected + ? const Color(0xFFE3F2FD) // 选中状态:蓝色背景 + : Colors.white, // 未选中状态:白色背景 + borderRadius: BorderRadius.circular(12.r), + border: isSelected + ? Border.all(color: const Color(0xFF2196F3), width: 1) + : Border.all(color: Colors.grey[200]!, width: 1), + ), + child: GestureDetector( + onTap: () { + // 处理团队选择 + if (widget.onTeamSelected != null) { + widget.onTeamSelected!(team); + } + }, + child: Row( + children: [ + // 用户图标 + Container( + width: 40.w, + height: 40.w, + decoration: BoxDecoration( + color: isSelected + ? const Color(0xFF2196F3) + : Colors.grey[400], + borderRadius: BorderRadius.circular(8.r), + ), + child: Icon( + Icons.person, + color: Colors.white, + size: 24.sp, + ), + ), + SizedBox(width: 12.w), + + // 团队信息 + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + team, // 使用teamList中的元素作为团队昵称 + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w500, + color: isSelected + ? const Color(0xFF2196F3) + : Colors.black, + ), + ), + SizedBox(height: 4.h), + Row( + children: [ + Icon( + isSelected + ? Icons.check_circle + : Icons.check_circle_outline, + size: 14.sp, + color: isSelected + ? const Color(0xFF2196F3) + : Colors.grey[400], + ), + SizedBox(width: 4.w), + Text( + isSelected ? '已选中' : '未选中', + style: TextStyle( + fontSize: 12.sp, + color: isSelected + ? const Color(0xFF2196F3) + : Colors.grey[600], + ), + ), + ], + ), + ], + ), + ), + + // 设置图标 + GestureDetector( + onTap: () { + // 处理设置点击事件 + Navigator.pop(context); + }, + child: Container( + padding: EdgeInsets.all(8.w), + child: Icon( + Icons.settings, + size: 20.sp, + color: isSelected + ? const Color(0xFF2196F3) + : Colors.grey[600], + ), + ), + ), + ], + ), + ), + ); + }, + ), + ), + ), + + // 底部按钮区域 + Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), + child: Column( + children: [ + // 创建团队和加入团队按钮 + Row( + children: [ + Expanded( + child: GestureDetector( + onTap: () { + // 处理创建团队 + Navigator.pop(context); + }, + child: Container( + padding: EdgeInsets.symmetric(vertical: 12.h), + decoration: BoxDecoration( + border: Border( + right: BorderSide( + color: Colors.grey[300]!, + width: 0.5, + ), + ), + ), + child: Text( + '创建团队', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16.sp, + color: Colors.black87, + ), + ), + ), + ), + ), + Expanded( + child: GestureDetector( + onTap: () { + // 处理加入团队 + Navigator.pop(context); + }, + child: Container( + padding: EdgeInsets.symmetric(vertical: 12.h), + child: Text( + '加入团队', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16.sp, + color: Colors.black87, + ), + ), + ), + ), + ), + ], + ), + + // 分割线 + Container( + margin: EdgeInsets.symmetric(vertical: 8.h), + height: 0.5, + color: Colors.grey[300], + ), + + // 快捷添加我的设备按钮 + GestureDetector( + onTap: () { + // 处理快捷添加设备 + Navigator.pop(context); + }, + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 12.h), + child: Text( + '快捷添加我的设备', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16.sp, + color: Colors.black87, + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/views/home/widget/home_one_button_door_opening_widget.dart b/lib/views/home/widget/home_one_button_door_opening_widget.dart new file mode 100644 index 0000000..aa70ab1 --- /dev/null +++ b/lib/views/home/widget/home_one_button_door_opening_widget.dart @@ -0,0 +1,194 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/common/constant/app_images.dart'; + +class HomeOnButtonDoorOpeningWidget extends StatefulWidget { + final List doorList; // 门禁列表,最多显示4个 + + const HomeOnButtonDoorOpeningWidget({ + super.key, + required this.doorList, + }); + + @override + State createState() => + _HomeOnButtonDoorOpeningWidgetState(); +} + +class _HomeOnButtonDoorOpeningWidgetState + extends State { + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.r), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.1), + spreadRadius: 1, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + children: [ + _buildTitle(), + SizedBox(height: 4.h), + _buildDoorCards(), + ], + ), + ); + } + + Widget _buildTitle() { + return Row( + children: [ + Image.asset( + AppImages.iconOneKeyDoor, + width: 16.w, + height: 16.h, + ), + SizedBox(width: 8.w), + Text( + '一键开门'.tr, + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w500, + ), + ), + const Spacer(), + Icon( + Icons.arrow_forward_ios, + size: 14.sp, + color: const Color(0xFFBFBFBF), + ), + ], + ); + } + + Widget _buildDoorCards() { + // 限制最多4个元素 + final displayList = widget.doorList.take(4).toList(); + + if (displayList.isEmpty) { + return const SizedBox.shrink(); // 如果没有数据则不显示 + } + + List rows = []; + + // 按照每行2个的方式组织数据 + for (int i = 0; i < displayList.length; i += 2) { + List rowChildren = []; + + // 第一个元素 + rowChildren.add(_buildDoorCard(i, displayList[i])); + + // 第二个元素(如果存在) + if (i + 1 < displayList.length) { + rowChildren.add(_buildDoorCard(i + 1, displayList[i + 1])); + } else { + // 如果只有一个元素,添加空白占位 + rowChildren.add(SizedBox(width: 150.w)); + } + + rows.add( + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: rowChildren, + ), + ); + } + + return Column( + children: rows, + ); + } + + Widget _buildDoorCard(int index, String doorName) { + return Container( + width: 150.w, // 增大宽度以适应一行两个的布局 + height: 80.h, // 增加高度 + margin: EdgeInsets.only(top: 12.h), + child: Stack( + children: [ + // 背景图片 + Container( + width: 150.w, + height: 80.h, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.r), + image: const DecorationImage( + image: AssetImage(AppImages.bgOneKeyDoor), + fit: BoxFit.cover, + ), + ), + ), + // 播放按钮 + Positioned( + top: 12.h, + right: 12.w, + child: Container( + width: 20.w, + height: 20.w, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.5), + borderRadius: BorderRadius.circular(8.r), + ), + child: Icon( + Icons.play_arrow_rounded, + size: 14.sp, + color: const Color(0xFF515057), + ), + ), + ), + // 左下角内容 + Positioned( + left: 12.w, + top: 8.h, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 圆形钥匙图标 + Container( + width: 34.w, + height: 34.h, + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.3), + shape: BoxShape.circle, + ), + child: Center( + child: Image.asset( + AppImages.iconOneKeyDoorKey, + width: 18.w, + height: 18.h, + ), + ), + ), + SizedBox(height: 16.h), + // 文本 + SizedBox( + width: 80.w, // 增加文本宽度 + child: Text( + doorName, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w500, + fontSize: 14.sp, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/views/home/widget/home_statistics_row_widget.dart b/lib/views/home/widget/home_statistics_row_widget.dart new file mode 100644 index 0000000..44b3b7e --- /dev/null +++ b/lib/views/home/widget/home_statistics_row_widget.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; + +class HomeStatisticsRowWidget extends StatelessWidget { + final int personCount; + final int deviceCount; + + const HomeStatisticsRowWidget({ + super.key, + this.personCount = 0, + this.deviceCount = 0, + }); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + _buildStatisticsCard( + count: personCount, + unit: '人', + label: '人员总数', + backgroundColor: const Color(0xFFCEF2F5), + textColor: const Color(0xFF134347), + buttonText: '人员管理', + ), + SizedBox(width: 8.w), // 卡片之间的间距 + _buildStatisticsCard( + count: deviceCount, + unit: '台', + label: '设备总数', + backgroundColor: const Color(0xFFD4E0FF), + textColor: const Color(0xFF172A5B), + buttonText: '设备管理', + ), + ], + ); + } + + Widget _buildStatisticsCard({ + required int count, + required String unit, + required String label, + required Color backgroundColor, + required Color textColor, + required String buttonText, + }) { + return Expanded( + // 使用Expanded让卡片自适应宽度 + child: Container( + height: 62.h, + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: BorderRadius.circular(8.r), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.1), + spreadRadius: 1, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 上半部分:数字 + 单位 + 管理按钮 + Row( + children: [ + // 数字和单位 + Row( + crossAxisAlignment: CrossAxisAlignment.baseline, + // 使用baseline对齐 + textBaseline: TextBaseline.alphabetic, + // 设置文本基线 + children: [ + Text( + count.toString(), + style: TextStyle( + fontSize: 20.sp, // text-5 + fontWeight: FontWeight.bold, + color: textColor, + ), + ), + Text( + unit, + style: TextStyle( + fontSize: 12.sp, // text-3 + color: textColor, + ), + ), + ], + ), + const Spacer(), + // 管理按钮 + Container( + padding: EdgeInsets.symmetric(horizontal: 4.w, vertical: 4.h), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.5), + borderRadius: BorderRadius.circular(4.r), + ), + child: Text( + buttonText, + style: TextStyle( + fontSize: 10.sp, // text-2.5 + fontWeight: FontWeight.w600, + color: textColor, + ), + ), + ), + ], + ), + SizedBox(height: 4.h), // mt-1 + // 下半部分:标签 + Text( + label, + style: TextStyle( + fontSize: 10.sp, // text-2.5 + color: textColor, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/views/home/widget/home_team_notice_row_widget.dart b/lib/views/home/widget/home_team_notice_row_widget.dart new file mode 100644 index 0000000..bb48a0f --- /dev/null +++ b/lib/views/home/widget/home_team_notice_row_widget.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; + +class HomeTeamNoticeRowWidget extends StatefulWidget { + const HomeTeamNoticeRowWidget({super.key}); + + @override + State createState() => _HomeTeamNoticeRowWidgetState(); +} + +class _HomeTeamNoticeRowWidgetState extends State { + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.symmetric(vertical: 10.h), + alignment: Alignment.center, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all( + Radius.circular(8.r), + ), + ), + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + RichText( + text: TextSpan( + children: [ + TextSpan( + text: '团队'.tr, + style: TextStyle( + color: Colors.black, + fontSize: 14.sp, + fontWeight: FontWeight.w600, + ), + ), + TextSpan( + text: '公告'.tr, + style: TextStyle( + color: Colors.red, + fontSize: 14.sp, + fontWeight: FontWeight.w600, + ), + ), + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: Icon( + Icons.brightness_1, + size: 6.sp, + ), + ), + ), + TextSpan( + text: '公告标题'.tr, + style: TextStyle( + color: Colors.black, + fontSize: 14.sp, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + RichText( + text: TextSpan( + children: [ + TextSpan( + text: '全部'.tr, + style: TextStyle( + color: Colors.grey, + fontSize: 14.sp, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + SizedBox( + width: 4.w, + ), + Icon( + Icons.arrow_forward_ios_rounded, + color: Colors.grey, + size: 12.sp, + ) + ], + ), + ], + ), + ); + } +} diff --git a/lib/views/login/inputVerificationCode/input_verification_code_controller.dart b/lib/views/login/inputVerificationCode/input_verification_code_controller.dart index e933f29..a5f8cf1 100644 --- a/lib/views/login/inputVerificationCode/input_verification_code_controller.dart +++ b/lib/views/login/inputVerificationCode/input_verification_code_controller.dart @@ -70,12 +70,29 @@ class InputVerificationCodeController extends BaseController { } // 校验验证码 - void checkVerificationCode(String pin) { + void checkVerificationCode(String pin) async { if (previousRoute.value.contains(AppRoutes.login)) { - Get.offAllNamed(AppRoutes.main); + showLoading(); + var validationCodeLoginResult = await userApi.validationCodeLogin( + request: ValidationCodeLoginRequest( + platId: int.parse(PlatformType.app.value), + phone: rawPhone.value, + verificationCode: pin, + ), + ); + if (validationCodeLoginResult.isSuccess) { + await SharedPreferencesUtils.setString( + CacheKeys.token, + validationCodeLoginResult.data!.token, + ); + Get.offAllNamed(AppRoutes.main); + } else { + showError(message: validationCodeLoginResult.errorMsg!); + } } else if (previousRoute.value.contains(AppRoutes.forgotPassword)) { Get.toNamed(AppRoutes.setNewPassword); } + hideLoading(); } void _handleSeedVerificationCode({ diff --git a/pubspec.lock b/pubspec.lock index 37d5265..beb4685 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -33,6 +33,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" + carousel_slider: + dependency: "direct main" + description: + name: carousel_slider + sha256: bcc61735345c9ab5cb81073896579e735f81e35fd588907a393143ea986be8ff + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.1.1" characters: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index adfa556..eb8960a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,8 @@ dependencies: flutter_spinkit: 5.2.1 # 骨架屏 skeletonizer: ^1.4.3 + # 轮播图 + carousel_slider: ^5.1.1 dev_dependencies: @@ -45,4 +47,8 @@ dev_dependencies: flutter: uses-material-design: true + assets: + - assets/images/ + - assets/logo/ + - assets/icon/