瀏覽代碼

Merge branch 'v1.1.0' into v1.1.0-iOS

# Conflicts:
#	lib/module/track/track_day_detail/track_day_detail_controller.dart
“HeShaoZe” 4 月之前
父節點
當前提交
c7c1dddc84

二進制
assets/images/bg_track_report_user.webp


二進制
assets/images/icon_share_phone.webp


二進制
assets/images/icon_share_stay.webp


二進制
assets/images/icon_share_track_detail.webp


二進制
assets/images/img_track_report_robot.webp


二進制
assets/images/img_track_report_title.webp


+ 3 - 0
assets/string/base/string.xml

@@ -383,4 +383,7 @@
     </string>
     <string name="one_login_error">一键登录失败,请尝试验证码登录</string>
     <string name="one_login_txt">一键登录</string>
+    <string name="track_stay_longest_place">停留最长地点</string>
+    <string name="track_stay_share_logo_desc">为你重要的朋友保驾护航</string>
+    <string name="track_stay_share_analysis">loca分析中,请稍等..</string>
 </resources>

+ 28 - 15
lib/module/main/main_page.dart

@@ -116,12 +116,7 @@ class MainPage extends BasePage<MainController> {
                   ? null
                   : controller
                       .todayTrackReportMap[controller.selectedFriend?.id];
-              if (controller.memberStatusInfo.value == null ||
-                  controller.memberStatusInfo.value?.expired == true) {
-                return buildNoMemberView();
-              } else {
-                return buildTodayTrackView(todayTrack);
-              }
+              return buildTodayTrackView(todayTrack);
             })),
       ),
     );
@@ -133,6 +128,10 @@ class MainPage extends BasePage<MainController> {
       Visibility(
           visible: todayTrack?.isRequestSuccess.value == false,
           child: buildTodayTrackLoadingView()),
+      Visibility(
+          visible: controller.memberStatusInfo.value == null ||
+              controller.memberStatusInfo.value?.expired == true,
+          child: buildNoMemberView())
     ]);
   }
 
@@ -313,9 +312,11 @@ class MainPage extends BasePage<MainController> {
               },
               child: Container(
                   margin: EdgeInsets.only(right: 16.w, left: 8.w),
-                  child: Platform.isIOS ? Assets.images.iconMainAddFriend
-                      .image(width: 60.w, height: 60.w) : Assets.images.iconMainAddFriendAndroid
-                      .image(width: 60.w, height: 60.w)),
+                  child: Platform.isIOS
+                      ? Assets.images.iconMainAddFriend
+                          .image(width: 60.w, height: 60.w)
+                      : Assets.images.iconMainAddFriendAndroid
+                          .image(width: 60.w, height: 60.w)),
             )
           ],
         ),
@@ -375,22 +376,34 @@ class MainPage extends BasePage<MainController> {
       children: [
         Expanded(
             child: buildFunItem(
-                Platform.isIOS ? Assets.images.iconMainFriendGuard.provider() : Assets.images.iconMainFriendGuardAndroid.provider(),
+                Platform.isIOS
+                    ? Assets.images.iconMainFriendGuard.provider()
+                    : Assets.images.iconMainFriendGuardAndroid.provider(),
                 StringName.mainFriendListTab,
                 () => controller.onFriendClick())),
         Expanded(child: Obx(() {
-          return buildFunItem(Platform.isIOS ? Assets.images.iconMainNews.provider() : Assets.images.iconMainNewsAndroid.provider(),
-              StringName.mainNewsTab, () => controller.onNewsClick(),
+          return buildFunItem(
+              Platform.isIOS
+                  ? Assets.images.iconMainNews.provider()
+                  : Assets.images.iconMainNewsAndroid.provider(),
+              StringName.mainNewsTab,
+              () => controller.onNewsClick(),
               isShowDot: controller.hasUnreadMessage);
         })),
         Expanded(
             child: buildFunItem(
-                Platform.isIOS ? Assets.images.iconMainHelp.provider() : Assets.images.iconMainHelpAndroid.provider(),
+                Platform.isIOS
+                    ? Assets.images.iconMainHelp.provider()
+                    : Assets.images.iconMainHelpAndroid.provider(),
                 StringName.mainHelpTab,
                 () => controller.onUrgentContactClick())),
         Expanded(
-            child: buildFunItem(Platform.isIOS ? Assets.images.iconMainMine.provider() : Assets.images.iconMainMineAndroid.provider(),
-                StringName.mainMineTab, () => controller.onMineClick()))
+            child: buildFunItem(
+                Platform.isIOS
+                    ? Assets.images.iconMainMine.provider()
+                    : Assets.images.iconMainMineAndroid.provider(),
+                StringName.mainMineTab,
+                () => controller.onMineClick()))
       ],
     );
   }

