周睿 2 years ago
parent
commit
76d6c4f961

+ 65 - 0
.vscode/launch.json

@@ -0,0 +1,65 @@
+{
+    // 使用 IntelliSense 了解相关属性。 
+    // 悬停以查看现有属性的描述。
+    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "trackoffical_app",
+            "request": "launch",
+            "type": "dart"
+        },
+        {
+            "name": "trackoffical_app (profile mode)",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "profile"
+        },
+        {
+            "name": "trackoffical_app (release mode)",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "release"
+        },
+        {
+            "name": "common_pub",
+            "cwd": "third_party\\common_pub",
+            "request": "launch",
+            "type": "dart"
+        },
+        {
+            "name": "common_pub (profile mode)",
+            "cwd": "third_party\\common_pub",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "profile"
+        },
+        {
+            "name": "common_pub (release mode)",
+            "cwd": "third_party\\common_pub",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "release"
+        },
+        {
+            "name": "example",
+            "cwd": "third_party\\common_pub\\example",
+            "request": "launch",
+            "type": "dart"
+        },
+        {
+            "name": "example (profile mode)",
+            "cwd": "third_party\\common_pub\\example",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "profile"
+        },
+        {
+            "name": "example (release mode)",
+            "cwd": "third_party\\common_pub\\example",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "release"
+        }
+    ]
+}

+ 3 - 0
l10n.yaml

@@ -0,0 +1,3 @@
+arb-dir: lib/l10n
+template-arb-file: app_en.arb
+output-localization-file: app_localizations.dart

+ 3 - 0
lib/l10n/app_en.arb

@@ -0,0 +1,3 @@
+{
+  "helloWorld": "Hello World!"
+}

+ 3 - 0
lib/l10n/app_zh.arb

@@ -0,0 +1,3 @@
+{
+  "helloWorld": "你好,世界!"
+}

+ 3 - 1
lib/main.dart

@@ -3,7 +3,7 @@ import 'package:get/get.dart';
 import 'logger.dart';
 import 'view/init_view.dart';
 import 'styles/theme.dart';
-
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
 
 void main() {
   WidgetsFlutterBinding.ensureInitialized();
@@ -21,6 +21,8 @@ class App extends StatelessWidget{
       title: '小飞龙定向场控',
       theme: appThemeData(),
       darkTheme: appThemeData(),
+      localizationsDelegates: AppLocalizations.localizationsDelegates,
+      supportedLocales: AppLocalizations.supportedLocales,
       home: const InitView(),
     );
   }

+ 1 - 1
lib/view/home/home_view.dart

@@ -49,7 +49,7 @@ final _tabElems = [
   _TabElem(title: '地图', child: const MapPage()),
   _TabElem(title: '场控', child: const FieldControlPage()),
   _TabElem(title: '用户管理', child: const UserManagePage()),
-  _TabElem(title: '个人排名', child: const PersonalRankPage()),
+  _TabElem(title: '个人排名', child: PersonalRankPage()),
   _TabElem(title: '分组排名', child: const SizedBox()),
   _TabElem(title: '数据详情', child: const DataDetailPage()),
 ];

+ 224 - 98
lib/view/home/personal_rank/personal_rank.dart

@@ -1,25 +1,77 @@
+import 'dart:io';
+
+import 'package:application/logger.dart';
+import 'package:application/service/api.dart';
 import 'package:application/service/map_watch.dart';
+import 'package:common_pub/model/distance.dart';
+import 'package:common_pub/model/pace.dart';
 import 'package:common_pub/utils.dart';
