瀏覽代碼

[new]增加代办编辑界面

zk 1 年之前
父節點
當前提交
82d727fe72

assets/images/icon_agent_un_check.webp → assets/images/icon_agent_done.webp


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

@@ -98,4 +98,10 @@
     <string name="file_import_fail">导入失败</string>
     <string name="file_importing">正在导入...</string>
     <string name="agenda_no_data">暂无待办事项</string>
+    <string name="agenda_sift">筛选</string>
+    <string name="agenda_detail_title">待办详情</string>
+    <string name="agenda_detail_from_that_talk">待办来源</string>
+    <string name="agenda_detail_edit_title">编辑待办</string>
+    <string name="agenda_detail_popup_cancel">取消我的待办</string>
+    <string name="agenda_detail_popup_update">编辑</string>
 </resources>

+ 4 - 0
lib/data/api/atmob_api.dart

@@ -8,6 +8,7 @@ import 'package:electronic_assistant/data/api/network_module.dart';
 import 'package:electronic_assistant/data/api/request/agenda_request.dart';
 import 'package:electronic_assistant/data/api/request/agenda_status_request.dart';
 import 'package:electronic_assistant/data/api/request/agenda_todo_request.dart';
+import 'package:electronic_assistant/data/api/request/agenda_update_request.dart';
 import 'package:electronic_assistant/data/api/request/chat_history_request.dart';
 import 'package:electronic_assistant/data/api/request/login_request.dart';
 import 'package:electronic_assistant/data/api/request/talk_create_request.dart';
@@ -130,6 +131,9 @@ abstract class AtmobApi {
 
   @POST("/project/secretary/v1/user/deprecate")
   Future<BaseResponse> userDeprecate(@Body() AppBaseRequest request);
+
+  @POST("/project/secretary/v1/agenda/update")
+  Future<BaseResponse> agendaUpdate(@Body() AgendaUpdateRequest request);
 }
 
 final atmobApi = AtmobApi(defaultDio, baseUrl: Constants.baseUrl);

+ 27 - 0
lib/data/api/request/agenda_update_bean.dart

@@ -0,0 +1,27 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'agenda_update_bean.g.dart';
+
+@JsonSerializable()
+class AgendaUpdateBean {
+  @JsonKey(name: 'id')
+  String? id;
+
+  @JsonKey(name: 'name')
+  String? name;
+
+  @JsonKey(name: 'content')
+  String? content;
+
+  AgendaUpdateBean(this.id, this.name, this.content);
+
+  Map<String, dynamic> toJson() => _$AgendaUpdateBeanToJson(this);
+
+  factory AgendaUpdateBean.fromJson(Map<String, dynamic> json) =>
+      _$AgendaUpdateBeanFromJson(json);
+
+  @override
+  String toString() {
+    return 'AgendaUpdateBean{id: $id, name: $name, content: $content}';
+  }
+}

+ 20 - 0
lib/data/api/request/agenda_update_request.dart

@@ -0,0 +1,20 @@
+import 'package:electronic_assistant/base/app_base_request.dart';
+import 'package:json_annotation/json_annotation.dart';
+
+import 'agenda_update_bean.dart';
+
+part 'agenda_update_request.g.dart';
+
+@JsonSerializable()
+class AgendaUpdateRequest extends AppBaseRequest {
+  @JsonKey(name: 'talkId')
+  String talkId;
+
+  @JsonKey(name: 'agendas')
+  List<AgendaUpdateBean> list;
+
+  AgendaUpdateRequest(this.talkId, this.list);
+
+  @override
+  Map<String, dynamic> toJson() => _$AgendaUpdateRequestToJson(this);
+}

+ 18 - 3
lib/data/bean/agenda.dart