+ 20 - 0
lib/module/track/track_controller.dart

@@ -5,6 +5,7 @@ import 'package:get/get.dart';
 import 'package:get/get_core/src/get_main.dart';
 import 'package:injectable/injectable.dart';
 import 'package:location/base/base_controller.dart';
+import 'package:location/data/api/response/track_daily_summary_response.dart';
 import 'package:location/data/bean/track_days.dart';
 import 'package:location/data/consts/constants.dart';
 import 'package:location/data/repositories/account_repository.dart';
@@ -19,6 +20,7 @@ import '../../data/bean/user_info.dart';
 import '../../dialog/common_confirm_dialog_impl.dart';
 import '../../dialog/location_permission_dialog.dart';
 import '../../sdk/map/map_helper.dart';
+import '../../sdk/wechat/wechat_share_util.dart';
 import '../../utils/permission_util.dart';
 import '../../utils/toast_util.dart';
 
@@ -41,6 +43,8 @@ class TrackController extends BaseController
 
   final Rxn<TrackDays> currentTrackDay = Rxn();
 
+  final GlobalKey shareGlobalKey = GlobalKey();
+
   final RxDouble _sheetProgress = 0.0.obs;
 
   double get sheetProgress => _sheetProgress.value;
@@ -53,6 +57,10 @@ class TrackController extends BaseController
 
   final RxDouble trackBottomHeight = RxDouble(330.w);
 
+  final Rxn<TrackDailySummaryResponse> _shareTrackData = Rxn();
+
+  TrackDailySummaryResponse? get shareTrackData => _shareTrackData.value;
+
   TrackDailyBean? selectedTrackDailyBean;
 
   final TrackRepository trackRepository;
@@ -252,6 +260,18 @@ class TrackController extends BaseController
     }
   }
 