+import 'package:fixnum/fixnum.dart';
 import 'package:get/get.dart';
 import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
 import '../../../widget/title_point.dart';
 
 class PersonalRankController extends GetxController {
   @override
   void onInit() {
     super.onInit();
-    final map = MapWatchService.instance;
-    if (map != null) {
-      activeList.bindStream(map.activeList.stream);
+    final now = DateTime.now();
+    filterStartAt.value = DateTime(now.year, now.month, now.day);
+
+    workFlushData();
+  }
+
+  Future<void> workFlushData() async {
+    while (!isClosed) {
+      final map = MapWatchService.instance;
+      if (map == null) {
+        activeList.clear();
+        await 3.seconds.delay();
+        continue;
+      }
+      final startAt = filterStartAt.value.millisecondsSinceEpoch ~/ 1000;
+      try {
+        final r = await ApiService.to.stub.toGameRanking(ToGameRankingRequest(
+            mapId: map.id.toInt(), startSecond: Int64(startAt)));
+        final out = <RankActiveInfo>[];
+
+        for (final actSrc in r.list) {
+          final act = RankActiveInfo()
+            ..id = actSrc.actId
+            ..name = actSrc.actName
+            ..userList = actSrc.rankList.map((e) {
+              final one = e.toModel();
+              final memAct = map.getActiveById(actSrc.actId);
+              if (memAct != null) {
+                final memUser = memAct.getUserById(one.id);
+                if (memUser != null) {
+                  one.flag = memUser.flag.value;
+                }
+              }
+
+              return one;
+            }).toList();
+
+          out.add(act);
+        }
+
+        activeList.value = out;
+      } catch (_) {}
+
+      await 3.seconds.delay();
     }
   }
 
-  final activeList = <ActiveInfo>[].obs;
-  final Rx<ActiveInfo?> selectActive = Rx(null);
+  final filterStartAt = DateTime.now().obs;
+  final activeList = <RankActiveInfo>[].obs;
+  final Rx<RankActiveInfo?> selectActive = Rx(null);
 }
 
 class PersonalRankPage extends StatelessWidget {
-  const PersonalRankPage({super.key});
+  PersonalRankPage({super.key});
+
+  final dateFmt = DateFormat('yyyy-MM-dd');
 
   @override
   Widget build(BuildContext context) {
@@ -50,6 +102,21 @@ class PersonalRankPage extends StatelessWidget {
         });
   }
 
+  Future<void> _pickDate(BuildContext context, PersonalRankController c) async {
+    final now = DateTime.now();
+
+    final date = await showDatePicker(
+        context: context,
+        initialDate: c.filterStartAt.value,
+        firstDate: DateTime(now.year - 1),
+        lastDate: DateTime(now.year, now.month, now.day + 1),
+    );
+
+    if (date != null) {
+      c.filterStartAt.value = DateTime(date.year, date.month, date.day);
+    }
+  }
+
   Widget titlePoint() {
     return const TitlePoint(color: Color(0xff98d8ff));
   }
@@ -67,7 +134,9 @@ class PersonalRankPage extends StatelessWidget {
             Container(
               decoration: BoxDecoration(
                   border: Border.all(color: const Color(0xffe3e3e3))),
-              child: const Text('2023-06-26'),
+              child: TextButton(
+                  onPressed: () => _pickDate(context, c),
+                  child: Text(dateFmt.format(c.filterStartAt.value))),
             )
           ],
         ),
@@ -90,7 +159,7 @@ class PersonalRankPage extends StatelessWidget {
     );
   }
 
