home.ts 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. import PageContainerBehavior from "../../core/behavior/page-container.behavior";
  2. import I18nBehavior from "../../i18n/behavior";
  3. import {
  4. DraggableSheetBehavior,
  5. getDraggableSheetContext,
  6. } from "../../core/behavior/draggableSheet.behavior";
  7. import { login } from "../../lib/logic";
  8. import { useRouteQuery } from "../../utils/route-query";
  9. import { appUpdate } from "../../lib/wx/update";
  10. import { addPatientOnlineRecordClockIn } from "./request";
  11. import { Get } from "../../lib/request/method";
  12. const { shared, Easing, timing } = wx.worklet;
  13. const offset = shared(0);
  14. const menus = ["shareAppMessage", "shareTimeline"];
  15. // pages/home/home.ts
  16. import {
  17. getPatients,
  18. healthReportMethod,
  19. healthIndexMethod,
  20. getSolarTerms,
  21. getShortScienceList,
  22. getPatientDescription,
  23. getNotDealLists,
  24. getCareList,
  25. } from "./request";
  26. import { toCertificationPage, toChats } from "./router";
  27. import { Post } from "../../lib/request/method";
  28. // import { useLocation } from "../../lib/use/use-location";
  29. Page({
  30. data: {
  31. i18n: {
  32. common: { title: '生活助理', zy: '' },
  33. home: { analysis: '评估', consult: '聊天', __showRecord__: false, __showDisplay__: false },
  34. report: { title: '报告' },
  35. healthTeach: { title: '' },
  36. },
  37. isShowComplete: true,
  38. careList: [],
  39. displayList: [] as {
  40. id: number;
  41. conditioningProgramType: string;
  42. operateBy?: string;
  43. operateTime: string;
  44. frequencyType: string; // 频次类型
  45. frequencyMeasure: string; // 总用量
  46. arrangeDate: string; // 下一次开始日期
  47. finishCount: number; // 完成量
  48. conditioningProgramName: string; // 调养计划名称
  49. conditioningProgramSupplierName: string; // 调养计划供应商名称
  50. expanded: boolean;
  51. }[],
  52. allExpanded: false,
  53. fixedHeight: "370rpx",
  54. tabbarValue: "/pages/home/home",
  55. tabbarHidden: false,
  56. pageHeight: "100vh", // 默认值
  57. popupList: [] as AnyArray,
  58. isShowPopup: false,
  59. isCompleting: false, // 防止重复点击标志
  60. patients: [] as (App.Patient.Model & { isDefault: "Y" | "N" })[],
  61. patient: null as App.Patient.Model | null,
  62. patientDescription: "",
  63. healthId: "",
  64. healthReport: { data: null, message: "", loading: true },
  65. healthIndex: { data: [], message: "" },
  66. position: {} as AnyObject,
  67. location: {} as AnyObject,
  68. solarTerms: {} as AnyObject,
  69. sheet: false,
  70. scienceList: [] as AnyArray,
  71. leftColumnList: [] as AnyArray,
  72. rightColumnList: [] as AnyArray,
  73. scienceListPage: 1,
  74. scienceListSize: 10,
  75. scienceListTotal: 0,
  76. scienceListLoading: false,
  77. scienceListHasMore: true,
  78. refreshing: false,
  79. _loaded: false,
  80. statusList: [] as AnyArray,
  81. switchType: "",
  82. carouselLoading: {} as Record<string | number, boolean>,
  83. carouselMediaList: [] as Array<{
  84. type: "image" | "video";
  85. src: string;
  86. }>,
  87. // 是否在咨询中
  88. isConsulting: false,
  89. // 是否有新消息
  90. hasNewMessage: false,
  91. },
  92. behaviors: [
  93. I18nBehavior,
  94. PageContainerBehavior,
  95. DraggableSheetBehavior(".draggable-sheet-wrapper"),
  96. ],
  97. async getCareLists() {
  98. const currentExpandedStates = this.data.displayList.map((item: any) => ({
  99. id: item.id,
  100. expanded: item.expanded,
  101. }));
  102. const res = await getCareList();
  103. if (res && res.length > 0) {
  104. res.forEach((item: any) => {
  105. item.carouselMediaList = [];
  106. // 添加photo
  107. if (item.photo) {
  108. item.carouselMediaList.push({
  109. type: "image",
  110. src: item.photo,
  111. });
  112. }
  113. // 添加itemImgFirst
  114. if (item?.itemImgFirst) {
  115. item.carouselMediaList.push({
  116. type: "image",
  117. src: item.itemImgFirst,
  118. });
  119. }
  120. // 添加itemVideoFirst
  121. if (item?.itemVideoFirst) {
  122. item.carouselMediaList.push({
  123. type: "video",
  124. src: item.itemVideoFirst,
  125. });
  126. }
  127. });
  128. // 先设置 careList
  129. this.setData({
  130. careList: res,
  131. });
  132. await this.updateDisplayList(currentExpandedStates);
  133. setTimeout(() => {
  134. res.forEach((item: any, index: number) => {
  135. if (item.carouselMediaList && item.carouselMediaList.length > 0) {
  136. this.setData({
  137. [`careList[${index}].carouselMediaList`]: [
  138. ...item.carouselMediaList,
  139. ],
  140. });
  141. // 同时更新 displayList
  142. const displayIndex = this.data.displayList.findIndex(
  143. (displayItem: any) => displayItem.id === item.id
  144. );
  145. if (displayIndex !== -1) {
  146. this.setData({
  147. [`displayList[${displayIndex}].carouselMediaList`]: [
  148. ...item.carouselMediaList,
  149. ],
  150. });
  151. }
  152. }
  153. });
  154. }, 100);
  155. }
  156. },
  157. async onLoad(options) {
  158. appUpdate();
  159. const query = useRouteQuery(options.scene!);
  160. if (query.ys) wx.setStorageSync("doctorId", query.ys);
  161. if (options.scene) wx.setStorageSync("scene", options.scene);
  162. this.initFabAnimated();
  163. if (options.switchType) {
  164. this.setData({
  165. switchType: options.switchType,
  166. });
  167. }
  168. },
  169. async onShow() {
  170. wx.showShareMenu({ withShareTicket: true, menus }).then();
  171. await this.load();
  172. // 如果是从一体机扫码进来的 有switchType值就直接跳转到注册页面
  173. if (
  174. this.data.switchType &&
  175. ((this.data.patient as any)?.isPerfectInfo ?? true)
  176. ) {
  177. wx.navigateTo({
  178. url: "/module/user/pages/user-certification/user-certification?type=home",
  179. });
  180. }
  181. // 检查咨询状态
  182. this.checkConsultationStatus();
  183. // 如果用户没有手机号每次进入页面都提示 点击跳到注册页补充
  184. // todo 要先判断用户有没有手机号 isPerfectInfo是true 就出来弹窗提示用户
  185. // if ((this.data.patient as any)?.isPerfectInfo ?? true) {
  186. // wx.showModal({
  187. // title: "提示",
  188. // content: "手机号为空,请补充",
  189. // success: (res) => {
  190. // if (res.confirm) {
  191. // wx.navigateTo({
  192. // url: "/module/user/pages/user-certification/user-certification?type=home",
  193. // });
  194. // }
  195. // },
  196. // });
  197. // }
  198. },
  199. onHide() {
  200. wx.hideShareMenu({ menus }).then();
  201. offset.value = timing(
  202. 0,
  203. { duration: 100, easing: (<any>Easing).linear },
  204. () => {
  205. "worklet";
  206. }
  207. );
  208. },
  209. onShareAppMessage(_opts): WechatMiniprogram.Page.ICustomShareContent {
  210. return {
  211. title: `健康为基,从容赏生活之美`,
  212. imageUrl: `https://wx.hzliuzhi.com/media/healthManager/wx/share.jpg`,
  213. path: `/pages/home/home`,
  214. };
  215. },
  216. onShareTimeline() {
  217. return {
  218. title: `健康为基,从容赏生活之美`,
  219. };
  220. },
  221. async updateDisplayList(
  222. preserveExpandedStates?: Array<{
  223. id: number;
  224. expanded: boolean;
  225. }>
  226. ) {
  227. const { careList, allExpanded } = this.data;
  228. let newDisplayList: any[] =
  229. allExpanded || careList.length <= 4 ? careList : careList.slice(0, 4);
  230. // 如果有保存的展开状态,则恢复它们
  231. if (preserveExpandedStates && preserveExpandedStates.length > 0) {
  232. newDisplayList = newDisplayList.map((item: any) => {
  233. const savedState = preserveExpandedStates.find(
  234. (state) => state.id === item.id
  235. );
  236. if (savedState) {
  237. return {
  238. ...item,
  239. expanded: savedState.expanded,
  240. };
  241. }
  242. return item;
  243. });
  244. }
  245. this.setData({
  246. displayList: newDisplayList,
  247. });
  248. },
  249. toggleAll() {
  250. this.setData(
  251. { allExpanded: !this.data.allExpanded },
  252. this.updateDisplayList
  253. );
  254. },
  255. toggleItem(e: any) {
  256. const index = e.currentTarget.dataset.index;
  257. const key = `displayList[${index}].expanded`;
  258. this.setData({ [key]: !this.data.displayList[index].expanded });
  259. },
  260. // 核销记录
  261. onRecord(e: any) {
  262. const id = e.currentTarget.dataset.id;
  263. if (id) {
  264. wx.navigateTo({
  265. url: `/module/care/pages/care/verifyRecord?id=${id}`,
  266. });
  267. } else {
  268. wx.showToast({
  269. title: "暂无核销记录",
  270. icon: "none",
  271. });
  272. }
  273. },
  274. onClose() {
  275. this.setData({
  276. isShowPopup: false,
  277. });
  278. },
  279. // 打卡
  280. pushCard(e: any) {
  281. const id = e.currentTarget.dataset.id;
  282. wx.navigateTo({
  283. url: `/module/article/pages/punch-card/punch-card?id=${id}`,
  284. });
  285. },
  286. calculatePageHeight() {
  287. const systemInfo = wx.getSystemInfoSync();
  288. const windowHeight = systemInfo.windowHeight; // 屏幕可用高度
  289. // 获取 tabbar 高度
  290. const query = wx.createSelectorQuery();
  291. query
  292. .select(".t-tabbar")
  293. .boundingClientRect((rect) => {
  294. if (rect) {
  295. const tabbarHeight = rect.height;
  296. const contentHeight = windowHeight - tabbarHeight;
  297. this.setData({
  298. pageHeight: `${contentHeight}px`,
  299. });
  300. }
  301. })
  302. .exec();
  303. },
  304. getNotDealList() {
  305. getNotDealLists().then((res) => {
  306. if (res.length > 0) {
  307. this.setData({
  308. popupList: res,
  309. isShowPopup: true,
  310. });
  311. } else {
  312. this.setData({
  313. popupList: [],
  314. isShowPopup: false,
  315. });
  316. }
  317. });
  318. },
  319. showFollowPopup() {
  320. this.getNotDealList();
  321. },
  322. onVisibleChange(e: { detail: { visible: any } }) {
  323. this.setData({
  324. isShowPopup: e.detail.visible,
  325. });
  326. },
  327. async goComplete(e: {
  328. currentTarget: { dataset: { page: string; id: number; title: string } };
  329. }) {
  330. if (this.data.isCompleting) {
  331. return;
  332. }
  333. this.setData({ isCompleting: true });
  334. try {
  335. const { title } = e.currentTarget.dataset;
  336. let page = e.currentTarget.dataset.page;
  337. let id = e.currentTarget.dataset.id;
  338. if (page === "/module/chats/pages/index/index") {
  339. if (title === "健康评估") {
  340. wx.setStorageSync("isAnalysis", 4);
  341. toChats("questionnaire", 2);
  342. } else {
  343. wx.setStorageSync("isAnalysis", 2);
  344. wx.setStorageSync("workId", id);
  345. wx.navigateTo({
  346. url: `${page}?component=questionnaire&messageType=1&id=${id}`,
  347. });
  348. }
  349. } else if (page === "/module/article/pages/science-info/science-info") {
  350. try {
  351. const res = await Get(`/psarticle/clickPsaNotice`, {
  352. params: { noticeSendRecordId: id },
  353. });
  354. const url = res?.data;
  355. const item = {
  356. url,
  357. };
  358. wx.navigateTo({
  359. url: `${page}`,
  360. }).then((res) => {
  361. res.eventChannel.emit("load", item);
  362. });
  363. } catch (error) {
  364. console.log(error);
  365. }
  366. } else {
  367. // 随访
  368. wx.redirectTo({ url: `${page}?id=${id}` });
  369. }
  370. } finally {
  371. setTimeout(() => {
  372. this.setData({ isCompleting: false });
  373. }, 500);
  374. }
  375. },
  376. // 随访评估报告已出
  377. goSeeFollowReport() {
  378. wx.navigateTo({ url: "/module/follow/pages/evaluation/report" });
  379. },
  380. async load(forceLogin = false) {
  381. try {
  382. await login(forceLogin);
  383. wx.showLoading({ title: "加载中" });
  384. const { patient } = await getPatients(/*this.data.patientId*/);
  385. // if (!patient) await toCertificationPage();
  386. if (!patient) {
  387. /*if (wx.getStorageSync("doctorId")) {
  388. toCertificationPage();
  389. }*/
  390. this.setData({
  391. "healthReport.loading": false,
  392. isShowComplete: true,
  393. });
  394. } else {
  395. this.setData({ patient });
  396. this.observerPatient(patient);
  397. }
  398. wx.hideLoading();
  399. } catch (error: any) {
  400. await wx
  401. .showModal({
  402. title: `加载失败`,
  403. content: `${error?.errMsg ?? error?.message ?? ""}`,
  404. showCancel: false,
  405. confirmText: `重新加载`,
  406. })
  407. .catch(() => { });
  408. await this.load(true);
  409. return;
  410. }
  411. // 加载健康宣教的文章
  412. this.loadScienceList(true);
  413. if (!this.data._loaded) {
  414. getSolarTerms()
  415. .then((solarTerms) => {
  416. this.setData({ solarTerms });
  417. })
  418. .catch(() => { });
  419. this.setData({ _loaded: true });
  420. }
  421. },
  422. async _getHealthReport() {
  423. wx.showLoading({ title: "加载中" });
  424. this.setData({ "healthReport.loading": true });
  425. try {
  426. const data = await healthReportMethod();
  427. if (!data) {
  428. this.setData({
  429. isShowComplete: true,
  430. });
  431. } else {
  432. this.setData({
  433. isShowComplete: false,
  434. });
  435. }
  436. this.setData({
  437. "healthReport.data": data,
  438. "healthReport.loading": false,
  439. healthId: data?.healthAnalysisReportId,
  440. });
  441. let arr2 = [
  442. [
  443. {
  444. title: "健康状态",
  445. value: data?.willillStateName ? `${data?.willillStateName}` : "",
  446. },
  447. {
  448. title: "程度",
  449. value: data?.willillDegreeName ? `${data?.willillDegreeName}` : "",
  450. },
  451. {
  452. title: "表现",
  453. value: data?.willillFunctionName
  454. ? `${data?.willillFunctionName}`
  455. : "",
  456. },
  457. ],
  458. [
  459. {
  460. title: "中医证素",
  461. value: data?.factorItemSummary ? `${data?.factorItemSummary}` : "",
  462. },
  463. ],
  464. [
  465. {
  466. title: "体质",
  467. value: data?.constitutionGroupName
  468. ? `${data?.constitutionGroupName}`
  469. : "",
  470. },
  471. ],
  472. ];
  473. this.setData({
  474. statusList: arr2,
  475. });
  476. } catch (error: any) {
  477. wx.showToast({
  478. title: error.errMsg || "加载失败",
  479. icon: "none",
  480. });
  481. this.setData({
  482. "healthReport.data": [],
  483. "healthReport.loading": false,
  484. "healthReport.message": error.errMsg,
  485. healthId: "",
  486. });
  487. }
  488. wx.hideLoading();
  489. },
  490. async _getAbnormalHealthIndex() {
  491. this.setData({ "healthIndex.loading": true });
  492. try {
  493. const data = await healthIndexMethod();
  494. this.setData({
  495. "healthIndex.data": data
  496. .map((item: AnyObject) => item.abnormalDesc)
  497. .filter(Boolean),
  498. "healthIndex.loading": false,
  499. });
  500. } catch (error: any) {
  501. this.setData({
  502. "healthIndex.data": [],
  503. "healthIndex.loading": false,
  504. "healthIndex.message": error.errMsg,
  505. });
  506. }
  507. },
  508. onBodyModel(event: WechatMiniprogram.TouchEvent) {
  509. if (event.detail?.position === "LB") {
  510. this.toReportPage();
  511. } else if (event.detail?.position === "item0") {
  512. const report = this.data.healthReport.data as unknown as AnyObject;
  513. this.setData({
  514. position: {
  515. LT: [
  516. "willillState",
  517. "willillDegree",
  518. "willillSocial",
  519. "willillFunction",
  520. ]
  521. .map((key) => {
  522. const title = report[`${key}Name`];
  523. const description = report[`${key}Description`];
  524. return title || description ? { title, description } : null;
  525. })
  526. .filter(Boolean),
  527. },
  528. });
  529. this.showDraggableSheet();
  530. } else if (event.detail?.position === "item2") {
  531. const report = this.data.healthReport.data as unknown as AnyObject;
  532. const get = (key: string) => ({ [key]: report[key] });
  533. this.setData({
  534. position: {
  535. RT: {
  536. ...get("constitutionGroupName"),
  537. ...get("constitutionGroupDefinition"),
  538. ...get("faceImg"),
  539. ...get("faceAnalysisResult"),
  540. ...get("tongueAnalysisResult"),
  541. ...get("upImg"),
  542. ...get("downImg"),
  543. },
  544. },
  545. });
  546. this.showDraggableSheet();
  547. } else if (event.detail?.position === "item1") {
  548. const report = this.data.healthReport.data as unknown as AnyObject;
  549. const get = (key: string) => ({ [key]: report[key] });
  550. this.setData({
  551. position: {
  552. RB: {
  553. ...get("diagnoseSyndromeSummary"),
  554. ...get("diagnoseSyndromes"),
  555. ...get("factorItemSummary"),
  556. ...get("factorItems"),
  557. },
  558. },
  559. });
  560. this.showDraggableSheet();
  561. } else if (event.detail?.position === "CT") {
  562. this.setData({
  563. position: {
  564. CT: this.data.healthIndex.data.map(
  565. (item, index) => `${index + 1}、${item}`
  566. ),
  567. },
  568. });
  569. this.showDraggableSheet();
  570. }
  571. },
  572. tabValue() {
  573. this.setData({
  574. position: {
  575. CT: this.data.healthIndex.data.map(
  576. (item, index) => `${index + 1}、${item}`
  577. ),
  578. },
  579. });
  580. this.showDraggableSheet();
  581. },
  582. initFabAnimated() {
  583. (<any>this).applyAnimatedStyle(".fab-wrapper", () => {
  584. "worklet";
  585. return { right: `${Math.min(offset.value, 36) - 36}px` };
  586. });
  587. (<any>this).applyAnimatedStyle(".fab-1", () => {
  588. "worklet";
  589. return { transform: `translateY(${-offset.value}px)` };
  590. });
  591. (<any>this).applyAnimatedStyle(".fab-2", () => {
  592. "worklet";
  593. return {
  594. transform: `translateX(${-offset.value}px) translateY(${-offset.value / 2
  595. }px)`,
  596. };
  597. });
  598. (<any>this).applyAnimatedStyle(".fab-3", () => {
  599. "worklet";
  600. return {
  601. transform: `translateX(${-offset.value}px) translateY(${offset.value / 2
  602. }px)`,
  603. };
  604. });
  605. (<any>this).applyAnimatedStyle(".fab-4", () => {
  606. "worklet";
  607. return { transform: `translateY(${offset.value}px)` };
  608. });
  609. },
  610. onFabTap() {
  611. const value = Math.abs(offset.value - 72);
  612. offset.value = timing(
  613. value,
  614. { duration: 500, easing: (<any>Easing).linear },
  615. () => {
  616. "worklet";
  617. if (offset.value > 0) offset.value = 72;
  618. }
  619. );
  620. },
  621. async toChatsPage() {
  622. if (!this.data.patient?.patientId) {
  623. try {
  624. await this.load();
  625. } catch (error: any) {
  626. wx.showModal({
  627. title: "出错了",
  628. content: error?.errMsg ?? error?.message ?? "错误,请重试",
  629. showCancel: false,
  630. });
  631. return;
  632. }
  633. }
  634. const page = "/module/chats/pages/index/index";
  635. wx.navigateTo({
  636. url: `${page}?component=guide&isShowGuide=true`,
  637. });
  638. wx.setStorageSync("isAnalysis", 3);
  639. },
  640. toSciencePage() {
  641. wx.navigateTo({ url: `/module/article/pages/science-list/science-list` });
  642. },
  643. async toReportPage() {
  644. // const { patient } = await getPatients(/*this.data.patientId*/);
  645. if (!this.data.patient) await toCertificationPage();
  646. else {
  647. const id = this.data.healthId;
  648. if (id)
  649. wx.navigateTo({ url: `/module/health/pages/report/report?id=${id}` });
  650. else wx.showToast({ title: "暂无分析报告", icon: "none" });
  651. }
  652. },
  653. onDraggableSizeUpdate(e: any) {
  654. "worklet";
  655. if (e.pixels < 1) {
  656. wx.worklet.runOnJS(this.hideDraggableSheet.bind(this))();
  657. }
  658. },
  659. showDraggableSheet() {
  660. getDraggableSheetContext.call(this).scrollTo({
  661. size: 0.5,
  662. pixels: 600,
  663. animated: true,
  664. duration: 300,
  665. easingFunction: "ease",
  666. });
  667. this.setData({ sheet: true });
  668. },
  669. hideDraggableSheet(event?: any) {
  670. if (event) {
  671. getDraggableSheetContext.call(this).scrollTo({
  672. size: 0,
  673. animated: true,
  674. duration: 300,
  675. easingFunction: "ease",
  676. });
  677. }
  678. this.setData({ position: {}, sheet: false });
  679. },
  680. async loadScienceList(reset = false) {
  681. if (this.data.scienceListLoading) {
  682. return;
  683. }
  684. if (!reset && !this.data.scienceListHasMore) {
  685. return;
  686. }
  687. if (reset) {
  688. this.setData({
  689. scienceListPage: 1,
  690. scienceListHasMore: true,
  691. scienceList: [],
  692. leftColumnList: [],
  693. rightColumnList: [],
  694. });
  695. }
  696. this.setData({ scienceListLoading: true });
  697. try {
  698. const page = reset ? 1 : this.data.scienceListPage;
  699. const {
  700. data,
  701. total,
  702. page: currentPage,
  703. } = await getShortScienceList(page, this.data.scienceListSize);
  704. const newList = reset ? data : [...this.data.scienceList, ...data];
  705. const hasMore = newList.length < total;
  706. this.setData({
  707. scienceList: newList,
  708. scienceListTotal: total,
  709. scienceListPage: currentPage + 1,
  710. scienceListHasMore: hasMore,
  711. scienceListLoading: false,
  712. });
  713. this.distributeCardsToColumns(newList, reset);
  714. } catch (error) {
  715. this.setData({ scienceListLoading: false });
  716. wx.showToast({
  717. title: "加载失败,请重试",
  718. icon: "none",
  719. });
  720. }
  721. },
  722. // 下拉刷新
  723. async onRefreshScienceList() {
  724. this.setData({ refreshing: true });
  725. await this.loadScienceList(true);
  726. this.setData({ refreshing: false });
  727. },
  728. // 上拉加载更多
  729. onLoadMoreScienceList() {
  730. if (this.data.scienceListHasMore && !this.data.scienceListLoading) {
  731. this.loadScienceList(false);
  732. }
  733. },
  734. // 将卡片分配到两列,实现瀑布流布局
  735. distributeCardsToColumns(list: AnyArray, reset = false) {
  736. // 如果是重置,使用空数组;否则过滤出新增的卡片
  737. const itemsToDistribute = reset
  738. ? list
  739. : list.filter(
  740. (item: any) =>
  741. !this.data.leftColumnList.some(
  742. (existing: any) => existing.id === item.id
  743. ) &&
  744. !this.data.rightColumnList.some(
  745. (existing: any) => existing.id === item.id
  746. )
  747. );
  748. if (itemsToDistribute.length === 0) return;
  749. // 计算当前两列的高度(重置时两列已为空,高度为0)
  750. let leftHeight = reset
  751. ? 0
  752. : this.data.leftColumnList.reduce(
  753. (sum: number, item: any) => sum + this.estimateCardHeight(item),
  754. 0
  755. );
  756. let rightHeight = reset
  757. ? 0
  758. : this.data.rightColumnList.reduce(
  759. (sum: number, item: any) => sum + this.estimateCardHeight(item),
  760. 0
  761. );
  762. const leftColumn = reset ? [] : [...this.data.leftColumnList];
  763. const rightColumn = reset ? [] : [...this.data.rightColumnList];
  764. itemsToDistribute.forEach((item: any) => {
  765. const estimatedHeight = this.estimateCardHeight(item);
  766. if (leftHeight <= rightHeight) {
  767. leftColumn.push(item);
  768. leftHeight += estimatedHeight;
  769. } else {
  770. rightColumn.push(item);
  771. rightHeight += estimatedHeight;
  772. }
  773. });
  774. this.setData({
  775. leftColumnList: leftColumn,
  776. rightColumnList: rightColumn,
  777. });
  778. },
  779. estimateCardHeight(item: any): number {
  780. let height = 0;
  781. if (item.briefImg) {
  782. const imgMinHeight = 120;
  783. const imgMaxHeight = 260;
  784. const imgHeightRange = imgMaxHeight - imgMinHeight;
  785. const idHash = item.id
  786. ? String(item.id)
  787. .split("")
  788. .reduce((acc: number, char: string) => acc + char.charCodeAt(0), 0)
  789. : 0;
  790. const imgHeight = imgMinHeight + (idHash % imgHeightRange);
  791. height += imgHeight;
  792. }
  793. const title = item.title || "";
  794. const titleLength = title.length;
  795. const charsPerLine = 12;
  796. const titleLines = Math.min(Math.ceil(titleLength / charsPerLine), 3);
  797. const titleHeight = titleLines * 24;
  798. const contentPadding = 24;
  799. const metaHeight = 40;
  800. height += titleHeight + contentPadding + metaHeight;
  801. height += 12;
  802. height += 4;
  803. if (!item.briefImg) {
  804. const minContentHeight =
  805. titleHeight + contentPadding + metaHeight + 12 + 4;
  806. if (height < minContentHeight) {
  807. height = minContentHeight;
  808. }
  809. }
  810. return Math.round(height);
  811. },
  812. observerPatient(model: { patientId: string; sex: "0" | "1" }) {
  813. wx.setStorageSync("patientId", model.patientId);
  814. this._getHealthReport();
  815. this._getAbnormalHealthIndex();
  816. const patientIcon = { 0: "gender-male", 1: "gender-female" }[model.sex];
  817. const patientIconColor = { 0: "#0f40f5", 1: "#E560B3" }[model.sex];
  818. this.setData({ patientIcon, patientIconColor });
  819. getPatientDescription(model).then((patientDescription) => {
  820. this.setData({ patientDescription });
  821. });
  822. // 获取未处理随访列表
  823. this.getNotDealList();
  824. // 获取调养计划
  825. this.getCareLists();
  826. },
  827. // 没有健康分析去做健康分析
  828. goHealthAnalyze() {
  829. wx.redirectTo({
  830. url: `/module/chats/pages/index/index?component=guide&isShowGuide=true`,
  831. });
  832. wx.setStorageSync("showGuideActive", 1);
  833. wx.setStorageSync("isAnalysis", 3);
  834. },
  835. // 检查咨询状态
  836. async checkConsultationStatus() {
  837. //获取正在咨询中的咨询id
  838. const res = await Post("/consultManage/getConsultIng");
  839. const isConsulting = !!res.data?.id;
  840. const hasNewMessage = res.data?.patientUnreadCount > 0;
  841. // 是否有新消息
  842. this.setData({ hasNewMessage });
  843. if (isConsulting) {
  844. // 保存咨询id,标记咨询未结束
  845. wx.setStorageSync("consultId", res.data);
  846. wx.setStorageSync("consultEnded", false);
  847. this.setData({
  848. isConsulting: true,
  849. });
  850. } else {
  851. // 没有咨询,清理相关数据
  852. wx.setStorageSync("consultEnded", true);
  853. wx.removeStorageSync("consultId");
  854. this.setData({
  855. isConsulting: false,
  856. hasNewMessage: false,
  857. });
  858. }
  859. },
  860. // 跳转到咨询页面
  861. goToConsultation() {
  862. // 跳转之后 未读的消息变为已读 就没有最新消息了 不显示绿点
  863. this.setData({ hasNewMessage: false });
  864. // 跳转到咨询页面,显示 message-consult 组件
  865. wx.navigateTo({
  866. url: `/module/chats/pages/index/index?component=questionnaire&messageType=3`,
  867. });
  868. wx.setStorageSync("isAnalysis", 5);
  869. },
  870. async isGoPunchcard(e: any) {
  871. const { id, signintime } = e.currentTarget.dataset;
  872. // 已经打卡了
  873. if (signintime) {
  874. // 已打卡 跳转到打卡页面
  875. wx.navigateTo({
  876. url: `/module/article/pages/punch-card/punch-card?id=${id}`,
  877. });
  878. } else {
  879. // 打卡
  880. const cardId = e.currentTarget.dataset.id;
  881. await addPatientOnlineRecordClockIn(cardId);
  882. wx.showToast({
  883. title: "打卡成功",
  884. icon: "success",
  885. duration: 1500,
  886. });
  887. await this.refreshCareListsWithState();
  888. }
  889. },
  890. async refreshCareListsWithState() {
  891. // 保存当前的展开状态
  892. const currentExpandedStates = this.data.displayList.map((item: any) => ({
  893. id: item.id,
  894. expanded: item.expanded,
  895. carouselMediaList: item.carouselMediaList,
  896. }));
  897. // 获取新数据
  898. const res = await getCareList();
  899. if (res && res.length > 0) {
  900. res.forEach((item: any) => {
  901. const oldItem = currentExpandedStates.find(
  902. (state) => state.id === item.id
  903. );
  904. if (oldItem && oldItem.carouselMediaList) {
  905. item.carouselMediaList = oldItem.carouselMediaList;
  906. } else {
  907. item.carouselMediaList = [];
  908. // 添加photo
  909. if (item.photo) {
  910. item.carouselMediaList.push({
  911. type: "image",
  912. src: item.photo,
  913. });
  914. }
  915. // 添加itemImgFirst
  916. if (item?.itemImgFirst) {
  917. item.carouselMediaList.push({
  918. type: "image",
  919. src: item.itemImgFirst,
  920. });
  921. }
  922. // 添加itemVideoFirst
  923. if (item?.itemVideoFirst) {
  924. item.carouselMediaList.push({
  925. type: "video",
  926. src: item.itemVideoFirst,
  927. });
  928. }
  929. }
  930. });
  931. // 更新 careList
  932. this.setData({
  933. careList: res,
  934. });
  935. // 更新 displayList 并恢复展开状态
  936. const { allExpanded } = this.data;
  937. let newDisplayList: any[] =
  938. allExpanded || res.length <= 4 ? res : res.slice(0, 4);
  939. // 恢复展开状态
  940. newDisplayList = newDisplayList.map((item: any) => {
  941. const savedState = currentExpandedStates.find(
  942. (state) => state.id === item.id
  943. );
  944. if (savedState) {
  945. return {
  946. ...item,
  947. expanded: savedState.expanded,
  948. carouselMediaList:
  949. savedState.carouselMediaList || item.carouselMediaList,
  950. };
  951. }
  952. return item;
  953. });
  954. this.setData({
  955. displayList: newDisplayList,
  956. });
  957. }
  958. },
  959. /**
  960. * 轮播组件进入/退出全屏时,隐藏/显示底部 tabbar,防止遮挡视频进度条
  961. */
  962. onCarouselFullscreenChange(e: { detail?: { fullScreen?: boolean } }) {
  963. const fullScreen = !!e.detail?.fullScreen;
  964. this.setData({
  965. tabbarHidden: fullScreen,
  966. });
  967. },
  968. });