view.dart 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. import 'package:electronic_assistant/base/base_page.dart';
  2. import 'package:electronic_assistant/data/bean/talks.dart';
  3. import 'package:electronic_assistant/data/repositories/account_repository.dart';
  4. import 'package:electronic_assistant/dialog/rename_dialog.dart';
  5. import 'package:electronic_assistant/dialog/talk_delete_dialog.dart';
  6. import 'package:electronic_assistant/module/chat/view.dart';
  7. import 'package:electronic_assistant/module/store/view.dart';
  8. import 'package:electronic_assistant/popup/talk_popup.dart';
  9. import 'package:electronic_assistant/resource/assets.gen.dart';
  10. import 'package:electronic_assistant/resource/colors.gen.dart';
  11. import 'package:electronic_assistant/resource/string.gen.dart';
  12. import 'package:electronic_assistant/utils/expand.dart';
  13. import 'package:electronic_assistant/widget/pull_to_refresh.dart';
  14. import 'package:flutter/gestures.dart';
  15. import 'package:flutter/material.dart';
  16. import 'package:flutter_screenutil/flutter_screenutil.dart';
  17. import 'package:get/get.dart';
  18. import '../../data/bean/agenda.dart';
  19. import '../../router/app_pages.dart';
  20. import '../agenda/task_item_view.dart';
  21. import 'controller.dart';
  22. class HomePage extends BasePage<HomePageController> {
  23. const HomePage({super.key});
  24. @override
  25. Widget buildBody(BuildContext context) {
  26. return Stack(
  27. children: [
  28. buildBgBox(),
  29. SafeArea(
  30. child: Column(
  31. children: [
  32. Container(
  33. width: 1.sw,
  34. padding: const EdgeInsets.all(12).w,
  35. child: buildOperationBar(),
  36. ),
  37. Expanded(
  38. child: Container(
  39. color: "#F6F5F8".toColor(),
  40. child: PullToRefresh(
  41. enableRefresh: true,
  42. controller: controller.refreshController,
  43. onRefresh: () {
  44. controller.requestHomeData();
  45. },
  46. child: CustomScrollView(
  47. slivers: [
  48. buildHeaderView(),
  49. buildTalkRecordTitle(),
  50. SliverToBoxAdapter(
  51. child: Container(
  52. decoration: BoxDecoration(
  53. gradient: LinearGradient(
  54. begin: Alignment.topCenter,
  55. end: Alignment.bottomCenter,
  56. colors: [
  57. "#FEFEFE".toColor(),
  58. "#FCFBFC".toColor()
  59. ],
  60. ),
  61. ),
  62. height: 0.3472222.sw,
  63. padding: EdgeInsets.only(bottom: 15.h),
  64. child: buildTalkRecord(),
  65. ),
  66. ),
  67. buildTalkTodoTitle(),
  68. buildTalkTodoContent(),
  69. buildSeeMoreView(),
  70. ],
  71. ),
  72. ),
  73. ))
  74. ],
  75. ),
  76. )
  77. ],
  78. );
  79. }
  80. @override
  81. bool immersive() {
  82. return true;
  83. }
  84. Widget buildHeaderView() {
  85. return SliverToBoxAdapter(
  86. child: Container(
  87. decoration: BoxDecoration(
  88. gradient: LinearGradient(
  89. begin: Alignment.topCenter,
  90. end: Alignment.bottomCenter,
  91. colors: [ColorName.white, "#EEEFFB".toColor()],
  92. ),
  93. ),
  94. padding:
  95. EdgeInsets.only(left: 12.w, right: 12.w, top: 8.h, bottom: 16.h),
  96. child: getHomeHeadView(
  97. key: controller.headGuideKey,
  98. recordClick: () {
  99. controller.onRecordClick();
  100. },
  101. pickerAudioFileClick: () {
  102. controller.onPickerAudioFile();
  103. },
  104. ),
  105. ),
  106. );
  107. }
  108. SliverToBoxAdapter buildSeeMoreView() {
  109. return SliverToBoxAdapter(
  110. child: Obx(() {
  111. return Visibility(
  112. visible: controller.agendaList.isNotEmpty,
  113. child: Container(
  114. alignment: Alignment.center,
  115. padding: const EdgeInsets.only(top: 12, bottom: 36).w,
  116. child: RichText(
  117. text: TextSpan(
  118. text: StringName.homeTalkTodo1.tr,
  119. style: TextStyle(
  120. color: ColorName.secondaryTextColor, fontSize: 12.sp),
  121. children: <TextSpan>[
  122. TextSpan(
  123. text: StringName.homeTalkTodo2.tr,
  124. style: TextStyle(
  125. color: ColorName.colorPrimary, fontSize: 12.sp),
  126. recognizer: TapGestureRecognizer()
  127. ..onTap = () {
  128. controller.onGoAgendaList();
  129. }),
  130. ],
  131. ),
  132. ),
  133. ),
  134. );
  135. }),
  136. );
  137. }
  138. SliverToBoxAdapter buildTalkTodoTitle() {
  139. return SliverToBoxAdapter(
  140. child: Container(
  141. decoration: BoxDecoration(
  142. gradient: LinearGradient(
  143. begin: Alignment.topCenter,
  144. end: Alignment.bottomCenter,
  145. colors: ["#FCFBFC".toColor(), "#F6F5F8".toColor()],
  146. ),
  147. ),
  148. child: Column(
  149. children: [
  150. SizedBox(height: 12.w),
  151. buildTitle(StringName.talkSummaryTodoTitle.tr, () {
  152. controller.onGoAgendaList();
  153. }),
  154. SizedBox(height: 12.w)
  155. ],
  156. ),
  157. ));
  158. }
  159. SliverToBoxAdapter buildTalkRecordTitle() {
  160. return SliverToBoxAdapter(
  161. child: Container(
  162. decoration: BoxDecoration(
  163. gradient: LinearGradient(
  164. begin: Alignment.topCenter,
  165. end: Alignment.bottomCenter,
  166. colors: [ColorName.white, "#FEFEFE".toColor()],
  167. ),
  168. ),
  169. padding: const EdgeInsets.symmetric(vertical: 12).w,
  170. child: buildTitle(StringName.homeTalkRecord.tr, () {
  171. controller.goTalkRecordPage();
  172. }),
  173. ),
  174. );
  175. }
  176. Row buildOperationBar() {
  177. return Row(children: [buildGoLogin(), const Spacer(), buildGoStore()]);
  178. }
  179. GestureDetector buildGoStore() {
  180. return GestureDetector(
  181. child: Container(
  182. decoration: BoxDecoration(
  183. gradient: LinearGradient(
  184. colors: ['#FFD3A8'.color, '#FFEDE1'.color, '#FFDBC2'.color],
  185. stops: const [0, 0.5, 1.0],
  186. begin: Alignment.bottomLeft,
  187. end: Alignment.topRight,
  188. ),
  189. borderRadius: BorderRadius.circular(100),
  190. ),
  191. padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8).w,
  192. child: Text(StringName.homeChargeTxt.tr,
  193. style: TextStyle(
  194. height: 1,
  195. fontSize: 13.sp,
  196. color: '#773C23'.color,
  197. fontWeight: FontWeight.bold)),
  198. ),
  199. onTap: () {
  200. // accountRepository.logout();
  201. // ToastUtil.showToast('GoStore');
  202. StorePage.start(fromType: StoreFromType.home);
  203. });
  204. }
  205. Widget buildGoLogin() {
  206. return Obx(() {
  207. return GestureDetector(
  208. onTap: () {
  209. if (controller.isLogin) {
  210. controller.showLoginDrawer();
  211. } else {
  212. controller.onLoginClick();
  213. }
  214. },
  215. child: Container(
  216. color: ColorName.transparent,
  217. child: Row(
  218. children: [
  219. SizedBox(
  220. width: 36.w,
  221. height: 36.w,
  222. child: controller.isLogin
  223. ? Assets.images.iconHomeLogged.image()
  224. : Assets.images.iconHomeNoLogin.image()),
  225. SizedBox(width: 8.w),
  226. Text(controller.loginTxt,
  227. style: TextStyle(
  228. fontWeight: FontWeight.bold,
  229. fontSize: 15.sp,
  230. color: ColorName.primaryTextColor)),
  231. SizedBox(width: 6.w),
  232. controller.isLogin
  233. ? const SizedBox.shrink()
  234. : SizedBox(
  235. height: 16.w,
  236. child: Assets.images.iconGoLoginArrow.image())
  237. ],
  238. ),
  239. ),
  240. );
  241. });
  242. }
  243. DecoratedBox buildBgBox() {
  244. return DecoratedBox(
  245. decoration: const BoxDecoration(color: ColorName.white),
  246. child: Container(
  247. height: double.infinity,
  248. ));
  249. }
  250. Widget buildTalkRecord() {
  251. return SizedBox(
  252. width: 1.sw,
  253. child: CustomScrollView(
  254. scrollDirection: Axis.horizontal,
  255. slivers: [
  256. SliverToBoxAdapter(child: SizedBox(width: 12.w)),
  257. Obx(() {
  258. return SliverList.builder(
  259. itemBuilder: _builderTalkItem,
  260. itemCount: controller.talkList.length >= 10
  261. ? 10
  262. : controller.talkList.length);
  263. }),
  264. ],
  265. ),
  266. );
  267. }
  268. Widget _builderTalkItem(BuildContext context, int index) {
  269. return Obx(() {
  270. TalkBean? item = controller.talkList[index];
  271. return _buildTalkView(item, onLongPressStart: (details) {
  272. if (!accountRepository.isLogin.value) {
  273. return;
  274. }
  275. showTalkPopup(details.globalPosition, Alignment.bottomRight,
  276. onRename: () {
  277. showRenameTalkDialog(item);
  278. }, onDelete: () {
  279. showDeleteTalkDialog(item);
  280. });
  281. }, onItemClick: () {
  282. controller.onTalkItemClick(item);
  283. });
  284. });
  285. }
  286. Widget _builderAgendaItem(BuildContext context, int index) {
  287. return Obx(() {
  288. Agenda item = controller.agendaList[index];
  289. return GestureDetector(
  290. onTap: () {
  291. controller.onAgendaItemClick(item);
  292. },
  293. child: taskItemView(
  294. item,
  295. onThinkingClick: () {
  296. ChatPage.startByTalkId(
  297. item.isExample == true
  298. ? ChatFromType.fromTalkExample
  299. : ChatFromType.fromAnalysisBtn,
  300. item.talkId,
  301. agenda: item);
  302. },
  303. onCheckClick: () {
  304. controller.agendaComplete(item);
  305. },
  306. ),
  307. );
  308. });
  309. }
  310. SliverToBoxAdapter buildGoRecordView() {
  311. return SliverToBoxAdapter(
  312. child: GestureDetector(
  313. onTap: () => Get.toNamed(RoutePath.record),
  314. child: Container(
  315. margin: EdgeInsets.only(right: 8.w),
  316. decoration: BoxDecoration(
  317. color: Colors.white,
  318. border: Border.all(color: '#EBEBFF'.toColor(), width: 1),
  319. borderRadius: BorderRadius.circular(8.0),
  320. ),
  321. child: SizedBox(
  322. width: 100.w,
  323. height: double.infinity,
  324. child: Stack(
  325. children: [
  326. Positioned(
  327. right: 0,
  328. bottom: 0,
  329. child: SizedBox(
  330. width: 48.w,
  331. child: Assets.images.bgHomeQuickAudio.image(),
  332. ),
  333. ),
  334. Positioned.fill(
  335. child: Align(
  336. alignment: Alignment.center,
  337. child: Column(
  338. children: [
  339. SizedBox(height: 20.h),
  340. SizedBox(
  341. width: 32.w,
  342. height: 32.w,
  343. child: Assets.images.iconAddTalk.image()),
  344. SizedBox(height: 6.h),
  345. Text(StringName.homeTalkAudio.tr,
  346. style: TextStyle(
  347. fontSize: 14.sp,
  348. color: ColorName.colorPrimary,
  349. fontWeight: FontWeight.bold)),
  350. Builder(builder: (context) {
  351. controller.todoTargetContext = context;
  352. return Text(StringName.homeTalkQuickAudio.tr,
  353. style: TextStyle(
  354. fontSize: 10.sp,
  355. color: ColorName.secondaryTextColor));
  356. })
  357. ],
  358. ),
  359. )),
  360. ],
  361. ),
  362. ),
  363. ),
  364. ));
  365. }
  366. Widget _buildTalkView(TalkBean item,
  367. {VoidCallback? onItemClick,
  368. GestureLongPressStartCallback? onLongPressStart}) {
  369. return GestureDetector(
  370. onTap: onItemClick,
  371. onLongPressStart: onLongPressStart,
  372. child: Container(
  373. width: 258.w,
  374. margin: EdgeInsets.only(right: 8.w),
  375. decoration: BoxDecoration(
  376. color: Colors.white,
  377. border: Border.all(color: '#F0F0F0'.toColor(), width: 2),
  378. borderRadius: const BorderRadius.only(
  379. topLeft: Radius.circular(12),
  380. topRight: Radius.circular(24),
  381. bottomRight: Radius.circular(12),
  382. bottomLeft: Radius.circular(12)),
  383. ),
  384. padding: EdgeInsets.only(left: 10.w, right: 16.w),
  385. child: Row(
  386. crossAxisAlignment: CrossAxisAlignment.start,
  387. children: [
  388. Padding(
  389. padding: const EdgeInsets.only(top: 14).h,
  390. child: Stack(
  391. children: [
  392. SizedBox(
  393. width: 35.w,
  394. height: 40.w,
  395. child: Assets.images.iconFilesFile.image()),
  396. Visibility(
  397. visible: item.isExample.isTrue,
  398. child: Container(
  399. margin: const EdgeInsets.only(top: 32).w,
  400. decoration: BoxDecoration(
  401. color: "#B2BAC4".toColor(),
  402. borderRadius: BorderRadius.circular(4)),
  403. padding: const EdgeInsets.symmetric(
  404. horizontal: 5.5, vertical: 2)
  405. .w,
  406. child: Text(StringName.homeTalkExample.tr,
  407. style: TextStyle(
  408. height: 1,
  409. fontSize: 12.sp,
  410. color: ColorName.white)),
  411. ),
  412. ),
  413. ],
  414. ),
  415. ),
  416. SizedBox(width: 8.w),
  417. Expanded(
  418. child: Column(
  419. mainAxisAlignment: MainAxisAlignment.center,
  420. crossAxisAlignment: CrossAxisAlignment.start,
  421. children: [
  422. Text(item.title.value.orEmpty,
  423. maxLines: 1,
  424. overflow: TextOverflow.ellipsis,
  425. style: TextStyle(
  426. fontSize: 15.sp,
  427. color: ColorName.primaryTextColor,
  428. fontWeight: FontWeight.bold)),
  429. SizedBox(height: 5.h),
  430. Text(
  431. item.summary.value.orEmpty,
  432. style: TextStyle(
  433. fontSize: 12.sp, color: ColorName.secondaryTextColor),
  434. overflow: TextOverflow.ellipsis,
  435. maxLines: 2,
  436. ),
  437. SizedBox(height: 8.h),
  438. Row(
  439. crossAxisAlignment: CrossAxisAlignment.center,
  440. children: [
  441. Text(item.duration.toFormattedDuration(),
  442. style: TextStyle(
  443. fontSize: 12.sp,
  444. color: ColorName.tertiaryTextColor)),
  445. SizedBox(width: 6.w),
  446. Container(
  447. width: 1,
  448. height: 9,
  449. color: ColorName.tertiaryTextColor),
  450. SizedBox(width: 6.w),
  451. Text(item.createTime.orEmpty,
  452. style: TextStyle(
  453. fontSize: 12.sp,
  454. color: ColorName.tertiaryTextColor))
  455. ],
  456. )
  457. ],
  458. ),
  459. )
  460. ],
  461. )),
  462. );
  463. }
  464. Widget buildTitle(String titleName, VoidCallback? onTap) {
  465. return Padding(
  466. padding: const EdgeInsets.symmetric(horizontal: 12).w,
  467. child: Row(
  468. crossAxisAlignment: CrossAxisAlignment.center,
  469. children: [
  470. Text(titleName,
  471. style: TextStyle(
  472. fontWeight: FontWeight.bold,
  473. fontSize: 17.sp,
  474. color: ColorName.primaryTextColor)),
  475. const Spacer(),
  476. Visibility(
  477. visible: onTap == null ? false : true,
  478. child: GestureDetector(
  479. onTap: onTap,
  480. child: Padding(
  481. padding: const EdgeInsets.symmetric(vertical: 6).w,
  482. child: Row(
  483. crossAxisAlignment: CrossAxisAlignment.center,
  484. children: [
  485. Text(
  486. StringName.homeTalkSeeAll.tr,
  487. style: TextStyle(
  488. fontSize: 13.sp, color: ColorName.tertiaryTextColor),
  489. ),
  490. Container(
  491. margin: const EdgeInsets.only(bottom: 1),
  492. width: 16.w,
  493. height: 16.w,
  494. child: Assets.images.iconHomeTalkArrow.image()),
  495. ],
  496. ),
  497. ),
  498. ),
  499. )
  500. ],
  501. ),
  502. );
  503. }
  504. void showRenameTalkDialog(TalkBean item) {
  505. reNameDialog(StringName.talkRenameTitle.tr, item.title.value,
  506. hintTxt: StringName.talkRenameTitleHint.tr,
  507. maxLength: 15, returnBuilder: (newName) {
  508. controller.requestName(newName, item);
  509. });
  510. }
  511. void showDeleteTalkDialog(TalkBean item) {
  512. talkDeleteDialog(item.id, item.title.value, returnBuilder: () {
  513. controller.requestDelete(item);
  514. });
  515. }
  516. Widget buildTalkTodoContent() {
  517. return Obx(() {
  518. if (controller.agendaList.isEmpty) {
  519. return SliverToBoxAdapter(child: buildAgendaEmptyView(50.h));
  520. } else {
  521. return SliverList.builder(
  522. itemBuilder: _builderAgendaItem,
  523. itemCount: controller.agendaList.length >= 10
  524. ? 10
  525. : controller.agendaList.length);
  526. }
  527. });
  528. }
  529. }
  530. Widget buildAgendaEmptyView(double top, {bool isVisible = true}) {
  531. return Visibility(
  532. visible: isVisible,
  533. child: Container(
  534. width: double.infinity,
  535. padding: EdgeInsets.symmetric(vertical: top),
  536. child: Column(
  537. children: [
  538. SizedBox(
  539. width: 100.w,
  540. height: 100.w,
  541. child: Assets.images.iconNoTask.image()),
  542. SizedBox(height: 4.h),
  543. Text(StringName.agendaNoData.tr,
  544. style: TextStyle(
  545. color: ColorName.secondaryTextColor, fontSize: 14.sp)),
  546. ],
  547. ),
  548. ),
  549. );
  550. }
  551. Widget getHomeHeadView(
  552. {GlobalKey? key,
  553. VoidCallback? recordClick,
  554. VoidCallback? pickerAudioFileClick}) {
  555. return Row(
  556. key: key,
  557. children: [
  558. Expanded(
  559. child: _buildHeaderCard(
  560. StringName.homeTalkAudio.tr,
  561. StringName.homeTalkQuickAudio.tr,
  562. Assets.images.iconHomeTalkRecordCard.image().image, [
  563. "#1763F9".toColor(),
  564. "#28B2FF".toColor(),
  565. ], onTap: () {
  566. recordClick?.call();
  567. }),
  568. ),
  569. SizedBox(width: 8.w),
  570. Expanded(
  571. child: _buildHeaderCard(
  572. StringName.homeTalkImportAudio.tr,
  573. StringName.homeTalkAnalyzeLocalAudio.tr,
  574. Assets.images.iconHomeTalkSelectCard.image().image, [
  575. "#5869ED".toColor(),
  576. "#6E8AF7".toColor(),
  577. ], onTap: () {
  578. pickerAudioFileClick?.call();
  579. }),
  580. ),
  581. ],
  582. );
  583. }
  584. Widget _buildHeaderCard(
  585. String title,
  586. String content,
  587. ImageProvider imageProvider,
  588. List<Color> bgColor, {
  589. VoidCallback? onTap,
  590. }) {
  591. return GestureDetector(
  592. onTap: onTap,
  593. child: AspectRatio(
  594. aspectRatio: 164 / 80,
  595. child: Container(
  596. decoration: BoxDecoration(
  597. gradient: LinearGradient(
  598. begin: Alignment.topLeft,
  599. end: Alignment.bottomRight,
  600. colors: bgColor,
  601. ),
  602. borderRadius: BorderRadius.circular(12),
  603. ),
  604. child: Stack(
  605. children: [
  606. Align(
  607. alignment: Alignment.centerRight,
  608. child: AspectRatio(
  609. aspectRatio: 1,
  610. child: SizedBox(
  611. height: double.infinity,
  612. child: Image(image: imageProvider)),
  613. ),
  614. ),
  615. Align(
  616. alignment: Alignment.centerLeft,
  617. child: IntrinsicHeight(
  618. child: Container(
  619. margin: EdgeInsets.only(left: 12.w),
  620. child: Column(
  621. crossAxisAlignment: CrossAxisAlignment.start,
  622. children: [
  623. Text(title,
  624. style: TextStyle(
  625. fontSize: 16.sp,
  626. color: ColorName.white,
  627. fontWeight: FontWeight.bold)),
  628. SizedBox(height: 4.h),
  629. Text(content,
  630. style: TextStyle(
  631. fontSize: 12.sp, color: ColorName.white80)),
  632. ],
  633. ),
  634. ),
  635. ),
  636. )
  637. ],
  638. ),
  639. ),
  640. ),
  641. );
  642. }