home.ts 30 KB

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