-  Widget wActiveCard(BuildContext context, ActiveInfo active, bool selected,
+  Widget wActiveCard(BuildContext context, RankActiveInfo active, bool selected,
       VoidCallback onTap) {
     return GestureDetector(
         onTap: onTap,
@@ -110,8 +179,8 @@ class PersonalRankPage extends StatelessWidget {
                 width: 6.4,
                 color: selected ? const Color(0xffff870d) : Colors.transparent,
               ),
-              Text(active.name),
-              const Spacer(),
+              Expanded(child: Text(active.name)),
+              const SizedBox(width: 8),
               Text(active.userList.length.toString())
             ],
           ),
@@ -125,7 +194,7 @@ class PersonalRankPage extends StatelessWidget {
       return const SizedBox();
     }
 
-    final userList = c.selectActive.value?.userList ?? <UserInfo>[];
+    final userList = c.selectActive.value?.userList ?? <RankUserInfo>[];
 
     return Column(
       children: [
@@ -176,18 +245,20 @@ class PersonalRankPage extends StatelessWidget {
             verticalDivider(show: false),
             const Expanded(child: Text('路线ID', textAlign: TextAlign.center)),
             verticalDivider(show: false),
+            const SizedBox(
+                width: _userResultWidth,
+                child: Text('成绩', textAlign: TextAlign.center)),
+            verticalDivider(show: false),
             const SizedBox(
                 width: _userTimeWidth,
                 child: Text('总时间', textAlign: TextAlign.center)),
             verticalDivider(show: false),
-            const Expanded(
-                child: Text('总里程', textAlign: TextAlign.center)),
+            const Expanded(child: Text('总里程', textAlign: TextAlign.center)),
             verticalDivider(show: false),
-            const Expanded(
-                child: Text('配速', textAlign: TextAlign.center)),
+            const Expanded(child: Text('配速', textAlign: TextAlign.center)),
             verticalDivider(show: false),
             const SizedBox(
-                width: _userResultWidth,
+                width: _userFlagWidth,
                 child: Text('分组', textAlign: TextAlign.center)),
             verticalDivider(show: false),
           ],
@@ -195,104 +266,159 @@ class PersonalRankPage extends StatelessWidget {
   }
 
   Widget eUserCard(BuildContext context, PersonalRankController c, int index,
-      UserInfo data) {
-    var startAt = '--';
-    if (data.startAt != null) {
-      startAt = data.startAt!.toIso8601String();
-    }
-
+      RankUserInfo data) {
     return DefaultTextStyle(
         style: context.textTheme.bodyMedium!.copyWith(color: Colors.white),
         child: Container(
-          height: _cardHeight,
-          width: double.infinity,
-          margin: const EdgeInsets.only(top: 3),
-          child: Row(children: [
-              Container(
-                  width: _userIndexWidth,
-                  height:double.infinity,
+            height: _cardHeight,
+            width: double.infinity,
+            margin: const EdgeInsets.only(top: 3),
+            child: Row(
+              children: [
+                Container(
+                    width: _userIndexWidth,
+                    height: double.infinity,
+                    decoration: const BoxDecoration(
+                        color: Color(0xffff870d),
+                        borderRadius: BorderRadius.only(
+                            topLeft: Radius.circular(6),
+                            bottomLeft: Radius.circular(6))),
+                    alignment: Alignment.center,
+                    child: Text(
+                      index.toString(),
+                      textAlign: TextAlign.center,
+                      style: const TextStyle(
+                          fontSize: 34,
+                          fontWeight: FontWeight.w700,
+                          fontStyle: FontStyle.italic),
+                    )),
+                const SizedBox(width: 4),
+                Expanded(
+                    child: Container(
+                  height: double.infinity,
                   decoration: const BoxDecoration(
-                    color: Color(0xffff870d),
-                    borderRadius: BorderRadius.only(topLeft: Radius.circular(6), bottomLeft: Radius.circular(6))
-                  ),
-                  alignment: Alignment.center,
-                  child: Text(
-                    index.toString(),
-                    textAlign: TextAlign.center,
-                    style: const TextStyle(fontSize: 34, fontWeight: FontWeight.w700, fontStyle: FontStyle.italic),
-                  )),
-              const SizedBox(width: 4),
-              Expanded(child: Container(
-                height: double.infinity,
-                decoration: const BoxDecoration(
-                    color: Color(0xff003656),
-                    borderRadius: BorderRadius.only(topRight: Radius.circular(6), bottomRight: Radius.circular(6))
-                ),
-                child: Row(
-                  children: [
-                    SizedBox(
-                        width: _userNameWidth,
-                        child: Text(
-                          data.name,
-                          textAlign: TextAlign.center,
-                        )),
-                    verticalDivider(),
-                    Expanded(
-                        child: Text(
-                          data.routeName,
-                          textAlign: TextAlign.center,
-                        )),
-                    verticalDivider(),
-                    SizedBox(
-                        width: _userTimeWidth,
-                        child: Text(
-                          data.duration.toMinSecondString(),
-                          textAlign: TextAlign.center,
-                        )),
-                    verticalDivider(),
-                    Expanded(
-
-                        child: Text(
-                          data.distance.toString(),
-                          textAlign: TextAlign.center,
-                        )),
-                    verticalDivider(),
-                    Expanded(
-                        child: Container(
-                          margin: const EdgeInsets.only(left: 8, right: 8),
-                          alignment: Alignment.center,
-                          height: 18,
-                          decoration: BoxDecoration(color: data.pace.color, borderRadius: BorderRadius.circular(9)),
+                      color: Color(0xff003656),
+                      borderRadius: BorderRadius.only(
+                          topRight: Radius.circular(6),
+                          bottomRight: Radius.circular(6))),
+                  child: Row(
+                    children: [
+                      SizedBox(
+                          width: _userNameWidth,
                           child: Text(
-                            data.pace.toString(),
+                            data.name,
                             textAlign: TextAlign.center,
-                          )
-                        ) ),
-                    verticalDivider(),
-                    Container(
-                      alignment: Alignment.center,
-                        width: _userResultWidth,
-                        child: Icon(Icons.flag, color: data.flag.value.color)),
-                  ],
-                ),
-              ))
-            ],
-          )
-        ));
+                          )),
+                      verticalDivider(),
+                      Expanded(
+                          child: Text(
+                        data.routeName,
+                        textAlign: TextAlign.center,
+                      )),
+                      verticalDivider(),
+                      SizedBox(
+                          width: _userResultWidth,
+                          child: Text(
+                            switch(data.state){
+                              GameState.processing=>'进行中',
+                              GameState.finish=>'完赛',
+                            GameState.unFinish=>'退赛'
+                            },
+                            textAlign: TextAlign.center,
+                          )),
+                      verticalDivider(),
+                      SizedBox(
+                          width: _userTimeWidth,
+                          child: Text(
+                            data.duration.toMinSecondString(),
+                            textAlign: TextAlign.center,
+                          )),
+                      verticalDivider(),
+                      Expanded(
+                          child: Text(
+                        data.distance.toString(),
+                        textAlign: TextAlign.center,
+                      )),
+                      verticalDivider(),
+                      Expanded(
+                          child: Container(
+                              margin: const EdgeInsets.only(left: 8, right: 8),
+                              alignment: Alignment.center,
+                              height: 18,
+                              decoration: BoxDecoration(
+                                  color: data.pace.color,
+                                  borderRadius: BorderRadius.circular(9)),
+                              child: Text(
+                                data.pace.toString(),
+                                textAlign: TextAlign.center,
+                              ))),
+                      verticalDivider(),
+                      Container(
+                          alignment: Alignment.center,
+                          width: _userFlagWidth,
+                          child: Icon(Icons.flag, color: data.flag.color)),
+                    ],
+                  ),
+                ))
+              ],
+            )));
   }
 
