home.dart 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. import 'dart:io';
  2. import 'package:cached_network_image/cached_network_image.dart';
  3. import 'package:carousel_slider/carousel_controller.dart' as cslider;
  4. import 'package:carousel_slider/carousel_slider.dart';
  5. import 'package:eitc_erm_app/select_clinic.dart';
  6. import 'package:eitc_erm_app/select_doctor.dart';
  7. import 'package:eitc_erm_app/select_patient.dart';
  8. import 'package:eitc_erm_app/utils/Component.dart';
  9. import 'package:eitc_erm_app/utils/Constants.dart';
  10. import 'package:eitc_erm_app/utils/Utils.dart';
  11. import 'package:eitc_erm_app/utils/logger.dart';
  12. import 'package:eitc_erm_app/widget/circular_loading.dart';
  13. import 'package:eitc_erm_app/widget/image_error.dart';
  14. import 'package:eitc_erm_app/widget/loading.dart';
  15. import 'package:eitc_erm_app/widget/marquee_widget.dart';
  16. import 'package:eitc_erm_app/widget/user_header.dart';
  17. import 'package:flutter/material.dart';
  18. import 'package:http/http.dart' as http;
  19. import 'Introduce.dart';
  20. import 'bean/doctor_list.dart';
  21. import 'bean/hospital_detail.dart';
  22. import 'bean/notice_manage_list.dart';
  23. import 'bean/user_info.dart';
  24. import 'doctor_detail.dart';
  25. import 'login.dart';
  26. import 'notice_list.dart';
  27. /*void main() {
  28. WidgetsFlutterBinding.ensureInitialized();
  29. runApp(Home());
  30. }*/
  31. late BuildContext parentCxt;
  32. class Home extends StatefulWidget {
  33. Home(BuildContext context) {
  34. parentCxt = context;
  35. }
  36. @override
  37. State<StatefulWidget> createState() => HomeState();
  38. }
  39. class HomeState extends State<Home> {
  40. NoticeManageList mNoticeManageList = new NoticeManageList();
  41. List<String> noticeList = [" "];
  42. @override
  43. void initState() {
  44. super.initState();
  45. getInfo();
  46. noticeManageList();
  47. }
  48. @override
  49. Widget build(BuildContext context) {
  50. return Scaffold(
  51. appBar: AppBar(
  52. backgroundColor: Global.StatusBarColor,
  53. title: Text(
  54. Global.clinic,
  55. style: const TextStyle(color: Colors.white),
  56. ),
  57. centerTitle: true,
  58. actions: <Widget>[
  59. IconButton(
  60. color: Colors.white,
  61. icon: const Icon(
  62. Icons.loop,
  63. ),
  64. onPressed: () {
  65. Navigator.pushReplacement(
  66. context,
  67. MaterialPageRoute(
  68. builder: (context) => SelectClinic("fromHome")),
  69. );
  70. }),
  71. ],
  72. ),
  73. body: SafeArea(
  74. child: SingleChildScrollView(
  75. child: Column(
  76. mainAxisSize: MainAxisSize.min, //横向大小
  77. crossAxisAlignment: CrossAxisAlignment.center, //纵向对齐方式
  78. children: [
  79. const Banner(),
  80. const Padding(
  81. padding: EdgeInsets.all(10),
  82. child: Row(children: [
  83. SizedBox(width: 1),
  84. Text(
  85. '▊',
  86. style: TextStyle(fontSize: 15, color: Colors.blue),
  87. ),
  88. SizedBox(width: 20),
  89. Text(
  90. '门诊服务',
  91. style: TextStyle(fontSize: 15),
  92. ),
  93. ])),
  94. Row(
  95. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  96. //元素与空白互相间隔
  97. children: <Widget>[
  98. Padding(
  99. padding: const EdgeInsets.all(10),
  100. child: GestureDetector(
  101. onTap: () {
  102. Navigator.push(
  103. context,
  104. MaterialPageRoute(
  105. builder: (context) => SelectPatient("")),
  106. );
  107. },
  108. child: Container(
  109. decoration: BoxDecoration(
  110. color: Colors.green[300],
  111. border: Border.all(color: Colors.green)),
  112. width: 160,
  113. child: const Column(
  114. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  115. children: [
  116. SizedBox(height: 10),
  117. Icon(
  118. Icons.account_balance_wallet_outlined,
  119. color: Colors.white,
  120. ),
  121. SizedBox(height: 10),
  122. Text("预约挂号",
  123. style: TextStyle(
  124. fontSize: 15, color: Colors.white)),
  125. SizedBox(height: 10),
  126. Text("点击在线预约挂号",
  127. style: TextStyle(
  128. fontSize: 15, color: Colors.white)),
  129. SizedBox(height: 10),
  130. ],
  131. ),
  132. ),
  133. ),
  134. ),
  135. Padding(
  136. padding: const EdgeInsets.all(10),
  137. child: GestureDetector(
  138. onTap: () {
  139. // if(Global.selectDoctor == -1) {
  140. Navigator.push(
  141. context,
  142. MaterialPageRoute(
  143. builder: (context) => SelectDoctor("")),
  144. );
  145. /*} else {
  146. */ /*Navigator.push(
  147. context,
  148. MaterialPageRoute(builder: (context) => ChatHome()),
  149. );*/ /*
  150. Navigator.push(
  151. context,
  152. MaterialPageRoute(builder: (context) => OnlineConsultationDetail()),
  153. );
  154. }*/
  155. },
  156. child: Container(
  157. decoration: BoxDecoration(
  158. color: Colors.orange[300],
  159. border: Border.all(color: Colors.orange)),
  160. width: 160,
  161. child: const Column(children: [
  162. SizedBox(height: 10),
  163. Icon(
  164. Icons.chat_outlined,
  165. color: Colors.white,
  166. ),
  167. SizedBox(height: 10),
  168. Text("线上咨询",
  169. style: TextStyle(
  170. fontSize: 15, color: Colors.white)),
  171. SizedBox(height: 10),
  172. Text("点击在线咨询医生",
  173. style: TextStyle(
  174. fontSize: 15, color: Colors.white)),
  175. SizedBox(height: 10),
  176. ]),
  177. ),
  178. ),
  179. ),
  180. ],
  181. ),
  182. Padding(
  183. padding: const EdgeInsets.only(left: 6),
  184. child: Row(children: [
  185. const Icon(
  186. Icons.notifications,
  187. color: Colors.lightGreen,
  188. size: 18,
  189. ),
  190. const SizedBox(width: 18),
  191. Expanded(
  192. child: Container(
  193. alignment: Alignment.center,
  194. width: double.infinity,
  195. height: 24,
  196. child: noticeList.isNotEmpty
  197. ? buildMarqueeWidget(noticeList)
  198. : const Text(
  199. '',
  200. ),
  201. ),
  202. ),
  203. ])),
  204. const Padding(
  205. padding: EdgeInsets.all(10),
  206. child: Row(
  207. // mainAxisAlignment: MainAxisAlignment.spaceBetween,
  208. children: [
  209. SizedBox(width: 1),
  210. Text(
  211. '▊',
  212. style: TextStyle(fontSize: 15, color: Colors.blue),
  213. ),
  214. SizedBox(width: 20),
  215. Text(
  216. '医生推荐',
  217. style: TextStyle(fontSize: 15),
  218. ),
  219. /*SizedBox(width: 190),
  220. Text(
  221. '查看更多>>',
  222. style:
  223. const TextStyle(fontSize: 13, color: Colors.grey),
  224. ),*/
  225. ])),
  226. const DoctorRecommend(),
  227. ],
  228. ),
  229. ),
  230. ));
  231. }
  232. MarqueeWidget buildMarqueeWidget(List<String> loopList) {
  233. ///上下轮播 安全提示
  234. return MarqueeWidget(
  235. //子Item构建器
  236. itemBuilder: (BuildContext context, int index) {
  237. String itemStr = loopList[index];
  238. //通常可以是一个 Text文本
  239. return GestureDetector(
  240. onTap: () {
  241. Navigator.push(
  242. context,
  243. MaterialPageRoute(
  244. builder: (context) => NoticeList(mNoticeManageList)),
  245. );
  246. },
  247. child: Text(
  248. itemStr,
  249. style: const TextStyle(
  250. fontSize: 16,
  251. color: Colors.lightGreen,
  252. fontWeight: FontWeight.w700,
  253. ),
  254. ));
  255. },
  256. //循环的提示消息数量
  257. count: loopList.length,
  258. );
  259. }
  260. Future<UserInfo?> getInfo() async {
  261. logd(Global.token);
  262. final response =
  263. await http.get(Uri.parse('${Global.BaseUrl}user/getInfo'), headers: jsonHeaders(withToken: true));
  264. if (response.statusCode == 200) {
  265. final json = decodeBodyToJson(response.bodyBytes);
  266. logd("用户信息=$json");
  267. UserInfo mUserInfo = UserInfo.fromJson(json);
  268. if (mUserInfo.code == Global.responseSuccessCode) {
  269. final data = mUserInfo.data;
  270. if (data != null) {
  271. Global.userId = data.id!;
  272. Global.userIdentificationCard = data.identificationCard!;
  273. Global.loginPhoneNo = data.phoneNumber!;
  274. Utils.saveData(
  275. "userIdentificationCard", Global.userIdentificationCard);
  276. Utils.saveData("loginPhoneNo", Global.loginPhoneNo);
  277. }
  278. } else {
  279. Component.toast(mUserInfo.msg.toString(), 0);
  280. }
  281. return mUserInfo;
  282. } else {
  283. Component.toast("获取用户信息出错!", 0);
  284. }
  285. }
  286. Future<NoticeManageList?> noticeManageList() async {
  287. Map<String, String> headers = {
  288. 'token': '${Global.token}',
  289. };
  290. final response = await http.get(
  291. Uri.parse('${Global.BaseUrl}notice-manage/list'),
  292. headers: headers);
  293. logd(response.body.toString());
  294. if (response.statusCode == 200) {
  295. final json = decodeBodyToJson(response.bodyBytes);
  296. logd("公告=$json");
  297. mNoticeManageList = NoticeManageList.fromJson(json);
  298. if (mNoticeManageList.code == Global.responseSuccessCode) {
  299. noticeList.clear();
  300. for (int i = 0; i < mNoticeManageList.data!.length; i++) {
  301. String line = mNoticeManageList.data![i].noticeTitle.toString();
  302. if (line.length > 18) line = "${line.substring(0, 18)}...";
  303. noticeList.add(line);
  304. }
  305. setState(() {});
  306. return mNoticeManageList;
  307. } else {
  308. Component.toast(mNoticeManageList.msg.toString(), 0);
  309. }
  310. return null;
  311. } else {
  312. Component.toast("获取通知出错!", 0);
  313. return null;
  314. }
  315. }
  316. }
  317. class Banner extends StatefulWidget {
  318. const Banner({super.key});
  319. @override
  320. BannerState createState() => BannerState();
  321. }
  322. class BannerState extends State<Banner> {
  323. HospitalDetail mHospitalDetail = new HospitalDetail();
  324. @override
  325. void initState() {
  326. super.initState();
  327. fetchHospitalData();
  328. }
  329. Future<HospitalDetail?> fetchHospitalData() async {
  330. logd(Global.token);
  331. final response = await http
  332. .get(Uri.parse('${Global.BaseUrl}clinic-info/detail'), headers: jsonHeaders(withToken: true));
  333. if (response.statusCode == 200) {
  334. final json = decodeBodyToJson(response.bodyBytes);
  335. logd("诊所详情=$json");
  336. mHospitalDetail = HospitalDetail.fromJson(json);
  337. if (mHospitalDetail.code == Global.responseSuccessCode) {
  338. if (mHospitalDetail.data != null) {
  339. if (mHospitalDetail.data?.bannerPic1 != null) {
  340. list.add(Global.ImageUrl + mHospitalDetail.data!.bannerPic1!);
  341. } else {
  342. list.add("");
  343. }
  344. if (mHospitalDetail.data?.bannerPic2 != null) {
  345. list.add(Global.ImageUrl + mHospitalDetail.data!.bannerPic2!);
  346. }
  347. if (mHospitalDetail.data?.bannerPic3 != null) {
  348. list.add(Global.ImageUrl + mHospitalDetail.data!.bannerPic3!);
  349. }
  350. }
  351. setState(() {});
  352. } else if (mHospitalDetail.code == 401 || mHospitalDetail.code == 403) {
  353. Navigator.pushReplacementNamed(parentCxt, '/login');
  354. } else {
  355. Component.toast(mHospitalDetail.msg.toString(), 0);
  356. }
  357. return mHospitalDetail;
  358. } else {
  359. Component.toast("获取顶部滚动条出错!", 0);
  360. }
  361. }
  362. final List list = [];
  363. int _current = 0;
  364. final cslider.CarouselSliderController _controller =
  365. cslider.CarouselSliderController();
  366. @override
  367. Widget build(BuildContext context) {
  368. if (list.isEmpty) {
  369. return const SizedBox(
  370. height: 250,
  371. );
  372. }
  373. if (list.length == 1) {
  374. return SizedBox(
  375. height: 250,
  376. child: GestureDetector(
  377. behavior: HitTestBehavior.opaque,
  378. onTap: () => _gotoHspitalDetail(context),
  379. child: _getBannerImage(list[0]),
  380. ),
  381. );
  382. }
  383. return Stack(
  384. alignment: AlignmentDirectional.bottomCenter,
  385. children: [
  386. CarouselSlider.builder(
  387. carouselController: _controller,
  388. options: CarouselOptions(
  389. height: 250,
  390. autoPlay: true,
  391. viewportFraction: 1.0,
  392. autoPlayInterval: const Duration(seconds: 3),
  393. onPageChanged: (index, reason) {
  394. setState(() {
  395. _current = index;
  396. });
  397. },
  398. ),
  399. itemCount: list.length,
  400. itemBuilder: (BuildContext context, int index, int pageViewIndex) {
  401. return GestureDetector(
  402. behavior: HitTestBehavior.opaque,
  403. onTap: () {
  404. switch (index) {
  405. case 0:
  406. _gotoHspitalDetail(context);
  407. break;
  408. case 1:
  409. break;
  410. case 2:
  411. break;
  412. }
  413. },
  414. child: _getBannerImage(list[index]),
  415. );
  416. },
  417. ),
  418. Row(
  419. mainAxisAlignment: MainAxisAlignment.center,
  420. children: list.asMap().entries.map((entry) {
  421. return GestureDetector(
  422. onTap: () => _controller.animateToPage(entry.key),
  423. child: Container(
  424. width: 10.0,
  425. height: 10.0,
  426. margin:
  427. const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0),
  428. decoration: BoxDecoration(
  429. shape: BoxShape.circle,
  430. color: (Theme.of(context).brightness == Brightness.dark
  431. ? Colors.white
  432. : Colors.black)
  433. .withOpacity(_current == entry.key ? 0.9 : 0.4)),
  434. ),
  435. );
  436. }).toList(),
  437. ),
  438. ],
  439. );
  440. }
  441. void _gotoHspitalDetail(BuildContext context) {
  442. Navigator.push(
  443. context,
  444. MaterialPageRoute(
  445. builder: (context) =>
  446. Introduce(hospitalDetail: mHospitalDetail, key: const Key(''))),
  447. );
  448. }
  449. Widget _getBannerImage(String url) {
  450. return CachedNetworkImage(
  451. imageUrl: url,
  452. width: double.infinity,
  453. fit: BoxFit.cover,
  454. progressIndicatorBuilder: (ctx, _, progress) => Circularloading(
  455. value: progress.progress,
  456. ),
  457. errorWidget: (ctx, _, __) => const ImageError(
  458. size: 80,
  459. ),
  460. );
  461. }
  462. }
  463. DoctorListEntity? data = DoctorListEntity();
  464. class DoctorRecommend extends StatefulWidget {
  465. const DoctorRecommend({super.key});
  466. @override
  467. State<StatefulWidget> createState() => DoctorRecommendState();
  468. }
  469. class DoctorRecommendState extends State<DoctorRecommend> {
  470. late Future<DoctorListEntity?> _future;
  471. late BuildContext cxt;
  472. @override
  473. void initState() {
  474. super.initState();
  475. _future = fetchData();
  476. }
  477. Future<DoctorListEntity?> fetchData() async {
  478. Map<String, String> headers = {
  479. 'token': Global.token,
  480. };
  481. final response =
  482. await http.get(Uri.parse('${Global.BaseUrl}doctor/list'), headers: jsonHeaders(withToken: true));
  483. logd(Uri.parse('${Global.BaseUrl}doctor/list'));
  484. if (response.statusCode == 200) {
  485. final json = decodeBodyToJson(response.bodyBytes);
  486. logd("推荐医生=$json");
  487. DoctorListEntity mDoctorListEntity = DoctorListEntity.fromJson(json);
  488. if (mDoctorListEntity.code == Global.responseSuccessCode) {
  489. return mDoctorListEntity;
  490. } else if (mDoctorListEntity.code == 401 ||
  491. mDoctorListEntity.code == 403) {
  492. Navigator.of(context).pop();
  493. Navigator.push(
  494. context,
  495. MaterialPageRoute(builder: (context) => Login()),
  496. );
  497. return null;
  498. } else {
  499. Component.toast(mDoctorListEntity.msg.toString(), 0);
  500. return null;
  501. }
  502. } else {
  503. Component.toast("出错了,请稍后再试!", 0);
  504. }
  505. }
  506. @override
  507. Widget build(BuildContext context) {
  508. cxt = context;
  509. return Center(
  510. child: FutureBuilder<DoctorListEntity?>(
  511. future: _future,
  512. builder: (context, snapshot) {
  513. if (snapshot.hasData) {
  514. data = snapshot.data;
  515. return GridView.builder(
  516. padding: const EdgeInsets.all(10),
  517. shrinkWrap: true,
  518. physics: const NeverScrollableScrollPhysics(),
  519. gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
  520. //注意此行
  521. crossAxisCount: 2, //每行 widget 数量
  522. crossAxisSpacing: 10, //widget 水平之间的距离
  523. mainAxisSpacing: 10, //widget 垂直之间的距离
  524. ),
  525. itemCount: data?.data?.length,
  526. // itemCount: 2,
  527. itemBuilder: _customWidget,
  528. );
  529. } else if (snapshot.hasError) {
  530. return Text('Error: ${snapshot.error}');
  531. }
  532. return const ColorLoader();
  533. },
  534. ),
  535. );
  536. }
  537. Widget _customWidget(BuildContext context, int index) {
  538. return GestureDetector(
  539. onTap: () {
  540. Global.doctor = data!;
  541. Global.selectDoctor = index;
  542. Navigator.push(
  543. cxt,
  544. MaterialPageRoute(
  545. builder: (context) => DoctorDetail(
  546. doctorListEntity: data, key: const Key(''), which: index)),
  547. );
  548. },
  549. child: Container(
  550. decoration: BoxDecoration(
  551. border: Border.all(color: Colors.grey),
  552. borderRadius: BorderRadius.circular(8),
  553. color: Colors.white,
  554. ),
  555. child: Column(
  556. children: [
  557. const SizedBox(
  558. height: 10,
  559. ),
  560. UserHeader(
  561. url: "${Global.ImageUrl}${data?.data?[index].avatar}",
  562. ),
  563. Column(
  564. mainAxisSize: MainAxisSize.min,
  565. children: [
  566. Text(
  567. data?.data?[index].nickName ?? "",
  568. style: Theme.of(context).textTheme.titleMedium,
  569. ),
  570. Text(
  571. "${data?.data?[index].deptName ?? ""} ${data?.data?[index].postNames ?? ""}",
  572. overflow: TextOverflow.ellipsis,
  573. style: Theme.of(context).textTheme.bodyMedium,
  574. )
  575. ],
  576. ),
  577. const Spacer(),
  578. Padding(
  579. padding: const EdgeInsets.only(bottom: 10),
  580. child: _getProficientWidget(
  581. context, data?.data?[index].doctorProficient),
  582. ),
  583. ],
  584. ),
  585. ),
  586. );
  587. }
  588. Widget _getProficientWidget(BuildContext context, String? proficients) {
  589. if (proficients == null || proficients.isEmpty) {
  590. return const SizedBox();
  591. }
  592. List<String> list = proficients.split(",");
  593. Widget row;
  594. if (list.isNotEmpty) {
  595. row = Row(
  596. mainAxisAlignment: MainAxisAlignment.center,
  597. children: [
  598. _getProficientItem(list, 0,
  599. list.length > 1 ? Alignment.centerRight : Alignment.center),
  600. Visibility(
  601. visible: list.length > 1,
  602. child: const SizedBox(
  603. width: 10,
  604. )),
  605. Visibility(
  606. visible: list.length > 1,
  607. child: _getProficientItem(list, 1, Alignment.centerLeft),
  608. ),
  609. ],
  610. );
  611. } else {
  612. row = const SizedBox();
  613. }
  614. return Padding(
  615. padding: const EdgeInsets.symmetric(horizontal: 5),
  616. child: row,
  617. );
  618. }
  619. Widget _getProficientItem(List<String> list, int index, Alignment alignment) {
  620. if (list.length <= index) {
  621. return const SizedBox();
  622. }
  623. return Expanded(
  624. child: Align(
  625. alignment: alignment,
  626. child: Container(
  627. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
  628. decoration: BoxDecoration(
  629. borderRadius: BorderRadius.circular(30),
  630. color: Colors.lightBlueAccent),
  631. child: Text(
  632. textAlign: TextAlign.center,
  633. maxLines: 1,
  634. overflow: TextOverflow.ellipsis,
  635. list[index],
  636. style: const TextStyle(color: Colors.white, fontSize: 10),
  637. ),
  638. ),
  639. ));
  640. }
  641. }