+  void shareTrack(TrackDailySummaryResponse summary) async {
+    final day = currentTrackDay.value;
+    final userInfo = _userInfo.value;
+    if (day == null || userInfo == null) {
+      return;
+    }
+    _shareTrackData.value = summary;
+    await Future.delayed(Duration(milliseconds: 100));
+    await WechatShareUtil.shareWidgetToWeChat(shareGlobalKey);
+    _shareTrackData.value = null;
+  }
+
   @override
   void onClose() {
     super.onClose();

+ 12 - 6
lib/module/track/track_day_detail/track_day_detail_controller.dart

@@ -2,7 +2,6 @@ import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
 import 'dart:math';
-
 import 'package:flutter/cupertino.dart';
 import 'package:flutter_map/flutter_map.dart';
 import 'package:get/get.dart';
@@ -10,7 +9,8 @@ import 'package:get/get_core/src/get_main.dart';
 import 'package:location/base/base_controller.dart';
 import 'package:location/data/bean/track_daily_bean.dart';
 import 'package:location/data/consts/constants.dart';
-import 'package:location/data/repositories/account_repository.dart' show AccountRepository;
+import 'package:location/data/repositories/account_repository.dart'
+    show AccountRepository;
 import 'package:location/data/repositories/track_repository.dart';
 import 'package:location/dialog/loading_dialog.dart';
 import 'package:location/handler/error_handler.dart';
@@ -19,7 +19,6 @@ import 'package:location/module/track/track_controller.dart';
 import 'package:location/module/track/track_day_detail/time_proportion/pie_chat_data.dart';
 import 'package:location/module/track/track_util.dart';
 import 'package:location/resource/string.gen.dart';
-import 'package:location/sdk/wechat/wechat_share_util.dart';
 import 'package:location/utils/async_util.dart';
 import 'package:location/utils/common_expand.dart';
 import 'package:location/utils/pair.dart';
@@ -85,8 +84,6 @@ class TrackDayDetailController extends BaseController {
 
   String? get summaryError => _summaryError.value;
 
-  final GlobalKey shareGlobalKey = GlobalKey();
-
   CancelableFuture? summaryFuture;
 
   //轨迹相关
@@ -410,7 +407,16 @@ class TrackDayDetailController extends BaseController {
   }
 
   void onShareClick() async {
-    WechatShareUtil.shareWidgetToWeChat(shareGlobalKey);
+    final summary = _trackDailySummary.value;
+    if (summary == null) {
+      return;
+    }
+    if (summary.showTrackSituation == true &&
+        summary.trackSituation?.isEmpty == true) {
+      ToastUtil.show(StringName.trackStayShareAnalysis);
+      return;
+    }
+    trackController.shareTrack(summary);
   }
 
   void onHistoryTrackItemClick(TrackDailyBean bean) {

+ 34 - 29
lib/module/track/track_day_detail/track_day_detail_view.dart

@@ -94,29 +94,30 @@ class TrackDayDetailView extends BaseView<TrackDayDetailController> {
   }
 
   Widget buildTrackDailySummaryView() {
-    return RepaintBoundary(
-      key: controller.shareGlobalKey,
-      child: Column(
-        children: [
-          buildDailySummaryTitle(),
-          SizedBox(height: 8.w),
-          Obx(() {
-            return _buildSituationItem(StringName.trackDailySummaryPhone,
-                controller.trackDailySummary?.phoneSituation);
-          }),
-          Obx(() {
-            return _buildSituationItem(StringName.trackDailySummaryStay,
-                controller.trackDailySummary?.stayLongest);
-          }),
-          //轨迹情况
-          Obx(() {
-            return _buildDailyTrack(
-                controller.trackDailySummary?.showTrackSituation,
-                controller.trackDailySummary?.trackSituation);
-          }),
-          SizedBox(height: 10.w)
-        ],
-      ),
+    return Column(
+      children: [
+        buildDailySummaryTitle(),
+        SizedBox(height: 8.w),
+        Obx(() {
+          return _buildSituationItem(StringName.trackDailySummaryPhone,
+              controller.trackDailySummary?.phoneSituation,
+              isShowDivider: controller.trackDailySummary?.stayLongest != null);
+        }),
+        Obx(() {
+          return _buildSituationItem(StringName.trackDailySummaryStay,
+              controller.trackDailySummary?.stayLongest,
+              isShowDivider:
+                  controller.trackDailySummary?.trackSituation?.isNotEmpty ==
+                      true);
+        }),
+        //轨迹情况
+        Obx(() {
+          return _buildDailyTrack(
+              controller.trackDailySummary?.showTrackSituation,
+              controller.trackDailySummary?.trackSituation);
+        }),
+        SizedBox(height: 10.w)
+      ],
     );
   }
 
@@ -254,7 +255,8 @@ class TrackDayDetailView extends BaseView<TrackDayDetailController> {
     );
   }
 
-  Widget _buildSituationItem(String title, TrackSummary? summary) {
+  Widget _buildSituationItem(String title, TrackSummary? summary,
+      {bool isShowDivider = true}) {
     if (summary == null) {
       return SizedBox.shrink();
     }
@@ -279,12 +281,15 @@ class TrackDayDetailView extends BaseView<TrackDayDetailController> {
                   fontSize: 11.sp,
                   color: '#333333'.color)),
           SizedBox(height: 12.w),
-          Container(
-            width: double.infinity,
-            height: 1.w,
-            color: '#EEEEEE'.color,
+          Visibility(
+            visible: isShowDivider,
+            child: Container(
+              width: double.infinity,
+              height: 1.w,
+              color: '#EEEEEE'.color,
+            ),
           ),
-          SizedBox(height: 10.w),
+          Visibility(visible: isShowDivider, child: SizedBox(height: 10.w)),
         ],
       ),
     );

+ 322 - 0
lib/module/track/track_day_detail/track_share_view.dart

@@ -0,0 +1,322 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:location/data/api/response/track_daily_summary_response.dart';
+import 'package:location/data/bean/user_info.dart';
+import 'package:location/resource/assets.gen.dart';
+import 'package:location/resource/string.gen.dart';
+import 'package:location/utils/common_expand.dart';
+import 'package:location/utils/date_util.dart';
+
+import '../../../data/bean/track_summary.dart';
+import '../../../resource/colors.gen.dart';
+import '../../../resource/fonts.gen.dart';
+import '../../../utils/common_style.dart';
+import '../../../widget/dashed_line.dart';
+import '../../../widget/rich_text_replace.dart';
+
+class TrackShareView extends StatelessWidget {
+  final TrackDailySummaryResponse trackDailySummary;
+  final UserInfo userInfo;
+  final int day;
+
+  const TrackShareView(this.day, this.userInfo, this.trackDailySummary,
+      {super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      width: double.infinity,
+      decoration: BoxDecoration(
+        gradient: LinearGradient(
+            colors: ['#C8C0FF'.color, '#FFFFFF'.color, '#B7ACFF'.color],
+            begin: Alignment.bottomLeft,
+            end: Alignment.topRight),
+      ),
+      child: IntrinsicHeight(
+        child: Stack(
+          children: [
+            buildShareTitleView(),
+            buildRobotView(),
+            Column(
+              children: [
+                SizedBox(height: 129.w),
+                buildShareContentView(),
+              ],
+            ),
+            _buildShareAvatarView(),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Widget buildShareContentView() {
+    return Container(
+      margin: EdgeInsets.symmetric(horizontal: 24.w),
+      child: Column(
+        children: [
+          buildContent1View(),
+          Container(
+            color: ColorName.white,
+            margin: EdgeInsets.symmetric(horizontal: 14.w),
+            child: DashedLine(
+              height: 1.w,
+              color: '#CCCCCC'.color,
+              dashWidth: 3.w,
+              dashSpace: 3.w,
+            ),
+          ),
+          buildContent2View(),
+          SizedBox(height: 96.w),
+          buildAppLogoView(),
+          SizedBox(height: 16.w),
+        ],
+      ),
+    );
+  }
+
+  Widget buildAppLogoView() {
+    return Row(
+      mainAxisAlignment: MainAxisAlignment.center,
+      children: [
+        Assets.images.iconLogo.image(width: 28.w, height: 28.w),
+        SizedBox(width: 6.w),
+        Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            Text(StringName.appName,
+                style: TextStyle(
+                    fontSize: 12.sp,
+                    fontWeight: FontWeight.bold,
+                    color: ColorName.black)),
+            SizedBox(height: 2.w),
+            Text(StringName.trackStayShareLogoDesc,
+                style: TextStyle(fontSize: 9.sp, color: '#74839C'.color))
+          ],
+        )
+      ],
+    );
+  }
+
+  Widget buildContent2View() {
+    return Container(
+      width: double.infinity,
+      padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 20.w),
+      decoration: BoxDecoration(
+          color: ColorName.white, borderRadius: BorderRadius.circular(14.w)),
+      child: Column(
+        children: [
+          _buildSituationItem(
+              Assets.images.iconSharePhone.provider(),
+              StringName.trackDailySummaryPhone,
+              trackDailySummary.phoneSituation,
+              isShowDivider: trackDailySummary.stayLongest != null),
+          _buildSituationItem(Assets.images.iconShareStay.provider(),
+              StringName.trackDailySummaryStay, trackDailySummary.stayLongest,
+              isShowDivider:
+                  trackDailySummary.trackSituation?.isNotEmpty == true),
+          _buildDailyTrack()
+        ],
+      ),
+    );
+  }
+
+  Widget _buildDailyTrack() {
+    if (trackDailySummary.trackSituation?.isNotEmpty == true) {
+      return SizedBox(
+        width: double.infinity,
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            Row(children: [
+              Assets.images.iconShareTrackDetail
+                  .image(width: 15.w, height: 15.w),
+              SizedBox(width: 4.w),
+              Text(StringName.trackDailySummarytrack,
+                  style: TextStyle(
+                      fontSize: 13.sp,
+                      color: '#333333'.color,
+                      fontWeight: FontWeight.bold)),
+            ]),
+            SizedBox(height: 8.w),
+            Text(
+              trackDailySummary.trackSituation ?? '',
+              style: TextStyle(fontSize: 11.sp, color: '#666666'.color),
+            ),
+            SizedBox(height: 12.w),
+          ],
+        ),
+      );
+    }
+    return SizedBox.shrink();
+  }
+
+  Widget _buildSituationItem(
+      ImageProvider icon, String title, TrackSummary? summary,
+      {bool isShowDivider = true}) {
+    if (summary == null) {
+      return SizedBox.shrink();
+    }
+    return SizedBox(
+      width: double.infinity,
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Row(children: [
+            Image(image: icon, width: 15.w, height: 15.w),
+            SizedBox(width: 4.w),
+            Text(title,
+                style: TextStyle(
+                    fontSize: 13.sp,
+                    color: '#333333'.color,
+                    fontWeight: FontWeight.bold)),
+          ]),
+          SizedBox(height: 8.w),
+          RichTextReplace(
+              text: summary.text,
+              items: summary.items,
+              defaultStyle: TextStyle(fontSize: 11.sp, color: '#666666'.color),
+              replacedStyle: TextStyle(
+                  fontWeight: FontWeight.bold,
+                  fontSize: 11.sp,
+                  color: '#333333'.color)),
+          SizedBox(height: 16.w),
+          Visibility(
+            visible: isShowDivider,
+            child: Container(
+              width: double.infinity,
+              height: 1.w,
+              color: '#EEEEEE'.color,
+            ),
+          ),
+          Visibility(visible: isShowDivider, child: SizedBox(height: 16.w)),
+        ],
+      ),
+    );
+  }
+
+  Widget buildContent1View() {
+    return Container(
+      decoration: BoxDecoration(
+          color: ColorName.white,
+          borderRadius: BorderRadius.circular(14.w),
+          border: Border.all(color: ColorName.white, width: 2.w),
+          image: DecorationImage(
+              image: Assets.images.bgTrackReportUser.provider())),
+      width: double.infinity,
+      height: 95.w,
+      child: Stack(
+        children: [
+          Positioned(
+            top: 0,
+            bottom: 0,
+            right: 32.w,
+            child: Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [
+                Text(DateUtil.getDayOfMonth(day),
+                    style: TextStyle(
+                        fontFamily: FontFamily.oppoSans,
+                        fontSize: 36.w,
+                        fontWeight: FontWeight.bold,
+                        height: 1)),
+                SizedBox(height: 4.w),
+                Row(
+                  children: [
+                    Text(
+                        '${DateUtil.fromMillisecondsSinceEpoch("yyyy/MM", day)} ${DateUtil.getWeekday(day)}',
+                        style: TextStyle(
+                            fontSize: 11.sp,
+                            color: '#666666'.color,
+                            height: 1)),
+                  ],
+                )
+              ],
+            ),
+          )
+        ],
+      ),
+    );
+  }
+
+  Widget _buildShareAvatarView() {
+    return Positioned(
+      left: 54.w,
+      top: 118.w,
+      child: Column(
+        children: [
+          Container(
+            width: 68.w,
+            height: 68.w,
+            decoration: BoxDecoration(
+              shape: BoxShape.circle,
+              border: Border.all(color: '#A362FF'.color, width: 2.w),
+            ),
+            child: buildCustomAvatarView(
+              size: double.infinity,
+              avatar: userInfo.avatar ?? '',
+            ),
+          ),
+          SizedBox(height: 6.w),
+          Text(getUserName(userInfo.phoneNumber),
+              style: TextStyle(
+                  fontSize: 12.sp,
+                  color: '#333333'.color,
+                  fontWeight: FontWeight.w500))
+        ],
+      ),
+    );
+  }
+
+  String getUserName(String phone) {
+    if (phone.length > 4) {
+      phone = phone.substring(phone.length - 4);
+    }
+    return '${StringName.mineAccountLoggedDesc}$phone';
+  }
+
+  Widget buildRobotView() {
+    return Positioned(
+        top: 8.w,
+        right: 6.w,
+        child: Assets.images.imgTrackReportRobot
+            .image(width: 150.w, height: 150.w));
+  }
+
+  Widget buildShareTitleView() {
+    return Positioned(
+      left: 23.w,
+      top: 31.w,
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Assets.images.imgTrackReportTitle.image(height: 57.w),
+          Row(
+            children: [
+              if (trackDailySummary.showTrackSituation &&
+                  trackDailySummary.trackSituation?.isNotEmpty == true)
+                _buildTitle(StringName.trackDailySummarytrack),
+              if (trackDailySummary.phoneSituation != null)
+                _buildTitle(StringName.trackDailySummaryPhone),
+              if (trackDailySummary.stayLongest != null)
+                _buildTitle(StringName.trackStayLongestPlace),
+            ],
+          )
+        ],
+      ),
+    );
+  }
+
+  Widget _buildTitle(String title) {
+    return Container(
+        margin: EdgeInsets.only(right: 6.w),
+        padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 4.w),
+        decoration: BoxDecoration(
+            color: '#A362FF'.color, borderRadius: BorderRadius.circular(100.w)),
+        child: Text(title,
+            style:
+                TextStyle(fontSize: 10.sp, color: ColorName.white, height: 1)));
+  }
+}