-  Widget verticalDivider({bool show = true}){
+  Widget verticalDivider({bool show = true}) {
     return VerticalDivider(
       width: 3,
       indent: 14,
       endIndent: 14,
-      color: show? Colors.white: Colors.transparent,
+      color: show ? Colors.white : Colors.transparent,
     );
   }
 
   static const _userIndexWidth = 56.0;
   static const _userNameWidth = 84.0;
-  static const _userResultWidth = 52.0;
-  static const _userTimeWidth = 62.0;
+  static const _userResultWidth = 62.0;
+  static const _userTimeWidth = 92.0;
   static const _cardHeight = 45.0;
+  static const _userFlagWidth = 52.0;
+}
+
+enum GameState {
+  processing,
+  finish,
+  unFinish,
+}
+
+class RankUserInfo {
+  var id = 0;
+  var name = '';
+  var routeName = '';
+  var state = GameState.processing;
+  var duration = 0.seconds;
+  var distance = 0.meter;
+  var startAt = DateTime(2000);
+
+  Pace get pace => Pace(distance, duration);
+  var flag = Flag.red;
+}
+
+class RankActiveInfo {
+  var id = 0;
+  var name = '';
+  var userList = <RankUserInfo>[];
+}
+
+extension UserRankInfoExt on ToOrienteerRankInfo {
+  RankUserInfo toModel() {
+    return RankUserInfo()
+      ..id = oId
+      ..name = oName
+      ..routeName = courseName
+      ..state = switch (state) {
+        1 => GameState.finish,
+        2 => GameState.processing,
+        _ => GameState.unFinish
+      }
+      ..startAt = startAt.toModel()
+      ..duration = duration.toModel()
+      ..distance = distance.meter;
+  }
 }

