周睿 2 年之前
父节点
当前提交
76bc9db8ad

+ 2 - 0
app_business/lib/service/map_watch.dart

@@ -13,6 +13,8 @@ class MapWatchImpl extends MapWatch {
   ApiService get _api => Get.find();
   @override
   Future<List<EventOnMap>> getEventList(int mapId) async {
+    // final r = _api.stub
+
     return [];
   }
 }

+ 425 - 0
app_business/lib/view/home/field_control.dart

@@ -0,0 +1,425 @@
+import 'dart:async';
+
+import 'package:app_business/service/abase.dart';
+import 'package:common_pub/ui/map_view/map_view.dart';
+import 'package:common_pub/ui/map_view/view_map_cp.dart';
+import 'package:common_pub/ui/map_view/view_map_image.dart';
+import 'package:common_pub/ui/map_view/view_map_touch.dart';
+import 'package:common_pub/ui/map_view/view_map_trace_tail.dart';
+import 'package:common_pub/ui/map_view/view_map_user_point.dart';
+import 'package:common_pub/ui/map_view/view_plug_loading.dart';
+import 'package:common_pub/utils.dart';
+import 'package:flutter/material.dart';
+import 'package:track_common/generated/assets.dart';
+import 'package:track_common/service/map_watch.dart';
+import 'package:track_common/widget/no_select_map.dart';
+
+class FieldControl extends StatelessWidget {
+  const FieldControl({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return GetBuilder(
+        init: FieldControlController(),
+        builder: (c) {
+          return Container(
+              height: double.infinity,
+              width: double.infinity,
+              color: const Color(0xffc9c0c0),
+              alignment: Alignment.center,
+              child: Obx(() {
+                final mapWatch = c.mapWatch;
+                return mapWatch != null
+                    ? content(context, mapWatch, c)
+                    : const NoSelectMap();
+              }));
+        });
+  }
+
+  Widget content(BuildContext context, MapWatch map, FieldControlController c) {
+    final children = <Widget>[
+      ViewPlugLoading(map.plugMap),
+      ViewMapImage(map.plugMap),
+    ];
+
+    final focusUser = c.focusUser;
+    if (focusUser != null) {
+      children.add(ViewMapCP(
+        // key:UniqueKey(),
+        map.plugMap,
+        cpWantAndHistoryList: focusUser.cpList,
+        isHideRouteBeforeStart: false,
+        controller: c.viewCpController,
+      ));
+    }
+
+    children.addAll([
+      _ViewTrace(map: map, traceDuration: 30.seconds),
+      ViewMapTouch(map.plugMap)
+    ]);
+
+    return Row(
+      children: [
+        Expanded(
+            child: Column(
+          children: [
+            _MapView(),
+            // Expanded(
+            //     child: ViewMapStack(plug: map.plugMap, children: children)),
+            _MsgView(),
+          ],
+        )),
+        _ActiveInfoView()
+      ],
+    );
+  }
+}
+
+class _MapView extends GetView<FieldControlController> {
+  @override
+  Widget build(BuildContext context) {
+    return Expanded(
+        child: ViewMapStack(
+      plug: controller.mapWatch!.plugMap,
+      children: [
+        Obx(() {
+          final map = controller.mapWatch!;
+          final children = <Widget>[
+            ViewPlugLoading(map.plugMap),
+            ViewMapImage(map.plugMap),
+          ];
+
+          final focusUser = controller.focusUser;
+          if (focusUser != null) {
+            children.add(ViewMapCP(
+              // key:UniqueKey(),
+              map.plugMap,
+              cpWantAndHistoryList: focusUser.cpList,
+              isHideRouteBeforeStart: false,
+              controller: controller.viewCpController,
+            ));
+          }
+
+          children.addAll([
+            _ViewTrace(map: map, traceDuration: 30.seconds),
+            ViewMapTouch(map.plugMap)
+          ]);
+          return SizedBox.expand(
+            child: Stack(
+              children: children,
+            ),
+          );
+        })
+      ],
+    ));
+  }
+}
+
+class _ViewTrace extends GetView<FieldControlController> {
+  const _ViewTrace({required this.map, required this.traceDuration});
+
+  final MapWatch map;
+  final Duration traceDuration;
+
+  @override
+  Widget build(BuildContext context) {
+    return Obx(() {
+      final children = <Widget>[];
+
+      for (final act in map.eventList) {
+        for (final user in act.userList) {
+          if (user.isHide.value) {
+            continue;
+          }
+          final trace = user.trace.lastOrNull;
+          final traceTailOnMap = <Offset>[];
+          final st = user.startAt;
+          for (final one in user.trace) {
+            if (DateTime.now().difference(st.add(one.ts)) < traceDuration) {
+              traceTailOnMap.add(one.onMap);
+            }
+          }
+
+          if (trace != null) {
+            children.add(ViewMapTraceTail(
+              plug: map.plugMap,
+              onMapTrace: traceTailOnMap,
+              color: user.flag.value.color,
+            ));
+            children.add(ViewMapUserPoint(
+                key: UniqueKey(),
+                map.plugMap,
+                trace,
+                info: user.name,
+                color: user.flag.value.color));
+          }
+        }
+      }
+
+      return Stack(alignment: Alignment.topLeft, children: children);
+    });
+  }
+}
+
+class _ActiveInfoView extends GetView<FieldControlController> {
+  @override
+  Widget build(BuildContext context) {
+    return Obx(() => Container(
+          width: 370,
+          height: double.infinity,
+          color: Colors.white,
+          child: ListView(
+            children: controller.eventList
+                .map((element) => activeView(element))
+                .toList(),
+          ),
+        ));
+  }
+
+  Widget activeView(EventOnMap info) {
+    final children = <Widget>[
+      Row(children: [
+        Expanded(
+            child: Text(
+          '${info.name} (${info.userList.length}人)',
+          maxLines: 1,
+        )),
+        const SizedBox(
+          width: 8,
+        ),
+        IconButton(
+            onPressed: () {
+              info.isHide.value = !info.isHide.value;
+            },
+            icon: info.isHide.value
+                ? const Icon(Icons.arrow_drop_down)
+                : const Icon(Icons.arrow_drop_up))
+      ]),
+    ];
+
+    if (!info.isHide.value) {
+      children.addAll([
+        Container(
+          decoration: BoxDecoration(
+              color: Colors.white, borderRadius: BorderRadius.circular(5)),
+          padding: const EdgeInsets.fromLTRB(26, 11, 26, 11),
+          child: Row(
+            children: [
+              const Text('广播'),
+              const Spacer(),
+              Image.asset(Assets.imagesIcCp, height: 20, width: 20),
+              Text(' ${info.cpAllCount}'),
+              const Spacer(),
+              const Text('全部隐藏'),
+            ],
+          ),
+        )
+      ]);
+      children
+          .addAll(info.userList.map((e) => _UserInfoView(data: e)).toList());
+    }
+
+    return Container(
+      decoration: BoxDecoration(
+          color: const Color(0xffe0e0e0),
+          borderRadius: BorderRadius.circular(5)),
+      margin: const EdgeInsets.fromLTRB(9, 12, 9, 12),
+      padding: const EdgeInsets.all(9),
+      child: Column(
+        children: children,
+      ),
+    );
+  }
+}
+
+class _UserInfoView extends GetView<FieldControlController> {
+  const _UserInfoView({required this.data});
+
+  final UserOnMap data;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      decoration: BoxDecoration(
+          color: Colors.white, borderRadius: BorderRadius.circular(5)),
+      padding: const EdgeInsets.fromLTRB(7, 11, 7, 11),
+      margin: const EdgeInsets.only(top: 5),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Row(
+            children: [
+              Obx(() => Container(
+                  margin: const EdgeInsets.only(top: 2),
+                  decoration: BoxDecoration(
+                      color: data.flag.value.color,
+                      borderRadius: BorderRadius.circular(4)),
+                  width: 7,
+                  height: 16)),
+              const SizedBox(
+                width: 8,
+              ),
+              Expanded(
+                child: Text.rich(TextSpan(
+                    text: data.name,
+                    children: [TextSpan(text: ' [${data.routeName}]')])),
+              ),
+              GestureDetector(
+                onTap: () {
+                  final oldId = controller.focusUserId.value;
+                  if (oldId != null) {
+                    if (oldId == data.id) {
+                      controller.focusUserId.value = null;
+                      return;
+                    }
+                  }
+                  controller.focusUserId.value = data.id;
+                },
+                child: Obx(() => Icon(
+                      Icons.route,
+                      color: data.id != controller.focusUser?.id
+                          ? Colors.grey
+                          : const Color(0xffffbb77),
+                    )),
+              ),
+              const SizedBox(width: 8),
+              GestureDetector(
+                onTap: () {
+                  data.isHide.value = !data.isHide.value;
+                },
+                child: Obx(() => Icon(
+                      data.isHide.value
+                          ? Icons.visibility_off
+                          : Icons.visibility,
+                      color: data.isHide.value
+                          ? Colors.grey
+                          : const Color(0xffffbb77),
+                    )),
+              )
+            ],
+          ),
+          Container(
+            margin: const EdgeInsets.only(left: 14),
+            child: Row(
+              children: [
+                container(null, cpInfo, Colors.blue),
+                container(
+                    const Icon(
+                      Icons.favorite,
+                      size: 13,
+                      color: Colors.white,
+                    ),
+                    ' ${hr == 0 ? '--' : hr}',
+                    data.heartRatePercent.toHRPColor()),
+                container(null, paceInfo, data.pace.color)
+              ],
+            ),
+          ),
+          const SizedBox(height: 5),
+          Container(
+              margin: const EdgeInsets.only(left: 14),
+              child: Row(
+                mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                children: [
+                  Text('距离 ${data.nextDistance.toString()}'),
+                  Text('时间 ${data.duration.toAppString()}'),
+                  Text('里程 ${data.distance.toString()}'),
+                ],
+              ))
+        ],
+      ),
+    );
+  }
+
+  int get hr {
+    return data.hrInfo.lastOrNull?.hr ?? 0;
+  }
+
+  String get cpInfo {
+    final next = data.nextWant;
+    return next != null ? '${data.nextCPSN}点(${next.areaId})' : '--';
+  }
+
+  String get paceInfo {
+    Duration;
+    return data.pace.toString();
+  }
+
+  Widget container(Widget? icon, String text, Color color) {
+    final children = <Widget>[];
+    if (icon != null) {
+      children.add(icon);
+    }
+    children.add(
+        Text(text, style: const TextStyle(color: Colors.white, fontSize: 14)));
+
+    return Container(
+      height: 20,
+      padding: const EdgeInsets.fromLTRB(10, 0, 12, 0),
+      margin: const EdgeInsets.only(right: 6),
+      alignment: Alignment.center,
+      decoration:
+          BoxDecoration(color: color, borderRadius: BorderRadius.circular(9)),
+      child: Row(
+        children: children,
+      ),
+    );
+  }
+}
+
+class _MsgView extends GetView<FieldControlController> {
+  @override
+  Widget build(BuildContext context) {
+    return Container();
+  }
+}
+
+class FieldControlController extends GetxController {
+  @override
+  void onInit() {
+    super.onInit();
+    final map = mapWatch;
+    if (map != null) {
+      eventList.bindStream(map.eventList.stream);
+    }
+
+    _subActive = eventList.listen((p0) {
+      final user = focusUser;
+      if (user != null) {
+        viewCpController.setCPList(user.cpList);
+      }
+    });
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    _subActive?.cancel();
+  }
+
+  final viewCpController = ViewMapCPController();
+  StreamSubscription<List<EventOnMap>>? _subActive;
+  final _mapService = Get.find<MapWatchService>();
+  MapWatch? get mapWatch => _mapService.instance;
+  final eventList = <EventOnMap>[].obs;
+  final Rx<int?> focusUserId = Rx(null);
+
+  UserOnMap? findFocusUser(List<EventOnMap> list) {
+    if (focusUserId.value == null) {
+      return null;
+    }
+
+    for (final act in list) {
+      for (final user in act.userList) {
+        if (user.id == focusUserId.value) {
+          return user;
+        }
+      }
+    }
+    return null;
+  }
+
+  UserOnMap? get focusUser {
+    return findFocusUser(eventList);
+  }
+}