@@ -15,7 +15,7 @@ class Agenda {
   String? name;
 
   @JsonKey(name: 'content')
-  String? content;
+  Rx<String> _content = ''.obs;
 
   @JsonKey(name: 'createTime')
   String? createTime;
@@ -29,24 +29,39 @@ class Agenda {
   @JsonKey(name: 'example')
   bool? isExample;
 
-  bool? isDone;
+  @JsonKey(ignore: true)
+  Rxn<AgendaStatus>? agendaStatus;
 
   Agenda(
       {required this.id,
       required this.talkId,
       this.name,
-      this.content,
       this.createTime,
       this.updateTime,
       this.isExample});
 
+  String get content => _content.value;
+
+  set content(String newContent) {
+    _content.value = newContent;
+  }
+
   factory Agenda.fromJson(Map<String, dynamic> json) {
     final agenda = _$AgendaFromJson(json);
     agenda.todo.value =
         json.containsKey('todo') ? json['todo'] as bool? ?? false : false;
+    agenda.agendaStatus = Rxn(AgendaStatus.todo);
+    agenda._content.value = json['content'] as String? ?? '';
     return agenda;
   }
 
   static String _intToString(int value) => value.toString();
+
   static int _stringToInt(String value) => int.parse(value);
 }
+
+enum AgendaStatus {
+  todo,
+  completing,
+  done,
+}

+ 10 - 7
lib/data/repositories/agenda_repository.dart

@@ -11,6 +11,8 @@ import '../api/atmob_api.dart';
 import '../api/request/agenda_request.dart';
 import '../api/request/agenda_status_request.dart';
 import '../api/request/agenda_todo_request.dart';
+import '../api/request/agenda_update_bean.dart';
+import '../api/request/agenda_update_request.dart';
 import '../api/response/agenda_list_all_response.dart';
 import '../api/response/agenda_list_mine_response.dart';
 import '../api/response/agenda_response.dart';
@@ -31,14 +33,9 @@ class AgendaRepository {
     _agendaList.assignAll(list);
   }
 
-  removeItem(Agenda item) {
-    _agendaList.removeWhere((element) => element.id == item.id);
-  }
-
-  Future<AgendaResponse> requestHomeAgendaData(
-      {TaskStatus completeStatus = TaskStatus.TODO}) {
+  Future<AgendaResponse> requestHomeAgendaData() {
     return requestAgendaPagePaginate(0, 10,
-        completeStatus: completeStatus, isClearAll: true);
+        completeStatus: TaskStatus.TODO, isClearAll: true);
   }
 
   Future<AgendaResponse> requestAgendaPagePaginate(int offset, int limit,
@@ -89,6 +86,12 @@ class AgendaRepository {
         .then(HttpHandler.handle(true));
   }
 
+  Future<void> agendaUpdate(String talkId, List<AgendaUpdateBean> list) {
+    return atmobApi
+        .agendaUpdate(AgendaUpdateRequest(talkId, list))
+        .then(HttpHandler.handle(true));
+  }
+
   Future<void> agendaTodo(String id, bool isTodo) async {
     if (!accountRepository.isLogin.value) {
       throw ServerErrorException(

+ 107 - 0
lib/dialog/edit_agenda_dialog.dart

@@ -0,0 +1,107 @@
+import 'package:electronic_assistant/resource/colors.gen.dart';
+import 'package:electronic_assistant/resource/string.gen.dart';
+import 'package:electronic_assistant/utils/expand.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+
+typedef EditAgendaCallback = void Function(String content);
+
+void showEditAgendaDialog(
+    BuildContext context, TextEditingController etFieldController,
+    {String? hintTxt, EditAgendaCallback? callback}) {
+  final etPrintTxt = ''.obs;
+
+  etFieldController.addListener(() {
+    etPrintTxt.value = etFieldController.text;
+  });
+  showModalBottomSheet(
+    context: context,
+    enableDrag: false,
+    isScrollControlled: true,
+    backgroundColor: ColorName.transparent,
+    builder: (BuildContext context) {
+      return Padding(
+        padding: EdgeInsets.only(
+          bottom: MediaQuery.of(context).viewInsets.bottom,
+        ),
+        child: Container(
+          padding: EdgeInsets.all(16.w),
+          margin: EdgeInsets.all(12.w),
+          decoration: BoxDecoration(
+            borderRadius: BorderRadius.all(Radius.circular(12.w)),
+            color: "#FFFFFF".toColor(),
+          ),
+          child: IntrinsicHeight(
+            child: Column(
+              mainAxisSize: MainAxisSize.min, // 使弹窗高度自适应内容
+              children: [
+                Row(
+                  children: [
+                    GestureDetector(
+                      onTap: () {
+                        Get.back();
+                      },
+                      child: Text(StringName.cancel.tr,
+                          style: TextStyle(
+                              color: ColorName.secondaryTextColor,
+                              fontSize: 14.sp)),
+                    ),
+                    const Spacer(),
+                    Text(StringName.agendaDetailEditTitle.tr,
+                        style: TextStyle(
+                            color: ColorName.primaryTextColor,
+                            fontSize: 15.sp)),
+                    const Spacer(),
+                    Obx(() {
+                      return GestureDetector(
+                        onTap: () {
+                          if (etFieldController.text.isEmpty) {
+                            return;
+                          }
+                          callback?.call(etFieldController.text);
+                        },
+                        child: Text(StringName.done.tr,
+                            style: TextStyle(
+                                color: etPrintTxt.value.isNotEmpty
+                                    ? ColorName.colorPrimary
+                                    : ColorName.tertiaryTextColor,
+                                fontSize: 15.sp)),
+                      );
+                    }),
+                  ],
+                ),
+                SizedBox(height: 16.h),
+                Container(
+                  decoration: BoxDecoration(
+                    color: "#F6F5F8".toColor(),
+                    borderRadius: BorderRadius.circular(8),
+                  ),
+                  height: 150.h,
+                  child: TextField(
+                    maxLines: null,
+                    maxLength: 200,
+                    cursorColor: ColorName.primaryTextColor,
+                    style: TextStyle(
+                        fontSize: 15.sp, color: ColorName.primaryTextColor),
+                    decoration: InputDecoration(
+                      counterText: '',
+                      border: InputBorder.none,
+                      fillColor: Colors.transparent,
+                      hintText: hintTxt,
+                      hintStyle: TextStyle(
+                          fontSize: 15.sp, color: ColorName.tertiaryTextColor),
+                      contentPadding: EdgeInsets.symmetric(
+                          vertical: 14.w, horizontal: 10.w), // 设置TextField的高度
+                    ),
+                    controller: etFieldController,
+                  ),
+                )
+              ],
+            ),
+          ),
+        ),
+      );
+    },
+  );
+}

+ 1 - 0
lib/dialog/rename_dialog.dart

@@ -66,6 +66,7 @@ void reNameDialog(String title, String? content,
                     children: [
                       Expanded(
                         child: TextField(
+                          cursorColor: ColorName.primaryTextColor,
                           maxLength: maxLength,
                           style: TextStyle(
                               fontSize: 16.sp,

+ 103 - 0
lib/module/agenda/controller.dart

@@ -0,0 +1,103 @@
+import 'package:electronic_assistant/base/base_controller.dart';
+import 'package:electronic_assistant/data/repositories/agenda_repository.dart';
+import 'package:electronic_assistant/data/repositories/talk_repository.dart';
+import 'package:electronic_assistant/utils/error_handler.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:get/get_rx/src/rx_types/rx_types.dart';
+import 'package:pull_to_refresh/pull_to_refresh.dart';
+
+import '../../data/api/request/agenda_request.dart';
+import '../../data/bean/agenda.dart';
+import 'detail/view.dart';
+
+class AgendaController extends BaseController {
+  String get filterTxt => '展示近两周待办';
+
+  final agendaDetailPopupTag = 'agendaDetailPopupTag';
+  final todoIsExpanded = true.obs;
+  final doneIsExpanded = true.obs;
+  final refreshController = RefreshController(initialRefresh: false);
+
+  RxList<Agenda> get agendaTodoList => agendaRepository.agendaList;
+  final RxList<Agenda> agendaDoneList = <Agenda>[].obs;
+
+  final int limit = 10;
+
+  @override
+  void onReady() {
+    refreshTodoTaskList();
+    agendaPage(0, limit, isClearAll: true);
+  }
+
+  void onLoadMoreDoneData() {
+    agendaPage(agendaDoneList.length, limit);
+  }
+
+  agendaPage(int offset, int limit, {bool? isClearAll}) {
+    agendaRepository
+        .agendaPage(offset, limit, completeStatus: TaskStatus.DONE)
+        .then((response) {
+      if (isClearAll == true) {
+        agendaDoneList.clear();
+      }
+      if (response.list != null) {
+        agendaDoneList.addAll(response.list!);
+      }
+      if (agendaDoneList.length >= response.count) {
+        debugPrint("refreshAgendaPage-没有更多数据了");
+        refreshController.loadNoData();
+      } else {
+        refreshController.loadComplete();
+      }
+      refreshController.refreshCompleted();
+    }).catchError((error) {
+      refreshController.loadFailed();
+      refreshController.refreshFailed();
+      ErrorHandler.toastError(error);
+    });
+  }
+
+  refreshTodoTaskList() {
+    agendaRepository.requestAgendaPagePaginate(0, 999,
+        completeStatus: TaskStatus.TODO, isClearAll: true);
+  }
+
+  void onClickTodoGroup() {
+    todoIsExpanded.value = !todoIsExpanded.value;
+  }
+
+  void onClickDoneGroup() {
+    doneIsExpanded.value = !doneIsExpanded.value;
+  }
+
+  void agendaComplete(Agenda item, bool isComplete) {
+    agendaRepository.agendaFinish(item.id, isComplete).then((data) {
+      _refreshData();
+    }).catchError((error) {
+      ErrorHandler.toastError(error);
+    });
+  }
+
+  void _refreshData() {
+    refreshTodoTaskList();
+    int size;
+    if (agendaDoneList.isEmpty) {
+      size = limit;
+    } else {
+      size = agendaDoneList.length;
+    }
+    agendaPage(0, size, isClearAll: true);
+  }
+
+  void onAgendaCancel(Agenda item) {
+    agendaRepository.agendaTodo(item.id, false).then((value) {
+      _refreshData();
+    }).catchError((error) {
+      ErrorHandler.toastError(error);
+    });
+  }
+
+  void onAgendaUpdate(Agenda item) {
+    AgendaDetailPage.start(item);
+  }
+}

+ 87 - 0
lib/module/agenda/detail/controller.dart

@@ -0,0 +1,87 @@
+import 'package:electronic_assistant/base/base_controller.dart';
+import 'package:electronic_assistant/data/bean/agenda.dart';
+import 'package:electronic_assistant/data/bean/talks.dart';
+import 'package:electronic_assistant/data/repositories/agenda_repository.dart';
+import 'package:electronic_assistant/data/repositories/talk_repository.dart';
+import 'package:electronic_assistant/module/chat/view.dart';
+import 'package:electronic_assistant/utils/error_handler.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+import 'package:get/get_core/src/get_main.dart';
+
+import '../../../data/api/request/agenda_update_bean.dart';
+
+class AgendaDetailController extends BaseController {
+  final Rxn<Agenda> detailBean = Rxn<Agenda>();
+  final Rxn<TalkBean> talkBean = Rxn<TalkBean>();
+  final etFieldController = TextEditingController();
+
+  @override
+  void onInit() {
+    super.onInit();
+    _getArguments();
+  }
+
+  void _getArguments() {
+    if (Get.arguments is Agenda) {
+      detailBean.value = Get.arguments as Agenda;
+      requestTalkBeanData();
+    }
+  }
+
+  void requestTalkBeanData() {
+    String? talkId = detailBean.value?.talkId;
+    if (talkId == null) {
+      return;
+    }
+    talkRepository.talkInfo(talkId).then((data) {
+      talkBean.value = data.talkInfo;
+    }).catchError((error) {
+      ErrorHandler.toastError(error);
+    });
+  }
+
+  void onGoChatClick() {
+    if (detailBean.value == null) {
+      return;
+    }
+    if (talkBean.value == null) {
+      ChatPage.startByTalkId(detailBean.value!.talkId,
+          agenda: detailBean.value!);
+    } else {
+      ChatPage.startByTalk(talkBean.value!, agenda: detailBean.value!);
+    }
+  }
+
+  void onTaskDoneClick() {
+    if (detailBean.value == null) {
+      return;
+    }
+    agendaRepository.agendaFinish(detailBean.value!.id, true).then((data) {
+      agendaRepository.requestHomeAgendaData();
+      Get.back();
+    }).catchError((error) {
+      ErrorHandler.toastError(error);
+    });
+  }
+
+  void onEditAgenda(String newContent) {
+    if (detailBean.value == null) {
+      return;
+    }
+    agendaRepository.agendaUpdate(detailBean.value!.talkId, [
+      AgendaUpdateBean(detailBean.value!.id, null, newContent)
+    ]).then((data) {
+      detailBean.value!.content = newContent;
+      etFieldController.clear();
+    }).catchError((error) {
+      ErrorHandler.toastError(error);
+    });
+  }
+
+  @override
+  void onClose() {
+    super.onClose();
+    etFieldController.dispose();
+  }
+}

+ 139 - 0
lib/module/agenda/detail/view.dart

@@ -0,0 +1,139 @@
+import 'package:electronic_assistant/base/base_page.dart';
+import 'package:electronic_assistant/data/bean/agenda.dart';
+import 'package:electronic_assistant/module/agenda/detail/controller.dart';
+import 'package:electronic_assistant/module/talk/view.dart';
+import 'package:electronic_assistant/resource/colors.gen.dart';
+import 'package:electronic_assistant/resource/string.gen.dart';
+import 'package:electronic_assistant/router/app_pages.dart';
+import 'package:electronic_assistant/utils/expand.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+import 'package:get/get_core/src/get_main.dart';
+import '../../../dialog/edit_agenda_dialog.dart';
+import '../../../resource/assets.gen.dart';
+import '../../files/view.dart';
+import '../task_item_view.dart';
+
+class AgendaDetailPage extends BasePage<AgendaDetailController> {
+  const AgendaDetailPage({super.key});
+
+  static start(Agenda agenda) {
+    Get.toNamed(RoutePath.agendaDetail, arguments: agenda);
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Scaffold(
+        backgroundColor: "#F6F6F6".toColor(),
+        appBar: AppBar(
+          systemOverlayStyle: SystemUiOverlayStyle.dark,
+          backgroundColor: Colors.transparent,
+          title: Text(
+            StringName.agendaDetailTitle.tr,
+            style: TextStyle(
+                fontSize: 17.sp,
+                fontWeight: FontWeight.bold,
+                color: ColorName.primaryTextColor),
+          ),
+          centerTitle: true,
+          leading: IconButton(
+            icon: SizedBox(
+                width: 24.w,
+                height: 24.w,
+                child: Assets.images.iconBack.image()),
+            // Custom icon
+            onPressed: () {
+              Get.back();
+            },
+          ),
+        ),
+        body: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            _buildAgendaDetail(),
+            SizedBox(height: 12.h),
+            Padding(
+              padding: EdgeInsets.symmetric(horizontal: 12.w),
+              child: Text(StringName.agendaDetailFromThatTalk.tr,
+                  style: TextStyle(
+                      fontSize: 14.sp, color: ColorName.secondaryTextColor)),
+            ),
+            SizedBox(height: 12.h),
+            _buildTalkDetail(),
+            const Spacer(),
+            _buildAnalyseButton(),
+            SizedBox(height: 26.h),
+          ],
+        ));
+  }
+
+  Widget _buildAnalyseButton() {
+    return GestureDetector(
+      onTap: () {
+        controller.onGoChatClick();
+      },
+      child: Container(
+        padding: EdgeInsets.symmetric(vertical: 12.h),
+        margin: EdgeInsets.symmetric(horizontal: 12.w),
+        width: double.infinity,
+        decoration: BoxDecoration(
+          gradient: LinearGradient(
+            colors: ['#6177F2'.toColor(), '#8B9DFF'.toColor()],
+            stops: const [0, 1.0],
+          ),
+          borderRadius: BorderRadius.circular(8),
+        ),
+        child: Align(
+          child: Text(StringName.homeTalkThinking.tr,
+              style: TextStyle(fontSize: 16.sp, color: ColorName.white)),
+        ),
+      ),
+    );
+  }
+
+  Widget _buildAgendaDetail() {
+    if (controller.detailBean.value == null) {
+      return Container();
+    }
+    return Builder(builder: (context) {
+      return GestureDetector(
+        onTap: () {
+          showEditAgendaDialog(context, controller.etFieldController,
+              hintTxt: controller.detailBean.value?.content,
+              callback: (newContent) {
+            Get.back();
+            controller.onEditAgenda(newContent);
+          });
+        },
+        child: Obx(() {
+          return taskItemView(controller.detailBean.value!,
+              isShowAnalyse: false, isLimitLines: false, onCheckClick: () {
+            controller.onTaskDoneClick();
+          });
+        }),
+      );
+    });
+  }
+
+  @override
+  Color backgroundColor() {
+    return "#F6F6F6".toColor();
+  }
+
+  Widget _buildTalkDetail() {
+    return Obx(() {
+      if (controller.talkBean.value == null) {
+        return Container();
+      }
+      return Padding(
+        padding: const EdgeInsets.symmetric(horizontal: 12).w,
+        child: buildFileTalkItem(controller.talkBean.value!, onTap: () {
+          TalkPage.start(controller.talkBean.value!);
+        }),
+      );
+    });
+  }
+}

+ 2 - 2
lib/module/task/search/task_search.dart

@@ -10,8 +10,8 @@ import 'package:get/get_core/src/get_main.dart';
 import '../../../resource/assets.gen.dart';
 import '../../../resource/colors.gen.dart';
 
-class TaskSearchPage extends BasePage {
-  const TaskSearchPage({super.key});
+class AgendaSearchPage extends BasePage {
+  const AgendaSearchPage({super.key});
 
   @override
   Widget buildBody(BuildContext context) {

+ 38 - 19
lib/module/task/task_item_view.dart

@@ -10,7 +10,10 @@ import '../../resource/string.gen.dart';
 import '../../utils/common_style.dart';
 
 Widget taskItemView(Agenda item,
-    {VoidCallback? onCheckClick, VoidCallback? onThinkingClick}) {
+    {VoidCallback? onCheckClick,
+    VoidCallback? onThinkingClick,
+    bool? isShowAnalyse,
+    bool isLimitLines = true}) {
   return Container(
     padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 17).w,
     margin: const EdgeInsets.only(left: 12, right: 12, bottom: 8).w,
@@ -23,14 +26,8 @@ Widget taskItemView(Agenda item,
       children: [
         GestureDetector(
           onTap: onCheckClick,
-          child: SizedBox(
-              width: 20.w,
-              height: 20.w,
-              child: item.isDone.isTrue
-                  ? Assets.images.iconAgentChecked.image()
-                  : Assets.images.iconAgentUnderway.image()),
+          child: _buildCheckBox(item.agendaStatus?.value),
         ),
-        SizedBox(width: 6.w),
         Visibility(
           visible: item.isExample.isTrue,
           child: Container(
@@ -41,7 +38,8 @@ Widget taskItemView(Agenda item,
             padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3).w,
             child: Text(
               StringName.homeTalkExample.tr,
-              style: TextStyle(fontSize: 12.sp, color: ColorName.white, height: 1),
+              style:
+                  TextStyle(fontSize: 12.sp, color: ColorName.white, height: 1),
             ),
           ),
         ),
@@ -50,22 +48,25 @@ Widget taskItemView(Agenda item,
           child: Padding(
             padding: const EdgeInsets.only(right: 12).w,
             child: Text(item.content ?? '',
-                maxLines: 1,
-                overflow: TextOverflow.ellipsis,
+                maxLines: isLimitLines ? 1 : null,
+                overflow: isLimitLines ? TextOverflow.ellipsis : null,
                 style: TextStyle(
                     fontSize: 15.sp,
                     fontWeight: FontWeight.bold,
                     color: ColorName.primaryTextColor)),
           ),
         ),
-        GestureDetector(
-          onTap: onThinkingClick,
-          child: Container(
-            decoration: getPrimaryBtnDecoration(6),
-            padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 5).w,
-            child: Text(
-              StringName.homeTalkThinking.tr,
-              style: TextStyle(fontSize: 13.sp, color: ColorName.white),
+        Visibility(
+          visible: isShowAnalyse ?? true,
+          child: GestureDetector(
+            onTap: onThinkingClick,
+            child: Container(
+              decoration: getPrimaryBtnDecoration(6),
+              padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 5).w,
+              child: Text(
+                StringName.homeTalkThinking.tr,
+                style: TextStyle(fontSize: 13.sp, color: ColorName.white),
+              ),
             ),
           ),
         )
@@ -73,3 +74,21 @@ Widget taskItemView(Agenda item,
     ),
   );
 }
+
+Widget _buildCheckBox(AgendaStatus? status) {
+  if (status == AgendaStatus.todo) {
+    return Container(
+        margin: EdgeInsets.only(right: 6.w),
+        width: 20.w,
+        height: 20.w,
+        child: Assets.images.iconAgentUnderway.image());
+  } else if (status == AgendaStatus.done) {
+    return Container(
+        margin: EdgeInsets.only(right: 6.w),
+        width: 20.w,
+        height: 20.w,
+        child: Assets.images.iconAgentDone.image());
+  } else {
+    return Container();
+  }
+}

+ 130 - 45
lib/module/task/view.dart

@@ -1,5 +1,5 @@
 import 'package:electronic_assistant/base/base_page.dart';
-import 'package:electronic_assistant/module/task/task_item_view.dart';
+import 'package:electronic_assistant/module/agenda/task_item_view.dart';
 import 'package:electronic_assistant/resource/colors.gen.dart';
 import 'package:electronic_assistant/resource/string.gen.dart';
 import 'package:electronic_assistant/router/app_pages.dart';
@@ -8,13 +8,20 @@ import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:get/get.dart';
+import 'package:pull_to_refresh/pull_to_refresh.dart';
 
+import '../../data/bean/agenda.dart';
+import '../../data/repositories/account_repository.dart';
+import '../../popup/common_popup.dart';
+import '../../popup/template_utils.dart';
 import '../../resource/assets.gen.dart';
+import '../chat/view.dart';
 import 'controller.dart';
 
-class TaskPage extends BasePage<TaskController> {
-  const TaskPage({super.key});
+class AgendaPage extends BasePage<AgendaController> {
+  const AgendaPage({super.key});
 
   @override
   Widget buildBody(BuildContext context) {
@@ -79,7 +86,7 @@ class TaskPage extends BasePage<TaskController> {
                       SizedBox(height: 2.h),
                       // Add some space between the image and text
                       Text(
-                        '筛选',
+                        StringName.agendaSift.tr,
                         style: TextStyle(
                             fontSize: 10.sp, color: ColorName.primaryTextColor),
                       ),
@@ -114,32 +121,103 @@ class TaskPage extends BasePage<TaskController> {
             ),
             SizedBox(width: 7.h),
             Expanded(
-                child: CustomScrollView(slivers: [
-              SliverToBoxAdapter(
-                child: taskGroupItem(StringName.taskItemTodo.tr, 3, false),
+                child: SmartRefresher(
+              footer: const ClassicFooter(
+                noDataText: '',
               ),
-              SliverAnimatedList(
-                  itemBuilder: _buildTodoItem, initialItemCount: 4),
-              SliverToBoxAdapter(
-                child: taskGroupItem(StringName.taskItemDone.tr, 16, false),
-              ),
-              SliverAnimatedList(
-                  itemBuilder: _buildDoneItem, initialItemCount: 10),
-            ]))
+              onLoading: controller.onLoadMoreDoneData,
+              enablePullDown: false,
+              enablePullUp: true,
+              controller: controller.refreshController,
+              child: CustomScrollView(slivers: [
+                SliverToBoxAdapter(
+                  child: Obx(() {
+                    return taskGroupItem(
+                        StringName.taskItemTodo.tr,
+                        controller.agendaTodoList.length,
+                        controller.todoIsExpanded.value, itemClick: () {
+                      controller.onClickTodoGroup();
+                    });
+                  }),
+                ),
+                Obx(() {
+                  return SliverList.builder(
+                      itemBuilder: _buildTodoItem,
+                      itemCount: controller.todoIsExpanded.value
+                          ? controller.agendaTodoList.length
+                          : 0);
+                }),
+                SliverToBoxAdapter(
+                  child: Obx(() {
+                    return taskGroupItem(
+                        StringName.taskItemDone.tr,
+                        controller.agendaDoneList.length,
+                        controller.doneIsExpanded.value, itemClick: () {
+                      controller.onClickDoneGroup();
+                    });
+                  }),
+                ),
+                Obx(() {
+                  return SliverList.builder(
+                      itemBuilder: _buildDoneItem,
+                      itemCount: controller.doneIsExpanded.value
+                          ? controller.agendaDoneList.length
+                          : 0);
+                })
+              ]),
+            ))
           ],
         )
       ],
     );
   }
 
-  Widget _buildTodoItem(
-      BuildContext context, int index, Animation<double> animation) {
-    return Container();
+  Widget _buildTodoItem(BuildContext context, int index) {
+    Agenda item = controller.agendaTodoList[index];
+    return Builder(builder: (context) {
+      return GestureDetector(
+        onLongPressStart: (details) {
+          if (!accountRepository.isLogin.value) {
+            return;
+          }
+          showCommonPopup(details.globalPosition, Alignment.bottomRight,
+              _buildListUpdatePopupView(item, controller.agendaDetailPopupTag),
+              tag: controller.agendaDetailPopupTag);
+        },
+        child: taskItemView(
+          item,
+          onCheckClick: () {
+            controller.agendaComplete(item, true);
+          },
+          onThinkingClick: () {
+            ChatPage.startByTalkId(item.talkId, agenda: item);
+          },
+        ),
+      );
+    });
   }
 
-  Widget _buildDoneItem(
-      BuildContext context, int index, Animation<double> animation) {
-    return Container();
+  List<Widget> _buildListUpdatePopupView(Agenda item, String tag) {
+    return [
+      createNormalPopupItem(StringName.agendaDetailPopupCancel.tr,
+          onItemClick: () {
+        SmartDialog.dismiss(tag: tag);
+        controller.onAgendaCancel(item);
+      }),
+      createPopupDivider(),
+      createNormalPopupItem(StringName.agendaDetailPopupUpdate.tr,
+          onItemClick: () {
+        SmartDialog.dismiss(tag: tag);
+        controller.onAgendaUpdate(item);
+      }),
+    ];
+  }
+
+  Widget _buildDoneItem(BuildContext context, int index) {
+    Agenda item = controller.agendaDoneList[index];
+    return taskItemView(item, onCheckClick: () {
+      controller.agendaComplete(item, false);
+    }, isShowAnalyse: false);
   }
 
   Container buildTopGradient() {
@@ -170,30 +248,37 @@ class TaskPage extends BasePage<TaskController> {
     return true;
   }
 
-  Widget taskGroupItem(String groupName, int count, bool isExpanded) {
-    return Padding(
-      padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 12.w),
-      child: Row(
-        crossAxisAlignment: CrossAxisAlignment.center,
-        children: [
-          Text(groupName,
-              style: TextStyle(
-                  fontSize: 14.sp, color: ColorName.secondaryTextColor)),
-          //占位填充
-          const Spacer(),
-          Row(
-            children: [
-              Text('$count${StringName.taskItemDesc.tr}',
-                  style: TextStyle(
-                      fontSize: 12.sp, color: ColorName.secondaryTextColor)),
-              SizedBox(width: 4.w),
-              SizedBox(
-                  width: 16.w,
-                  height: 16.h,
-                  child: Assets.images.iconTalkCollapse.image())
-            ],
-          )
-        ],
+  Widget taskGroupItem(String groupName, int count, bool isExpanded,
+      {VoidCallback? itemClick}) {
+    return GestureDetector(
+      onTap: itemClick,
+      child: Container(
+        color: Colors.transparent,
+        padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 12.w),
+        child: Row(
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            Text(groupName,
+                style: TextStyle(
+                    fontSize: 14.sp, color: ColorName.secondaryTextColor)),
+            //占位填充
+            const Spacer(),
+            Row(
+              children: [
+                Text('$count${StringName.taskItemDesc.tr}',
+                    style: TextStyle(
+                        fontSize: 12.sp, color: ColorName.secondaryTextColor)),
+                SizedBox(width: 4.w),
+                SizedBox(
+                    width: 16.w,
+                    height: 16.h,
+                    child: isExpanded
+                        ? Assets.images.iconTalkExpand.image()
+                        : Assets.images.iconTalkCollapse.image())
+              ],
+            )
+          ],
+        ),
       ),
     );
   }

+ 89 - 83
lib/module/files/view.dart

@@ -168,89 +168,19 @@ class FilesPage extends BasePage<FilesController> {
   Widget _buildFileItem(BuildContext context, int index) {
     TalkBean talkBean = controller.talkList[index];
     return Obx(() {
-      return Padding(
-          padding: EdgeInsets.only(bottom: 8.w),
-          child: GestureDetector(
-            onLongPressStart: (details) {
-              if (!accountRepository.isLogin.value) {
-                return;
-              }
-              showTalkPopup(details.globalPosition, Alignment.bottomRight,
-                  onRename: () {
-                showRenameTalkDialog(talkBean);
-              }, onDelete: () {
-                showDeleteTalkDialog(talkBean);
-              });
-            },
-            onTap: () {
-              TalkPage.start(talkBean);
-            },
-            child: Container(
-              decoration: BoxDecoration(
-                color: Colors.white,
-                borderRadius: BorderRadius.circular(8.w),
-              ),
-              padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 14.w),
-              child: Row(
-                crossAxisAlignment: CrossAxisAlignment.start,
-                children: [
-                  Image(
-                      image: (talkBean.status.value == TalkStatus.analysing ||
-                              talkBean.status.value == TalkStatus.waitAnalysis)
-                          ? Assets.images.iconTalkAnalysis.provider()
-                          : Assets.images.iconFilesFile.provider(),
-                      width: 28.w,
-                      height: 32.w),
-                  Expanded(
-                      child: Padding(
-                    padding: EdgeInsets.symmetric(horizontal: 8.w),
-                    child: Column(
-                      crossAxisAlignment: CrossAxisAlignment.start,
-                      children: [
-                        Text(talkBean.title.value.orEmpty,
-                            maxLines: 1,
-                            overflow: TextOverflow.ellipsis,
-                            style: TextStyle(
-                                fontSize: 14.sp,
-                                color: ColorName.primaryTextColor,
-                                fontWeight: FontWeight.bold)),
-                        Text(talkBean.summary.value.orEmpty,
-                            maxLines: 2,
-                            overflow: TextOverflow.ellipsis,
-                            style: TextStyle(
-                                fontSize: 12.sp,
-                                color: talkBean.status.value ==
-                                        TalkStatus.analysisFail
-                                    ? "#F5574E".toColor()
-                                    : ColorName.secondaryTextColor)),
-                        Container(
-                          margin: EdgeInsets.only(top: 6.w),
-                          child: Row(
-                            crossAxisAlignment: CrossAxisAlignment.center,
-                            children: [
-                              Text(talkBean.duration.toFormattedDuration(),
-                                  style: TextStyle(
-                                      fontSize: 12.sp,
-                                      color: ColorName.tertiaryTextColor)),
-                              Text("  |  ",
-                                  style: TextStyle(
-                                      fontSize: 12.sp,
-                                      color: ColorName.tertiaryTextColor,
-                                      fontWeight: FontWeight.bold)),
-                              Text(talkBean.createTime.orEmpty,
-                                  style: TextStyle(
-                                      fontSize: 12.sp,
-                                      color: ColorName.tertiaryTextColor)),
-                            ],
-                          ),
-                        )
-                      ],
-                    ),
-                  )),
-                ],
-              ),
-            ),
-          ));
+      return buildFileTalkItem(talkBean, onTap: () {
+        TalkPage.start(talkBean);
+      }, onLongPressStart: (details) {
+        if (!accountRepository.isLogin.value) {
+          return;
+        }
+        showTalkPopup(details.globalPosition, Alignment.bottomRight,
+            onRename: () {
+          showRenameTalkDialog(talkBean);
+        }, onDelete: () {
+          showDeleteTalkDialog(talkBean);
+        });
+      });
     });
   }
 
@@ -268,3 +198,79 @@ class FilesPage extends BasePage<FilesController> {
     });
   }
 }
+
+Widget buildFileTalkItem(TalkBean talkBean,
+    {GestureTapCallback? onTap,
+    GestureLongPressStartCallback? onLongPressStart}) {
+  return Padding(
+      padding: EdgeInsets.only(bottom: 8.w),
+      child: GestureDetector(
+        onLongPressStart: onLongPressStart,
+        onTap: onTap,
+        child: Container(
+          decoration: BoxDecoration(
+            color: Colors.white,
+            borderRadius: BorderRadius.circular(8.w),
+          ),
+          padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 14.w),
+          child: Row(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Image(
+                  image: (talkBean.status.value == TalkStatus.analysing ||
+                          talkBean.status.value == TalkStatus.waitAnalysis)
+                      ? Assets.images.iconTalkAnalysis.provider()
+                      : Assets.images.iconFilesFile.provider(),
+                  width: 28.w,
+                  height: 32.w),
+              Expanded(
+                  child: Padding(
+                padding: EdgeInsets.symmetric(horizontal: 8.w),
+                child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    Text(talkBean.title.value.orEmpty,
+                        maxLines: 1,
+                        overflow: TextOverflow.ellipsis,
+                        style: TextStyle(
+                            fontSize: 14.sp,
+                            color: ColorName.primaryTextColor,
+                            fontWeight: FontWeight.bold)),
+                    Text(talkBean.summary.value.orEmpty,
+                        maxLines: 2,
+                        overflow: TextOverflow.ellipsis,
+                        style: TextStyle(
+                            fontSize: 12.sp,
+                            color:
+                                talkBean.status.value == TalkStatus.analysisFail
+                                    ? "#F5574E".toColor()
+                                    : ColorName.secondaryTextColor)),
+                    Container(
+                      margin: EdgeInsets.only(top: 6.w),
+                      child: Row(
+                        crossAxisAlignment: CrossAxisAlignment.center,
+                        children: [
+                          Text(talkBean.duration.toFormattedDuration(),
+                              style: TextStyle(
+                                  fontSize: 12.sp,
+                                  color: ColorName.tertiaryTextColor)),
+                          Text("  |  ",
+                              style: TextStyle(
+                                  fontSize: 12.sp,
+                                  color: ColorName.tertiaryTextColor,
+                                  fontWeight: FontWeight.bold)),
+                          Text(talkBean.createTime.orEmpty,
+                              style: TextStyle(
+                                  fontSize: 12.sp,
+                                  color: ColorName.tertiaryTextColor)),
+                        ],
+                      ),
+                    )
+                  ],
+                ),
+              )),
+            ],
+          ),
+        ),
+      ));
+}

+ 10 - 2
lib/module/home/controller.dart

@@ -57,7 +57,6 @@ class HomePageController extends BaseController {
 
   @override
   void dispose() {
-    // TODO: implement dispose
     super.dispose();
     eventBus.off(EventUserLogin);
     eventBus.off(EventUserLogout);
@@ -113,7 +112,7 @@ class HomePageController extends BaseController {
 
   void agendaComplete(Agenda item) {
     agendaRepository.agendaFinish(item.id, true).then((data) {
-      agendaRepository.removeItem(item);
+      agendaRepository.requestHomeAgendaData();
     }).catchError((error) {
       ErrorHandler.toastError(error);
     });
@@ -195,6 +194,15 @@ class HomePageController extends BaseController {
     String newFilePath = '${dir.path}/${file.uri.pathSegments.last}';
     file.renameSync(newFilePath);
   }
+
+  void onGoAgendaList() {
+    if (!accountRepository.isLogin.value) {
+      Get.toNamed(RoutePath.login);
+      ToastUtil.showToast(StringName.errorCodeNoLogin.tr);
+      return;
+    }
+    Get.toNamed(RoutePath.task);
+  }
 }
 
 Future<Directory> _getChoiceUploadDir(String talkId) async {

+ 39 - 31
lib/module/home/view.dart

@@ -18,8 +18,9 @@ import 'package:get/get.dart';
 
 import '../../data/bean/agenda.dart';
 import '../../router/app_pages.dart';
+import '../agenda/detail/view.dart';
+import '../agenda/task_item_view.dart';
 import '../talk/view.dart';
-import '../task/task_item_view.dart';
 import 'controller.dart';
 
 class HomePage extends BasePage<HomePageController> {
@@ -192,30 +193,32 @@ class HomePage extends BasePage<HomePageController> {
 
   SliverToBoxAdapter buildSeeMoreView() {
     return SliverToBoxAdapter(
-      child: Visibility(
-        visible: controller.agendaList.isNotEmpty,
-        child: Container(
-          alignment: Alignment.center,
-          padding: const EdgeInsets.only(top: 12, bottom: 36).w,
-          child: RichText(
-            text: TextSpan(
-              text: StringName.homeTalkTodo1.tr,
-              style: TextStyle(
-                  color: ColorName.secondaryTextColor, fontSize: 12.sp),
-              children: <TextSpan>[
-                TextSpan(
-                    text: StringName.homeTalkTodo2.tr,
-                    style: TextStyle(
-                        color: ColorName.colorPrimary, fontSize: 12.sp),
-                    recognizer: TapGestureRecognizer()
-                      ..onTap = () {
-                        Get.toNamed(RoutePath.task);
-                      }),
-              ],
+      child: Obx(() {
+        return Visibility(
+          visible: controller.agendaList.isNotEmpty,
+          child: Container(
+            alignment: Alignment.center,
+            padding: const EdgeInsets.only(top: 12, bottom: 36).w,
+            child: RichText(
+              text: TextSpan(
+                text: StringName.homeTalkTodo1.tr,
+                style: TextStyle(
+                    color: ColorName.secondaryTextColor, fontSize: 12.sp),
+                children: <TextSpan>[
+                  TextSpan(
+                      text: StringName.homeTalkTodo2.tr,
+                      style: TextStyle(
+                          color: ColorName.colorPrimary, fontSize: 12.sp),
+                      recognizer: TapGestureRecognizer()
+                        ..onTap = () {
+                          controller.onGoAgendaList();
+                        }),
+                ],
+              ),
             ),
           ),
-        ),
-      ),
+        );
+      }),
     );
   }
 
@@ -233,7 +236,7 @@ class HomePage extends BasePage<HomePageController> {
         children: [
           SizedBox(height: 12.w),
           buildTitle(StringName.homeTalkTodoTitle.tr, () {
-            Get.toNamed(RoutePath.task);
+            controller.onGoAgendaList();
           }),
           SizedBox(height: 12.w)
         ],
@@ -378,14 +381,19 @@ class HomePage extends BasePage<HomePageController> {
   Widget _builderAgendaItem(BuildContext context, int index) {
     return Obx(() {
       Agenda item = controller.agendaList[index];
-      return taskItemView(
-        item,
-        onThinkingClick: () {
-          ChatPage.startByTalkId(item.talkId, agenda: item);
-        },
-        onCheckClick: () {
-          controller.agendaComplete(item);
+      return GestureDetector(
+        onTap: () {
+          AgendaDetailPage.start(item);
         },
+        child: taskItemView(
+          item,
+          onThinkingClick: () {
+            ChatPage.startByTalkId(item.talkId, agenda: item);
+          },
+          onCheckClick: () {
+            controller.agendaComplete(item);
+          },
+        ),
       );
     });
   }

+ 3 - 1
lib/module/talk/controller.dart

@@ -133,7 +133,9 @@ class TalkController extends BaseController {
         uri = Uri.parse(bean!.audioUrl!);
       } else {
         File? file = await getFileByTalk(id, bean?.uploadType);
-        uri = file?.uri;
+        if (file?.existsSync() == true) {
+          uri = file?.uri;
+        }
       }
       if (uri == null) {
         throw '音频文件不存在';

+ 0 - 10
lib/module/task/controller.dart

@@ -1,10 +0,0 @@
-import 'package:electronic_assistant/base/base_controller.dart';
-import 'package:electronic_assistant/data/repositories/talk_repository.dart';
-
-class TaskController extends BaseController {
-  String get filterTxt => '展示近两周待办';
-
-  refreshTodoTaskList() {
-    // taskRepository.agendaPage(page, pageSize)
-  }
-}

+ 47 - 0
lib/popup/common_popup.dart

@@ -0,0 +1,47 @@
+import 'dart:ui';
+
+import 'package:electronic_assistant/utils/expand.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+
+void showCommonPopup(
+    Offset offset, Alignment alignment, List<Widget> childWidget,
+    {String? tag, BuildContext? context}) {
+  SmartDialog.showAttach(
+    targetContext: null,
+    targetBuilder: (_, __) => offset,
+    animationType: SmartAnimationType.fade,
+    clickMaskDismiss: true,
+    alignment: alignment,
+    bindWidget: context,
+    tag: tag,
+    maskColor: Colors.transparent,
+    builder: (_) {
+      return IntrinsicWidth(
+        child: Container(
+          constraints: BoxConstraints(
+            minWidth: 128.w,
+          ),
+          decoration: BoxDecoration(
+            color: Colors.white,
+            border: Border.all(color: '#D8D8D8'.toColor(), width: 1), // 边框
+            borderRadius: BorderRadius.circular(8), // 圆角
+            boxShadow: [
+              BoxShadow(
+                color: Colors.black.withOpacity(0.1), // 阴影颜色
+                spreadRadius: 2, // 阴影扩散半径
+                blurRadius: 6, // 阴影模糊半径
+                offset: const Offset(0, 3), // 阴影偏移量
+              ),
+            ],
+          ),
+          child: Column(
+            children: childWidget,
+          ),
+        ),
+      );
+    },
+  );
+}

+ 11 - 88
lib/popup/talk_popup.dart

@@ -1,6 +1,6 @@
 import 'dart:ui';
 
-import 'package:electronic_assistant/resource/colors.gen.dart';
+import 'package:electronic_assistant/popup/template_utils.dart';
 import 'package:electronic_assistant/resource/string.gen.dart';
 import 'package:electronic_assistant/utils/expand.dart';
 import 'package:flutter/cupertino.dart';
@@ -9,96 +9,19 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:get/get.dart';
 
-import '../resource/assets.gen.dart';
+import 'common_popup.dart';
 
 void showTalkPopup(Offset offset, Alignment alignment,
     {VoidCallback? onRename, VoidCallback? onDelete}) {
-  SmartDialog.showAttach(
-    targetContext: null,
-    targetBuilder: (_, __) => offset,
-    animationType: SmartAnimationType.fade,
-    clickMaskDismiss: true,
-    alignment: alignment,
-    maskColor: Colors.transparent,
-    builder: (_) {
-      return Container(
-        width: 128.w,
-        decoration: BoxDecoration(
-          color: Colors.white,
-          border: Border.all(color: '#D8D8D8'.toColor(), width: 1), // 边框
-          borderRadius: BorderRadius.circular(8), // 圆角
-          boxShadow: [
-            BoxShadow(
-              color: Colors.black.withOpacity(0.1), // 阴影颜色
-              spreadRadius: 2, // 阴影扩散半径
-              blurRadius: 6, // 阴影模糊半径
-              offset: const Offset(0, 3), // 阴影偏移量
-            ),
-          ],
-        ),
-        child: Column(
-          children: [
-            _createNormalItem(StringName.talkRename.tr, onItemClick: () {
-              SmartDialog.dismiss();
-              onRename?.call();
-            }),
-            Divider(color: "#F6F6F6".toColor(), height: 1),
-            _buildDeleteItem(onDelete),
-          ],
-        ),
-      );
-    },
-  );
-}
-
-GestureDetector _buildDeleteItem(VoidCallback? onDelete) {
-  return GestureDetector(
-    onTap: () {
+  showCommonPopup(offset, alignment, [
+    createNormalPopupItem(StringName.talkRename.tr, onItemClick: () {
+      SmartDialog.dismiss();
+      onRename?.call();
+    }),
+    createPopupDivider(),
+    createDeletePopupItem(() {
       SmartDialog.dismiss();
       onDelete?.call();
-    },
-    child: Container(
-      color: Colors.transparent,
-      padding: EdgeInsets.symmetric(horizontal: _itemPadding),
-      height: _itemHeight,
-      child: Row(
-        crossAxisAlignment: CrossAxisAlignment.center,
-        children: [
-          Text(
-            StringName.talkDelete.tr,
-            style: TextStyle(color: '#F5574E'.toColor(), fontSize: 14.sp),
-          ),
-          const Spacer(),
-          SizedBox(
-              width: 20.w,
-              height: 20.w,
-              child: Assets.images.iconTalkDelete.image())
-        ],
-      ),
-    ),
-  );
+    }),
+  ]);
 }
-
-Widget _createNormalItem(String title, {VoidCallback? onItemClick}) {
-  return GestureDetector(
-    onTap: onItemClick,
-    child: Container(
-      color: Colors.transparent,
-      padding: EdgeInsets.symmetric(horizontal: _itemPadding),
-      height: _itemHeight,
-      child: Align(
-        alignment: Alignment.centerLeft,
-        child: Text(
-          StringName.talkRename.tr,
-          style: TextStyle(
-            fontSize: 14.sp,
-            color: ColorName.primaryTextColor,
-          ),
-        ),
-      ),
-    ),
-  );
-}
-
-final _itemHeight = 52.h;
-final _itemPadding = 14.w;

+ 67 - 0
lib/popup/template_utils.dart

@@ -0,0 +1,67 @@
+import 'dart:ui';
+
+import 'package:electronic_assistant/utils/expand.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+
+import '../resource/assets.gen.dart';
+import '../resource/colors.gen.dart';
+import '../resource/string.gen.dart';
+
+Widget createPopupDivider(
+    {Color color = const Color(0xFFF6F6F6), double height = 1}) {
+  return Divider(color: color, height: height);
+}
+
+Widget createNormalPopupItem(String title, {VoidCallback? onItemClick}) {
+  return GestureDetector(
+    onTap: onItemClick,
+    child: Container(
+      color: Colors.transparent,
+      padding: EdgeInsets.symmetric(horizontal: _itemPadding),
+      height: _itemHeight,
+      child: Align(
+        alignment: Alignment.centerLeft,
+        child: Text(
+          title,
+          style: TextStyle(
+            fontSize: 14.sp,
+            color: ColorName.primaryTextColor,
+          ),
+        ),
+      ),
+    ),
+  );
+}
+
+GestureDetector createDeletePopupItem(VoidCallback? onDelete) {
+  return GestureDetector(
+    onTap: () {
+      onDelete?.call();
+    },
+    child: Container(
+      color: Colors.transparent,
+      padding: EdgeInsets.symmetric(horizontal: _itemPadding),
+      height: _itemHeight,
+      child: Row(
+        crossAxisAlignment: CrossAxisAlignment.center,
+        children: [
+          Text(
+            StringName.talkDelete.tr,
+            style: TextStyle(color: '#F5574E'.toColor(), fontSize: 14.sp),
+          ),
+          const Spacer(),
+          SizedBox(
+              width: 20.w,
+              height: 20.w,
+              child: Assets.images.iconTalkDelete.image())
+        ],
+      ),
+    ),
+  );
+}
+
+final _itemHeight = 52.h;
+final _itemPadding = 14.w;

+ 12 - 6
lib/router/app_pages.dart

@@ -13,10 +13,13 @@ import 'package:electronic_assistant/module/talk/controller.dart';
 import 'package:electronic_assistant/module/talk/summary/controller.dart';
 import 'package:electronic_assistant/module/talk/todo/controller.dart';
 import 'package:electronic_assistant/module/talk/view.dart';
-import 'package:electronic_assistant/module/task/search/task_search.dart';
-import 'package:electronic_assistant/module/task/view.dart';
 import 'package:get/get.dart';
 
+import '../module/agenda/controller.dart';
+import '../module/agenda/detail/controller.dart';
+import '../module/agenda/detail/view.dart';
+import '../module/agenda/search/view.dart';
+import '../module/agenda/view.dart';
 import '../module/chat/controller.dart';
 import '../module/chat/view.dart';
 import '../module/files/search/view.dart';
@@ -28,7 +31,6 @@ 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 {
   static final pages = <GetPage>[
@@ -60,6 +62,8 @@ abstract class RoutePath {
   static const store = '/store';
 
   static const browser = '/browser';
+
+  static const agendaDetail = '/agendaDetail';
 }
 
 class AppBinding extends Bindings {
@@ -69,7 +73,7 @@ class AppBinding extends Bindings {
     lazyPut(() => SplashController());
     lazyPut(() => HomePageController());
     lazyPut(() => LoginController());
-    lazyPut(() => TaskController());
+    lazyPut(() => AgendaController());
     lazyPut(() => ChatController());
     lazyPut(() => TalkController());
     lazyPut(() => RecordController());
@@ -82,6 +86,7 @@ class AppBinding extends Bindings {
     lazyPut(() => ChatStartController());
     lazyPut(() => BrowserController());
     lazyPut(() => MainDrawerController());
+    lazyPut(() => AgendaDetailController());
   }
 
   void lazyPut<S>(InstanceBuilderCallback<S> builder) {
@@ -96,10 +101,11 @@ final generalPages = [
   GetPage(name: RoutePath.files, page: () => const FilesPage()),
   GetPage(name: RoutePath.fileSearch, page: () => const FileSearchPage()),
   GetPage(name: RoutePath.chat, page: () => const ChatPage()),
-  GetPage(name: RoutePath.task, page: () => const TaskPage()),
-  GetPage(name: RoutePath.taskSearch, page: () => const TaskSearchPage()),
+  GetPage(name: RoutePath.task, page: () => const AgendaPage()),
+  GetPage(name: RoutePath.taskSearch, page: () => const AgendaSearchPage()),
   GetPage(name: RoutePath.record, page: () => const RecordPage()),
   GetPage(name: RoutePath.store, page: () => const StorePage()),
   GetPage(name: RoutePath.talkDetail, page: () => const TalkPage()),
   GetPage(name: RoutePath.browser, page: () => const BrowserPage()),
+  GetPage(name: RoutePath.agendaDetail, page: () => const AgendaDetailPage()),
 ];