+ 18 - 9
lib/view/login/login_view.dart

@@ -39,11 +39,12 @@ class _AnimationPageState extends State<AnimationPage>
 
   @override
   Widget build(BuildContext context) {
-    return wPageLeft();
+    return wPageLeft(context);
   }
 
-  Widget wPageLeft() {
+  Widget wPageLeft(BuildContext context) {
     return Container(
+      padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
       decoration: const BoxDecoration(
           image: DecorationImage(
               image: AssetImage(Assets.imagesBkCommonPage), fit: BoxFit.cover)),
@@ -241,9 +242,16 @@ class LoginView extends GetView<LoginController> {
   }
 
   Widget wPageRight(BuildContext context) {
+    var inputHeight = context.height * 0.45;
+    const inputMinHeight = 260.0;
+    if(inputHeight < inputMinHeight){
+      inputHeight= inputMinHeight;
+    }
+
+
     return Container(
-        margin: EdgeInsets.fromLTRB(context.wp(6), context.wp(5.6), context.wp(6), context.wp(2)),
-        padding: const EdgeInsets.all(10.0),
+        // margin: EdgeInsets.fromLTRB(context.wp(6), context.wp(5.6), context.wp(6), context.wp(2)),
+        padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
         decoration: const BoxDecoration(
           // color: Color(0xffffcb00),
           image: DecorationImage(
@@ -255,17 +263,18 @@ class LoginView extends GetView<LoginController> {
           mainAxisAlignment: MainAxisAlignment.center,
           crossAxisAlignment: CrossAxisAlignment.center,
           children: [
-            const Text(
+            const Expanded(child: Text(
               '定向运动活动现场监控系统',
               textAlign: TextAlign.center,
               style: TextStyle(fontSize: 22.0, fontWeight: FontWeight.bold),
-            ),
+            )),
+            // SizedBox(height: inputHeight),
+            // Expanded(child: wInput(context)),
             SizedBox(
-                height: context.height * 0.45,
+                height: inputHeight,
                 child: wInput(context)
             ),
-            SizedBox(
-                height: context.wp(3),
+            Expanded(
                 child: Image.asset(Assets.imagesIcLoginLogo)
             ),
             // Expanded(flex: 10,child:  wExtra()),

+ 13 - 0
pubspec.lock

@@ -181,6 +181,11 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.3"
+  flutter_localizations:
+    dependency: "direct main"
+    description: flutter
+    source: sdk
+    version: "0.0.0"
   flutter_rust_bridge:
     dependency: transitive
     description:
@@ -263,6 +268,14 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "4.0.2"
+  intl:
+    dependency: "direct main"
+    description:
+      name: intl
+      sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.18.1"
   js:
     dependency: transitive
     description:

+ 5 - 2
pubspec.yaml

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 # In Windows, build-name is used as the major, minor, and patch parts
 # of the product and file versions while build-number is used as the build suffix.
-version: 1.0.6+1
+version: 1.0.8+1
 
 environment:
   sdk: '>=3.0.6 <4.0.0'
@@ -49,6 +49,9 @@ dependencies:
   f_cache: ^3.1.1
   http: ^1.1.0
   fixnum: any
+  flutter_localizations:
+    sdk: flutter
+  intl: any
 
 
 dev_dependencies:
@@ -72,7 +75,7 @@ flutter:
   # included with your application, so that you can use the icons in
   # the material Icons class.
   uses-material-design: true
-
+  generate: true
   # To add assets to your application, add an assets section, like this:
   # assets:
   #   - images/a_dot_burr.jpeg

+ 1 - 1
third_party/common_pub

@@ -1 +1 @@
-Subproject commit f1bd8a9cb6b440783815157eea4246ef5b93d676
+Subproject commit 792adf67085955778feba0b7fb853250c0825439