home.ts 31 KB

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