فهرست منبع

[new]增加谈话原文显示

zk 1 سال پیش
والد
کامیت
2667b718c6

BIN
assets/images/icon_talk_audio_pause.webp


BIN
assets/images/icon_talk_audio_playing.webp


BIN
assets/images/icon_talk_logo.webp


+ 1 - 1
lib/data/api/request/talk_request.dart

@@ -6,7 +6,7 @@ part 'talk_request.g.dart';
 @JsonSerializable()
 class TalkRequest extends AppBaseRequest {
   @JsonKey(name: 'talkId')
-  String talkId;
+  String? talkId;
 
   TalkRequest(this.talkId);
 

+ 4 - 2
lib/data/bean/talks.dart

@@ -57,8 +57,10 @@ class TalkBean {
 class TalkStatus {
   TalkStatus._();
 
-  static int notAnalysis = 0;
-  static int analysing = 1;
+  //(0等待生成 1生成中,都处于生成中)  2成功 3失败 4未分析
+  static int analysing = 0;
+  static int waitAnalysis = 1;
   static int analysisSuccess = 2;
   static int analysisFail = 3;
+  static int notAnalysis = 4;
 }

+ 15 - 0
lib/data/repositories/talk_repository.dart

@@ -7,10 +7,25 @@ import '../api/request/talk_rename_request.dart';
 import '../api/request/talk_request.dart';
 import '../api/response/talk_check_electric_response.dart';
 import '../api/response/talk_detail_response.dart';
+import '../api/response/talk_original_response.dart';
+import '../bean/talk_original.dart';
 
 class TalkRepository {
   TalkRepository._();
 
+  Future<List<TalkOriginal>> talkOriginal(String? talkId) {
+    return atmobApi
+        .talkOriginal(TalkRequest(talkId))
+        .then(HttpHandler.handle(false))
+        .then((data) {
+      if (data.list != null) {
+        return data.list!;
+      } else {
+        return [];
+      }
+    });
+  }
+
   // duration 音频时长,单位为秒
   Future<TalkCheckElectricResponse> checkElectric(double duration) {
     return atmobApi

+ 28 - 13
lib/module/talk/controller.dart

@@ -1,14 +1,12 @@
+import 'dart:async';
+
 import 'package:electronic_assistant/base/base_controller.dart';
 import 'package:electronic_assistant/data/repositories/talk_repository.dart';
 import 'package:electronic_assistant/module/talk/summary/view.dart';
 import 'package:electronic_assistant/module/talk/todo/view.dart';
 import 'package:electronic_assistant/resource/string.gen.dart';
-import 'package:electronic_assistant/router/app_pages.dart';
 import 'package:electronic_assistant/utils/toast_util.dart';
-import 'package:flutter/cupertino.dart';
 import 'package:get/get.dart';
-import 'package:get/get_core/src/get_main.dart';
-
 import '../../data/bean/talks.dart';
 import 'original/view.dart';
 
@@ -17,31 +15,39 @@ class TalkController extends BaseController {
 
   final analyseStatus = TalkStatus.notAnalysis.obs;
 
+  // final isOriginalAnalysed = false.obs;
   final isShowElectricLow = false.obs;
 
+  final isAudioPlaying = false.obs;
+
+  final audioProgressValue = 0.0.obs;
+
   final List<String> tabBeans = [
     StringName.talkTabSummary.tr,
     StringName.talkTabMyTask.tr,
     StringName.talkTabOriginal.tr
   ];
 
-  final pages = [SummaryView(), TodoView(), OriginalView()];
+  StreamSubscription<TalkBean>? _talkBeanListener;
 
-  @override
-  void onInit() {
-    super.onInit();
-    getArguments();
-  }
+  final pages = [const SummaryView(), const TodoView(), const OriginalView()];
 
   @override
   void onReady() {
     super.onReady();
+    _initListener();
+    _getArguments();
+  }
+
+  void _initListener() {
+    _talkBeanListener = talkBean.listen((bean) {
+      analyseStatus.value = bean.status ?? TalkStatus.notAnalysis;
+    });
   }
 
-  void getArguments() {
+  void _getArguments() {
     if (Get.arguments is TalkBean) {
       talkBean.value = Get.arguments as TalkBean;
-      analyseStatus.value = talkBean.value.status ?? TalkStatus.notAnalysis;
     }
   }
 
@@ -69,6 +75,15 @@ class TalkController extends BaseController {
 
   void goElectricStore() {
     //TODO 跳转至商店页
-    Get.toNamed(RoutePath.login);
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    _talkBeanListener?.cancel();
+  }
+
+  void updateProgress(double value) {
+    audioProgressValue.value = value;
   }
 }

+ 22 - 1
lib/module/talk/original/controller.dart

@@ -1,3 +1,24 @@
 import 'package:electronic_assistant/base/base_controller.dart';
+import 'package:electronic_assistant/data/repositories/talk_repository.dart';
+import 'package:get/get.dart';
 
-class OriginalController extends BaseController {}
+import '../../../data/bean/talk_original.dart';
+import '../controller.dart';
+
+class OriginalController extends BaseController {
+  TalkController talkController = Get.find();
+
+  final originalList = <TalkOriginal>[].obs;
+
+  @override
+  void onReady() {
+    super.onReady();
+    requestOriginal();
+  }
+
+  void requestOriginal() {
+    talkRepository.talkOriginal(talkController.talkBean.value.id).then((value) {
+      originalList.value = value;
+    });
+  }
+}

+ 79 - 1
lib/module/talk/original/view.dart

@@ -1,6 +1,11 @@
 import 'package:electronic_assistant/base/base_page.dart';
+import 'package:electronic_assistant/resource/colors.gen.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
 
+import '../../../data/bean/talk_original.dart';
+import '../status_view.dart';
 import 'controller.dart';
 
 class OriginalView extends BasePage<OriginalController> {
@@ -8,6 +13,79 @@ class OriginalView extends BasePage<OriginalController> {
 
   @override
   Widget buildBody(BuildContext context) {
-    return Container();
+    return buildOriginalContentView();
+  }
+
+  Widget buildOriginalContentView() {
+    return Obx(() {
+      if (controller.originalList.isEmpty) {
+        return getTalkLoadingView();
+      } else {
+        return ListView.builder(
+          padding: EdgeInsets.only(bottom: 70.h),
+          itemBuilder: _buildOriginalItem,
+          itemCount: controller.originalList.length,
+        );
+      }
+    });
+  }
+
+  Widget _buildOriginalItem(BuildContext context, int index) {
+    TalkOriginal item = controller.originalList[index];
+    return Padding(
+      padding:
+          EdgeInsets.only(left: 12.w, right: 12.w, top: 11.h, bottom: 13.h),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Row(
+            children: [
+              Container(
+                decoration: const BoxDecoration(
+                  color: ColorName.colorPrimary,
+                  shape: BoxShape.circle,
+                ),
+                width: 20.w,
+                height: 20.w,
+                child: Center(
+                  child: Text(
+                    item.speakerId.toString(),
+                    style: TextStyle(fontSize: 12.sp, color: Colors.white),
+                  ),
+                ),
+              ),
+              SizedBox(width: 6.w),
+              Text(item.speaker.toString(),
+                  style: TextStyle(
+                      fontSize: 14.sp, color: ColorName.secondaryTextColor)),
+              SizedBox(width: 4.w),
+              Text(formatMilliseconds(item.startMs),
+                  style: TextStyle(
+                      fontSize: 12.sp, color: ColorName.tertiaryTextColor)),
+            ],
+          ),
+          SizedBox(height: 12.h),
+          Text(item.sentence.toString(),
+              style: TextStyle(
+                  fontSize: 14.sp, color: ColorName.primaryTextColor)),
+        ],
+      ),
+    );
+  }
+
+  String formatMilliseconds(int? totalMilliseconds) {
+    if (totalMilliseconds == null) {
+      return '';
+    }
+    int totalSeconds = (totalMilliseconds / 1000).round();
+    int hours = totalSeconds ~/ 3600;
+    int minutes = (totalSeconds % 3600) ~/ 60;
+    int seconds = totalSeconds % 60;
+
+    if (hours > 0) {
+      return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
+    } else {
+      return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
+    }
   }
 }

+ 26 - 0
lib/module/talk/status_view.dart

@@ -0,0 +1,26 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+
+import '../../resource/assets.gen.dart';
+import '../../resource/colors.gen.dart';
+import '../../resource/string.gen.dart';
+
+Widget getTalkLoadingView() {
+  return SizedBox(
+    width: double.infinity,
+    child: Column(
+      children: [
+        SizedBox(height: 138.h),
+        SizedBox(
+            width: 100.w,
+            height: 100.w,
+            child: Assets.anim.talkAnalyse.image()),
+        SizedBox(height: 12.h),
+        Text(StringName.talkAnalyzing.tr,
+            style:
+                TextStyle(fontSize: 14.sp, color: ColorName.secondaryTextColor))
+      ],
+    ),
+  );
+}

+ 2 - 14
lib/module/talk/summary/view.dart

@@ -11,20 +11,8 @@ class SummaryView extends BasePage<SummaryController> {
 
   @override
   Widget buildBody(BuildContext context) {
-    return Stack(
-      children: [
-        SizedBox(
-          width: double.infinity,
-          child: Column(
-            children: [
-              SizedBox(height: 12.h),
-              Text(StringName.talkAnalyzing.tr,
-                  style: TextStyle(
-                      fontSize: 14.sp, color: ColorName.secondaryTextColor))
-            ],
-          ),
-        )
-      ],
+    return Container(
+      child: Text('谈话总结'),
     );
   }
 }

+ 3 - 1
lib/module/talk/todo/view.dart

@@ -8,6 +8,8 @@ class TodoView extends BasePage<TodoController> {
 
   @override
   Widget buildBody(BuildContext context) {
-    return Container();
+    return Container(
+      child: Text('todo页面'),
+    );
   }
 }

+ 231 - 78
lib/module/talk/view.dart

@@ -1,18 +1,12 @@
 import 'package:electronic_assistant/base/base_page.dart';
 import 'package:electronic_assistant/module/talk/controller.dart';
-import 'package:electronic_assistant/module/talk/original/view.dart';
-import 'package:electronic_assistant/module/talk/summary/view.dart';
-import 'package:electronic_assistant/module/talk/todo/view.dart';
 import 'package:electronic_assistant/resource/colors.gen.dart';
 import 'package:electronic_assistant/utils/expand.dart';
 import 'package:electronic_assistant/utils/fixed_size_tab_indicator.dart';
-import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:get/get.dart';
-import 'package:get/get_core/src/get_main.dart';
-
 import '../../data/bean/talks.dart';
 import '../../resource/assets.gen.dart';
 import '../../resource/string.gen.dart';
@@ -20,7 +14,7 @@ import '../../router/app_pages.dart';
 import '../../utils/common_style.dart';
 
 class TalkPage extends BasePage<TalkController> {
-  TalkPage({super.key});
+  const TalkPage({super.key});
 
   static void start(TalkBean item) {
     Get.toNamed(RoutePath.talkDetail, arguments: item);
@@ -28,13 +22,14 @@ class TalkPage extends BasePage<TalkController> {
 
   @override
   Widget buildBody(BuildContext context) {
-    return Stack(
-      children: [
-        buildTopGradient(),
-        Column(
-          crossAxisAlignment: CrossAxisAlignment.start,
-          children: [
-            AppBar(
+    return DefaultTabController(
+      length: controller.tabBeans.length,
+      child: Stack(
+        children: [
+          buildTopGradient(),
+          Scaffold(
+            backgroundColor: Colors.transparent,
+            appBar: AppBar(
               systemOverlayStyle: SystemUiOverlayStyle.dark,
               backgroundColor: Colors.transparent,
               leading: IconButton(
@@ -48,68 +43,69 @@ class TalkPage extends BasePage<TalkController> {
                 },
               ),
             ),
-            Padding(
-              padding: EdgeInsets.symmetric(horizontal: 12.w),
-              child: Column(
-                crossAxisAlignment: CrossAxisAlignment.start,
-                children: [
-                  SizedBox(height: 8.h),
-                  Text(controller.talkBean.value.title.orEmpty,
-                      style: TextStyle(
-                          fontSize: 22.sp,
-                          fontWeight: FontWeight.bold,
-                          color: ColorName.primaryTextColor)),
-                  SizedBox(height: 4.h),
-                  Text(controller.talkBean.value.createTime.orEmpty,
-                      style: TextStyle(
-                          fontSize: 12.sp,
-                          color: ColorName.secondaryTextColor)),
-                  SizedBox(height: 14.h),
-                ],
-              ),
-            ),
-            Expanded(
-                child: DefaultTabController(
-                    length: controller.tabBeans.length,
-                    child: Column(
+            body: Column(
+              children: [
+                Padding(
+                  padding: EdgeInsets.symmetric(horizontal: 12.w),
+                  child: Obx(() {
+                    return Column(
+                      crossAxisAlignment: CrossAxisAlignment.start,
                       children: [
-                        Container(
-                          decoration: const BoxDecoration(
-                              color: Colors.white,
-                              borderRadius: BorderRadius.only(
-                                topLeft: Radius.circular(12),
-                                topRight: Radius.circular(12),
-                              )),
-                          child: TabBar(
-                              labelStyle: TextStyle(
-                                  fontSize: 16.sp, fontWeight: FontWeight.bold),
-                              unselectedLabelStyle: TextStyle(fontSize: 14.sp),
-                              labelColor: ColorName.primaryTextColor,
-                              unselectedLabelColor:
-                                  ColorName.secondaryTextColor,
-                              labelPadding: EdgeInsets.only(top: 4.h),
-                              dividerHeight: 0,
-                              indicator: FixedSizeTabIndicator(
-                                  width: 16.w,
-                                  height: 3.w,
-                                  radius: 3,
-                                  color: ColorName.colorPrimary),
-                              tabs: controller.tabBeans
-                                  .map((txt) => Tab(text: txt))
-                                  .toList()),
-                        ),
-                        Divider(
-                            height: 1,
-                            color: const Color(0xFFf6f6f6),
-                            indent: 12.w,
-                            endIndent: 12.w),
                         SizedBox(height: 8.h),
-                        buildTalkContentView()
+                        Text(controller.talkBean.value.title.orEmpty,
+                            style: TextStyle(
+                                fontSize: 22.sp,
+                                fontWeight: FontWeight.bold,
+                                color: ColorName.primaryTextColor)),
+                        SizedBox(height: 4.h),
+                        Text(controller.talkBean.value.createTime.orEmpty,
+                            style: TextStyle(
+                                fontSize: 12.sp,
+                                color: ColorName.secondaryTextColor)),
+                        SizedBox(height: 14.h),
                       ],
-                    )))
-          ],
-        )
-      ],
+                    );
+                  }),
+                ),
+                buildTabBar(),
+                Divider(
+                    height: 1,
+                    color: const Color(0xFFf6f6f6),
+                    indent: 12.w,
+                    endIndent: 12.w),
+                SizedBox(height: 8.h),
+                buildTalkContentView()
+              ],
+            ),
+          ),
+          buildBottomView(),
+          buildAIAnalysisView()
+        ],
+      ),
+    );
+  }
+
+  Container buildTabBar() {
+    return Container(
+      decoration: const BoxDecoration(
+          color: Colors.white,
+          borderRadius: BorderRadius.only(
+            topLeft: Radius.circular(12),
+            topRight: Radius.circular(12),
+          )),
+      child: TabBar(
+          labelStyle: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
+          unselectedLabelStyle: TextStyle(fontSize: 14.sp),
+          labelColor: ColorName.primaryTextColor,
+          unselectedLabelColor: ColorName.secondaryTextColor,
+          labelPadding: EdgeInsets.only(top: 4.h),
+          dividerHeight: 0,
+          indicator: FixedSizeTabIndicator(
+              width: 16.w,
+              height: 3.w,
+              radius: 3,
+              color: ColorName.colorPrimary),
+          tabs: controller.tabBeans.map((txt) => Tab(text: txt)).toList()),
     );
   }
 
@@ -141,15 +137,19 @@ class TalkPage extends BasePage<TalkController> {
           return buildNotAnalysisView();
         }
       } else {
-        return Expanded(
-          child: TabBarView(
-            children: controller.pages,
-          ),
-        );
+        return buildTabContentView();
       }
     });
   }
 
+  Widget buildTabContentView() {
+    return Expanded(
+      child: TabBarView(
+        children: controller.pages,
+      ),
+    );
+  }
+
   Widget buildNotAnalysisView() {
     return SizedBox(
       width: double.infinity,
@@ -235,4 +235,157 @@ class TalkPage extends BasePage<TalkController> {
       ),
     );
   }
+
+  Widget buildBottomView() {
+    return Align(
+      alignment: Alignment.bottomCenter,
+      child: Container(
+        margin: EdgeInsets.only(bottom: 20.h), // 设置底部偏移距离
+        child: IntrinsicHeight(
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.end,
+            children: [
+              Container(
+                  margin: EdgeInsets.only(right: 8.w),
+                  width: 64.w,
+                  height: 64.w,
+                  child: Assets.images.iconTalkLogo.image()),
+              SizedBox(height: 24.h),
+              buildAudioView()
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  buildAIAnalysisView() {
+    return Container();
+  }
+
+  buildAudioView() {
+    return Container(
+      decoration: BoxDecoration(
+        color: Colors.white,
+        borderRadius: BorderRadius.circular(100),
+        boxShadow: [
+          BoxShadow(
+            color: ColorName.black5.withOpacity(0.1),
+            spreadRadius: 2,
+            blurRadius: 12,
+            offset: const Offset(0, 3),
+          ),
+        ],
+      ),
+      margin: EdgeInsets.symmetric(horizontal: 12.w),
+      padding: EdgeInsets.symmetric(vertical: 6.w),
+      child: Row(
+        children: [
+          SizedBox(width: 9.w),
+          GestureDetector(
+            onTap: () {
+              // controller.playAudio();
+            },
+            child: Obx(() {
+              return SizedBox(
+                  width: 36.w,
+                  height: 36.w,
+                  child: (controller.isAudioPlaying.value)
+                      ? Assets.images.iconTalkAudioPlaying.image()
+                      : Assets.images.iconTalkAudioPause.image());
+            }),
+          ),
+          SizedBox(width: 15.w),
+          Builder(builder: (context) {
+            return SizedBox(
+              width: 226.w,
+              height: 18.w,
+              child: Obx(() {
+                return SliderTheme(
+                  data: SliderTheme.of(context).copyWith(
+                    thumbColor: Colors.white,
+                    overlayShape: SliderComponentShape.noOverlay,
+                    trackHeight: 8,
+                    activeTrackColor: "#8A89E9".toColor(),
+                    inactiveTrackColor: "#F6F5F8".toColor(),
+                    trackShape: CustomTrackShape(),
+                  ),
+                  child: Slider(
+                    value: controller.audioProgressValue.value,
+                    min: 0.0,
+                    max: 1.0,
+                    onChanged: (value) {
+                      controller.updateProgress(value);
+                    },
+                  ),
+                );
+              }),
+            );
+          }),
+          SizedBox(width: 11.w),
+          Text('3:21',
+              style: TextStyle(
+                  fontSize: 10.sp, color: ColorName.secondaryTextColor))
+        ],
+      ),
+    );
+  }
+}
+
+class CustomTrackShape extends RoundedRectSliderTrackShape {
+  @override
+  Rect getPreferredRect({
+    required RenderBox parentBox,
+    Offset offset = Offset.zero,
+    required SliderThemeData sliderTheme,
+    bool isEnabled = false,
+    bool isDiscrete = false,
+  }) {
+    final trackHeight = sliderTheme.trackHeight;
+    final trackLeft = offset.dx;
+    final trackTop = offset.dy + (parentBox.size.height - trackHeight!) / 2;
+    final trackWidth = parentBox.size.width;
+    return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
+  }
 }
+
+// class EnhancedShadowSliderThumbShape extends RoundSliderThumbShape {
+//   final double thumbRadius;
+//   final double elevation;
+//   final double shadowOffsetY;
+//
+//   EnhancedShadowSliderThumbShape({
+//     required this.thumbRadius,
+//     this.elevation = 8.0,
+//     this.shadowOffsetY = 0, // 阴影向下偏移量
+//   });
+//
+//   @override
+//   void paint(
+//       PaintingContext context,
+//       Offset center, {
+//         required Animation<double> activationAnimation,
+//         required Animation<double> enableAnimation,
+//         required bool isDiscrete,
+//         required TextPainter labelPainter,
+//         required RenderBox parentBox,
+//         required SliderThemeData sliderTheme,
+//         required TextDirection textDirection,
+//         required double value,
+//         required double textScaleFactor,
+//         required Size sizeWithOverflow,
+//       }) {
+//     final Canvas canvas = context.canvas;
+//     final Paint shadowPaint = Paint()
+//       ..color = Colors.black.withOpacity(0.25)
+//       ..maskFilter = MaskFilter.blur(BlurStyle.normal, elevation);
+//
+//     final Paint thumbPaint = Paint()
+//       ..color = sliderTheme.thumbColor!
+//       ..style = PaintingStyle.fill;
+//
+//     // 调整阴影的绘制位置,使其向下偏移
+//     canvas.drawCircle(center.translate(0, shadowOffsetY), thumbRadius + 2, shadowPaint);
+//     canvas.drawCircle(center, thumbRadius, thumbPaint);
+//   }
+// }

+ 4 - 0
lib/router/app_pages.dart

@@ -16,6 +16,7 @@ import '../module/login/view.dart';
 import '../module/main/view.dart';
 import '../module/record/view.dart';
 import '../module/splash/view.dart';
+import '../module/talk/original/controller.dart';
 import '../module/task/controller.dart';
 
 abstract class AppPage {
@@ -56,6 +57,7 @@ class AppBinding extends Bindings {
     lazyPut(() => ChatController());
     lazyPut(() => TalkController());
     lazyPut(() => RecordController());
+    lazyPut(() => OriginalController());
   }
 
   void lazyPut<S>(InstanceBuilderCallback<S> builder) {
@@ -72,6 +74,8 @@ final generalPages = [
   GetPage(name: RoutePath.chat, page: () => const ChatPage()),
   GetPage(name: RoutePath.task, page: () => const TaskPage()),
   GetPage(name: RoutePath.taskSearch, page: () => const TaskSearchPage()),
+  GetPage(name: RoutePath.record, page: () => const RecordPage()),
+  GetPage(name: RoutePath.talkDetail, page: () => const TalkPage()),
   GetPage(name: RoutePath.record, page: () =>  const RecordPage()),
   GetPage(name: RoutePath.talkDetail, page: () => TalkPage()),
 ];