+ 8 - 0
app_business/lib/view/home/home.dart

@@ -1,3 +1,4 @@
+import 'package:app_business/view/home/field_control.dart';
 import 'package:app_business/view/home/maplist.dart';
 import 'package:get/get.dart';
 import 'package:track_common/view.dart';
@@ -16,5 +17,12 @@ class HomeControllerImpl extends HomeController {
                 builder: (c) {
                   return const MapPage();
                 })),
+        HomeTab(
+            '场控',
+            () => GetBuilder(
+                init: FieldControlController(),
+                builder: (c) {
+                  return const FieldControl();
+                }))
       ];
 }

+ 20 - 0
libs/track_common/lib/widget/no_select_map.dart

@@ -0,0 +1,20 @@
+import 'package:track_common/widget/prelude.dart';
+
+class NoSelectMap extends StatelessWidget {
+  const NoSelectMap({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Center(
+      child: Column(
+        mainAxisSize: MainAxisSize.min,
+        children: [
+          Image.asset(Assets.imagesIcNoData, height: 64, package: package),
+          const SizedBox(height: 25),
+          const Text('没有数据, 请选择地图',
+              style: TextStyle(color: Color(0xff707070), fontSize: 18.5)),
+        ],
+      ),
+    );
+  }
+}

+ 1 - 0
libs/track_common/lib/widget/prelude.dart

@@ -2,5 +2,6 @@ export 'package:flutter/material.dart';
 export 'package:get/get.dart';
 
 export '../generated/assets.dart';
+export 'no_select_map.dart';
 
 const package = 'track_common';