+ 18 - 1
lib/module/track/track_page.dart

@@ -9,6 +9,7 @@ import 'package:location/base/base_page.dart';
 import 'package:location/data/bean/user_info.dart';
 import 'package:location/module/track/track_controller.dart';
 import 'package:location/module/track/track_day_detail/track_day_detail_view.dart';
+import 'package:location/module/track/track_day_detail/track_share_view.dart';
 import 'package:location/resource/colors.gen.dart';
 import 'package:location/utils/common_expand.dart';
 import 'package:location/utils/common_style.dart';
@@ -34,6 +35,7 @@ class TrackPage extends BasePage<TrackController> {
   Widget buildBody(BuildContext context) {
     return Stack(
       children: [
+        _buildHideShareView(),
         SizedBox(
           width: double.infinity,
           height: double.infinity,
@@ -68,11 +70,26 @@ class TrackPage extends BasePage<TrackController> {
           builder: (context, state) {
             return buildSheetContentView();
           },
-        )
+        ),
       ],
     );
   }
 
+  Widget _buildHideShareView() {
+    return RepaintBoundary(
+      key: controller.shareGlobalKey,
+      child: Obx(() {
+        final day = controller.currentTrackDay.value;
+        final userInfo = controller.userInfo;
+        final shareTrackData = controller.shareTrackData;
+        if (shareTrackData != null && day != null && userInfo != null) {
+          return TrackShareView(day.start, userInfo, shareTrackData);
+        }
+        return SizedBox.shrink();
+      }),
+    );
+  }
+
   Widget buildMapFunView() {
     return Obx(() {
       return Positioned(

+ 31 - 1
lib/resource/assets.gen.dart

@@ -93,6 +93,10 @@ class $AssetsImagesGen {
   AssetGenImage get bgTrackPieChat =>
       const AssetGenImage('assets/images/bg_track_pie_chat.webp');
 
+  /// File path: assets/images/bg_track_report_user.webp
+  AssetGenImage get bgTrackReportUser =>
+      const AssetGenImage('assets/images/bg_track_report_user.webp');
+
   /// File path: assets/images/bg_urgent_contact_add.webp
   AssetGenImage get bgUrgentContactAdd =>
       const AssetGenImage('assets/images/bg_urgent_contact_add.webp');
@@ -529,6 +533,18 @@ class $AssetsImagesGen {
   AssetGenImage get iconNewsReportAgree =>
       const AssetGenImage('assets/images/icon_news_report_agree.webp');
 
+  /// File path: assets/images/icon_share_phone.webp
+  AssetGenImage get iconSharePhone =>
+      const AssetGenImage('assets/images/icon_share_phone.webp');
+
+  /// File path: assets/images/icon_share_stay.webp
+  AssetGenImage get iconShareStay =>
+      const AssetGenImage('assets/images/icon_share_stay.webp');
+
+  /// File path: assets/images/icon_share_track_detail.webp
+  AssetGenImage get iconShareTrackDetail =>
+      const AssetGenImage('assets/images/icon_share_track_detail.webp');
+
   /// File path: assets/images/icon_splash_title.webp
   AssetGenImage get iconSplashTitle =>
       const AssetGenImage('assets/images/icon_splash_title.webp');
@@ -689,6 +705,14 @@ class $AssetsImagesGen {
   AssetGenImage get imgTrackNoMemberTips =>
       const AssetGenImage('assets/images/img_track_no_member_tips.webp');
 
+  /// File path: assets/images/img_track_report_robot.webp
+  AssetGenImage get imgTrackReportRobot =>
+      const AssetGenImage('assets/images/img_track_report_robot.webp');
+
+  /// File path: assets/images/img_track_report_title.webp
+  AssetGenImage get imgTrackReportTitle =>
+      const AssetGenImage('assets/images/img_track_report_title.webp');
+
   /// List of all assets
   List<AssetGenImage> get values => [
         bgAddFriendDialog,
@@ -704,6 +728,7 @@ class $AssetsImagesGen {
         bgPageBackground,
         bgTrackLocationTie,
         bgTrackPieChat,
+        bgTrackReportUser,
         bgUrgentContactAdd,
         bgUrgentContactEmpty,
         bgUrgentContactLogo,
@@ -813,6 +838,9 @@ class $AssetsImagesGen {
         iconNewsItem,
         iconNewsReport,
         iconNewsReportAgree,
+        iconSharePhone,
+        iconShareStay,
+        iconShareTrackDetail,
         iconSplashTitle,
         iconTrackAiInterpretation,
         iconTrackAnalyseRefresh,
@@ -852,7 +880,9 @@ class $AssetsImagesGen {
         imgMemberUserCancelsContainer,
         imgTrackAiAnalyse,
         imgTrackNoData,
-        imgTrackNoMemberTips
+        imgTrackNoMemberTips,
+        imgTrackReportRobot,
+        imgTrackReportTitle
       ];
 }
 

+ 9 - 0
lib/resource/string.gen.dart

@@ -314,6 +314,12 @@ class StringName {
   static String get locationPermissionRefuseDesc => 'location_permission_refuse_desc'.tr; // 我们需要您的定位权限,以提供更准确的位置服务,如果未开启,可能会影响功能使用(如实时定位、轨迹记录等)。
   static String get oneLoginError => 'one_login_error'.tr; // 一键登录失败,请尝试验证码登录
   static String get oneLoginTxt => 'one_login_txt'.tr; // 一键登录
+  static String get trackStayLongestPlace =>
+      'track_stay_longest_place'.tr; // 停留最长地点
+  static String get trackStayShareLogoDesc =>
+      'track_stay_share_logo_desc'.tr; // 为你重要的朋友保驾护航
+  static String get trackStayShareAnalysis =>
+      'track_stay_share_analysis'.tr; // loca分析中,请稍等..
 }
 class StringMultiSource {
   StringMultiSource._();
@@ -631,6 +637,9 @@ class StringMultiSource {
       'location_permission_refuse_desc': '我们需要您的定位权限,以提供更准确的位置服务,如果未开启,可能会影响功能使用(如实时定位、轨迹记录等)。',
       'one_login_error': '一键登录失败,请尝试验证码登录',
       'one_login_txt': '一键登录',
+      'track_stay_longest_place': '停留最长地点',
+      'track_stay_share_logo_desc': '为你重要的朋友保驾护航',
+      'track_stay_share_analysis': 'loca分析中,请稍等..',
     },
   };
 }

+ 27 - 0
lib/utils/date_util.dart

@@ -45,6 +45,33 @@ class DateUtil {
         .add(Duration(days: 1))
         .subtract(Duration(milliseconds: 1));
   }
+
+  static String getDayOfMonth(int timestamp) {
+    final date = DateTime.fromMillisecondsSinceEpoch(timestamp);
+    return date.day.toString().padLeft(2, '0');
+  }
+
+  static String getWeekday(int timestamp) {
+    final date = DateTime.fromMillisecondsSinceEpoch(timestamp);
+    switch (date.weekday) {
+      case 1:
+        return '星期一';
+      case 2:
+        return '星期二';
+      case 3:
+        return '星期三';
+      case 4:
+        return '星期四';
+      case 5:
+        return '星期五';
+      case 6:
+        return '星期六';
+      case 7:
+        return '星期日';
+      default:
+        return '';
+    }
+  }
 }
 
 extension DateTimeExt on DateTime {

+ 63 - 0
lib/widget/dashed_line.dart

@@ -0,0 +1,63 @@
+import 'package:flutter/material.dart';
+
+class DashedLine extends StatelessWidget {
+  final double height;
+  final Color color;
+  final double dashWidth;
+  final double dashSpace;
+
+  const DashedLine({
+    super.key,
+    this.height = 1,
+    this.color = Colors.black,
+    this.dashWidth = 5,
+    this.dashSpace = 3,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return CustomPaint(
+      size: Size(double.infinity, height),
+      painter: _DashedLinePainter(
+        color: color,
+        dashWidth: dashWidth,
+        dashSpace: dashSpace,
+        strokeWidth: height,
+      ),
+    );
+  }
+}
+
+class _DashedLinePainter extends CustomPainter {
+  final Color color;
+  final double dashWidth;
+  final double dashSpace;
+  final double strokeWidth;
+
+  _DashedLinePainter({
+    required this.color,
+    required this.dashWidth,
+    required this.dashSpace,
+    required this.strokeWidth,
+  });
+
+  @override
+  void paint(Canvas canvas, Size size) {
+    final paint = Paint()
+      ..color = color
+      ..strokeWidth = strokeWidth;
+
+    double startX = 0;
+    while (startX < size.width) {
+      canvas.drawLine(
+        Offset(startX, 0),
+        Offset(startX + dashWidth, 0),
+        paint,
+      );
+      startX += dashWidth + dashSpace;
+    }
+  }
+
+  @override
+  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
+}