cc12458 1 anno fa
parent
commit
cf89108f9d
100 ha cambiato i file con 1978 aggiunte e 418 eliminazioni
  1. 22 2
      miniprogram/app.json
  2. 4 0
      miniprogram/app.scss
  3. BIN
      miniprogram/assets/bg/body-model-frame.bg-1.png
  4. BIN
      miniprogram/assets/bg/body-model.bg.png
  5. BIN
      miniprogram/assets/icon/gender-0.icon.png
  6. BIN
      miniprogram/assets/icon/gender-1.icon.png
  7. BIN
      miniprogram/assets/icon/location.icon.png
  8. BIN
      miniprogram/assets/icon/weather.icon.png
  9. 12 0
      miniprogram/components/form-picker/form-picker.scss
  10. 55 5
      miniprogram/components/form-picker/form-picker.ts
  11. 24 11
      miniprogram/components/form-picker/form-picker.wxml
  12. 17 8
      miniprogram/components/form-ruler/form-ruler.ts
  13. 3 2
      miniprogram/core/wxs/dictionary.wxs
  14. 32 0
      miniprogram/lib/use/use-location.ts
  15. 9 0
      miniprogram/lib/wx/location.ts
  16. 6 1
      miniprogram/lib/wx/network.ts
  17. 5 0
      miniprogram/module/article/components/diet-card/diet-card.json
  18. 51 0
      miniprogram/module/article/components/diet-card/diet-card.scss
  19. 20 0
      miniprogram/module/article/components/diet-card/diet-card.ts
  20. 12 0
      miniprogram/module/article/components/diet-card/diet-card.wxml
  21. 5 0
      miniprogram/module/article/components/science-card/science-card.json
  22. 51 0
      miniprogram/module/article/components/science-card/science-card.scss
  23. 20 0
      miniprogram/module/article/components/science-card/science-card.ts
  24. 11 0
      miniprogram/module/article/components/science-card/science-card.wxml
  25. 7 0
      miniprogram/module/article/pages/diet-info/diet-info.json
  26. 29 0
      miniprogram/module/article/pages/diet-info/diet-info.scss
  27. 37 0
      miniprogram/module/article/pages/diet-info/diet-info.ts
  28. 31 0
      miniprogram/module/article/pages/diet-info/diet-info.wxml
  29. 7 0
      miniprogram/module/article/pages/diet-list/diet-list.json
  30. 4 0
      miniprogram/module/article/pages/diet-list/diet-list.scss
  31. 76 0
      miniprogram/module/article/pages/diet-list/diet-list.ts
  32. 18 0
      miniprogram/module/article/pages/diet-list/diet-list.wxml
  33. 5 0
      miniprogram/module/article/pages/science-info/science-info.json
  34. 15 0
      miniprogram/module/article/pages/science-info/science-info.scss
  35. 37 0
      miniprogram/module/article/pages/science-info/science-info.ts
  36. 18 0
      miniprogram/module/article/pages/science-info/science-info.wxml
  37. 7 0
      miniprogram/module/article/pages/science-list/science-list.json
  38. 4 0
      miniprogram/module/article/pages/science-list/science-list.scss
  39. 81 0
      miniprogram/module/article/pages/science-list/science-list.ts
  40. 18 0
      miniprogram/module/article/pages/science-list/science-list.wxml
  41. 42 0
      miniprogram/module/article/request.ts
  42. 42 0
      miniprogram/module/article/searc-list.scss
  43. 0 0
      miniprogram/module/charts/ec-canvas/echarts.min.js
  44. 156 0
      miniprogram/module/charts/record-index/chalk.theme.ts
  45. 47 100
      miniprogram/module/charts/record-index/health-index.ts
  46. 3 0
      miniprogram/module/charts/record-index/record-index.scss
  47. 4 1
      miniprogram/module/charts/record-index/record-index.ts
  48. BIN
      miniprogram/module/chats/assets/face-1.png
  49. 3 0
      miniprogram/module/chats/components/guide/guide.ts
  50. 2 1
      miniprogram/module/chats/components/guide/guide.wxml
  51. 4 0
      miniprogram/module/chats/components/message-select/message-select.scss
  52. 13 20
      miniprogram/module/chats/components/message-select/message-select.ts
  53. 6 12
      miniprogram/module/chats/components/message-select/message-select.wxml
  54. 49 46
      miniprogram/module/chats/components/questionnaire/questionnaire.ts
  55. 1 1
      miniprogram/module/chats/components/questionnaire/questionnaire.wxml
  56. 14 17
      miniprogram/module/chats/pages/analysis/analysis.ts
  57. 21 0
      miniprogram/module/chats/pages/index/index.scss
  58. 5 0
      miniprogram/module/chats/pages/index/index.ts
  59. 4 0
      miniprogram/module/chats/pages/index/index.wxml
  60. 4 0
      miniprogram/module/health/components/card-report/card-report.json
  61. 0 0
      miniprogram/module/health/components/card-report/card-report.scss
  62. 11 0
      miniprogram/module/health/components/card-report/card-report.ts
  63. 1 0
      miniprogram/module/health/components/card-report/card-report.wxml
  64. 4 0
      miniprogram/module/health/components/field-ruler/field-ruler.scss
  65. 1 1
      miniprogram/module/health/components/field-ruler/field-ruler.ts
  66. 9 3
      miniprogram/module/health/components/field-ruler/field-ruler.wxml
  67. 6 0
      miniprogram/module/health/components/horizontal-scrollable/horizontal-scrollable.json
  68. 44 0
      miniprogram/module/health/components/horizontal-scrollable/horizontal-scrollable.scss
  69. 53 0
      miniprogram/module/health/components/horizontal-scrollable/horizontal-scrollable.ts
  70. 14 0
      miniprogram/module/health/components/horizontal-scrollable/horizontal-scrollable.wxml
  71. 2 0
      miniprogram/module/health/components/report-health-index/report-health-index.scss
  72. 8 2
      miniprogram/module/health/components/report-health-patient/report-health-patient.scss
  73. 7 5
      miniprogram/module/health/components/report-health-patient/report-health-patient.wxml
  74. 6 5
      miniprogram/module/health/components/report-health-scheme/report-health-scheme.wxml
  75. 2 1
      miniprogram/module/health/components/report-health-status/report-health-status.json
  76. 1 0
      miniprogram/module/health/components/report-health-status/report-health-status.scss
  77. 23 14
      miniprogram/module/health/components/report-health-status/report-health-status.wxml
  78. 2 1
      miniprogram/module/health/pages/home/home.json
  79. 12 22
      miniprogram/module/health/pages/home/home.scss
  80. 27 4
      miniprogram/module/health/pages/home/home.ts
  81. 6 3
      miniprogram/module/health/pages/home/home.wxml
  82. 47 43
      miniprogram/module/health/pages/report/report.scss
  83. 24 2
      miniprogram/module/health/pages/report/report.ts
  84. 114 19
      miniprogram/module/health/pages/report/report.wxml
  85. 2 1
      miniprogram/module/health/pages/scheme/scheme.json
  86. 38 0
      miniprogram/module/health/pages/scheme/scheme.scss
  87. 2 0
      miniprogram/module/health/pages/scheme/scheme.ts
  88. 13 5
      miniprogram/module/health/pages/scheme/scheme.wxml
  89. 5 0
      miniprogram/module/health/pages/status-record/status-record.json
  90. 56 0
      miniprogram/module/health/pages/status-record/status-record.scss
  91. 87 0
      miniprogram/module/health/pages/status-record/status-record.ts
  92. 27 0
      miniprogram/module/health/pages/status-record/status-record.wxml
  93. 2 5
      miniprogram/module/health/pages/status/status.ts
  94. 1 1
      miniprogram/module/health/pages/status/status.wxml
  95. 8 0
      miniprogram/module/health/pages/tongue-analysis/tongue-analysis.json
  96. 16 0
      miniprogram/module/health/pages/tongue-analysis/tongue-analysis.scss
  97. 17 0
      miniprogram/module/health/pages/tongue-analysis/tongue-analysis.ts
  98. 24 0
      miniprogram/module/health/pages/tongue-analysis/tongue-analysis.wxml
  99. 70 48
      miniprogram/module/health/request.ts
  100. 13 6
      miniprogram/module/health/tools/health-index.ts

+ 22 - 2
miniprogram/app.json

@@ -18,7 +18,19 @@
         "pages/home/home",
         "pages/report/report",
         "pages/status/status",
-        "pages/scheme/scheme"
+        "pages/scheme/scheme",
+        "pages/status-record/status-record",
+        "pages/tongue-analysis/tongue-analysis"
+      ]
+    },
+    {
+      "name": "article",
+      "root": "module/article",
+      "pages": [
+        "pages/diet-list/diet-list",
+        "pages/diet-info/diet-info",
+        "pages/science-list/science-list",
+        "pages/science-info/science-info"
       ]
     },
     {
@@ -39,7 +51,7 @@
     }
   ],
   "preloadRule": {
-    "module/chats/pages/index/index": {
+    "pages/home/home": {
       "packages": [
         "chats"
       ]
@@ -66,5 +78,13 @@
     "t-navbar": "tdesign-miniprogram/navbar/navbar",
     "t-message": "tdesign-miniprogram/message/message",
     "popup-privacy": "./components/popup-privacy/popup-privacy"
+  },
+  "requiredPrivateInfos": [
+    "getFuzzyLocation"
+  ],
+  "permission": {
+    "scope.userFuzzyLocation": {
+      "desc": "你的位置信息将用于小程序推荐"
+    }
   }
 }

+ 4 - 0
miniprogram/app.scss

@@ -1 +1,5 @@
 /**app.wxss**/
+
+page {
+  --td-cell-title-color: #111;
+}

BIN
miniprogram/assets/bg/body-model-frame.bg-1.png


BIN
miniprogram/assets/bg/body-model.bg.png


BIN
miniprogram/assets/icon/gender-0.icon.png


BIN
miniprogram/assets/icon/gender-1.icon.png


BIN
miniprogram/assets/icon/location.icon.png


BIN
miniprogram/assets/icon/weather.icon.png


+ 12 - 0
miniprogram/components/form-picker/form-picker.scss

@@ -78,4 +78,16 @@
     top: 1.5px;
     z-index: 1;
   }
+}
+
+.input-wrapper {
+  box-sizing: border-box;
+
+  input {
+    padding: 0 8px;
+    height: 100%;
+    text-align: left;
+    border: 1px solid #2A2A2A;
+    border-radius: 4px;
+  }
 }

+ 55 - 5
miniprogram/components/form-picker/form-picker.ts

@@ -72,6 +72,11 @@ Component({
     containerHeight: 350,
     gap: 8,
     selected: [] as any[],
+    replenish: {} as AnyObject,
+    showReplenish: false,
+    replenishValue: '',
+
+    offset: 0,
   },
   observers: {
     'options,optionsColumns,itemHeight'(options, columns, height) {
@@ -90,14 +95,42 @@ Component({
       const index = event.currentTarget.dataset.index;
       const option = this.data.options[index];
       if (option.disabled) return;
-      if (this.data.multiple) {
-        this.setData({ selected: filtration(this.data.selected, this.data.options, option) });
+
+      const handle = () => {
+        if (this.data.multiple) {
+          this.setData({ selected: filtration(this.data.selected, this.data.options, option) });
+        } else {
+          this.setData({ selected: this.data.selected.includes(option.value) ? [] : [option.value] });
+        }
+      }
+
+      if (option.label === '其他') {
+        if (this.data.selected.includes(option.value)) {
+          handle();
+          this.setData({ [`replenish.${option.value}`]: '' })
+        } else {
+          this.setData({ showReplenish: true, replenishValue: '' });
+          this.onSubConfirm = () => {
+            if (this.data.replenishValue) {
+              handle();
+              this.setData({ [`replenish.${option.value}`]: this.data.replenishValue })
+            }
+            this.onSubCancel();
+          }
+        }
       } else {
-        this.setData({ selected: this.data.selected.includes(option.value) ? [] : [option.value] });
+        handle()
       }
     },
     onConfirm() {
-      const get = (option: Option) => option ? ({ label: option.label, value: option.value }) : null;
+      const get = (option: Option) => {
+        if (!option) return null;
+        const replenish = this.data.replenish[option.value];
+        return {
+          label: replenish ? replenish : option.label,
+          value: replenish ? `${option.value}:${replenish}` : option.value
+        }
+      };
       this.setData({ visible: false })
       this.triggerEvent('confirm', {
         selected: this.data.selected,
@@ -112,11 +145,28 @@ Component({
       this.triggerEvent('cancel');
       this.triggerEvent('close', { trigger: 'cancel-btn' });
     },
+    onSubConfirm() { },
+    onSubCancel() {
+      this.setData({ showReplenish: false, offset: 0 });
+    },
     onVisibleChange(event: any) {
       if (!event.detail.visible) {
         this.triggerEvent('close', { trigger: event.detail.trigger })
-        setTimeout(() => { this.setData({ selected: reset(this.data.value, this.data.options) }); }, 100)
+        setTimeout(() => {
+          this.setData({
+            selected: reset(this.data.value, this.data.options),
+            offset: 0
+          });
+        }, 100)
       };
+    },
+    onBlur() {
+      this.setData({ offset: 0 })
+    },
+
+    onKeyboardheightchange(event: any) {
+      const _height = event?.detail?.height;
+      if (_height !== this.data.offset) this.setData({ offset: _height });
     }
   }
 })

+ 24 - 11
miniprogram/components/form-picker/form-picker.wxml

@@ -3,19 +3,32 @@
 <view class="form-picker">
   <t-popup t-class="form-picker__inner" visible="{{ visible }}" bind:visible-change="onCancel" placement="bottom" close-on-overlay-click="{{closeOnOverlayClick}}" bind:visible-change="onVisibleChange">
     <view class="form-picker__header">
-      <view class="btn btn--cancel" aria-role="button" bind:tap="onCancel">取消</view>
-      <view class="title">{{title}}</view>
-      <view class="btn btn--confirm" aria-role="button" bind:tap="onConfirm">确定</view>
+      <block wx:if="{{showReplenish}}">
+        <view class="btn btn--cancel" aria-role="button" bind:tap="onSubCancel">返回</view>
+        <view class="title">{{title}}: 其他</view>
+        <view class="btn btn--confirm" aria-role="button" bind:tap="onSubConfirm">完成</view>
+      </block>
+      <block wx:else>
+        <view class="btn btn--cancel" aria-role="button" bind:tap="onCancel">取消</view>
+        <view class="title">{{title}}</view>
+        <view class="btn btn--confirm" aria-role="button" bind:tap="onConfirm">确定</view>
+      </block>
     </view>
     <view class="form-picker__content">
-      <scroll-view type="custom" scroll-y style="height: {{containerHeight}}px;">
-        <grid-builder list="{{options}}" cross-axis-count="{{optionsColumns}}" cross-axis-gap="{{gap}}" main-axis-gap="{{gap}}">
-          <view slot:item slot:index class="card {{_.getClassName(selected, item)}}" style="height: {{itemHeight}}px;" data-index="{{index}}" bind:tap="handle">
-            <t-icon wx:if="{{_.contain(selected, item.value)}}" name="check" t-class="card__icon" ariaHidden="{{true}}" />
-            <text overflow="ellipsis" max-lines="3">{{item.label}}</text>
-          </view>
-        </grid-builder>
-      </scroll-view>
+      <view class="input-wrapper" wx:if="{{showReplenish}}" style="height: {{itemHeight}}px; padding: {{(itemHeight - 40) / 2}}px;">
+        <input placeholder="请输入" focus="{{true}}" model:value="{{replenishValue}}" adjust-position="{{false}}" bind:keyboardheightchange="onKeyboardheightchange" bind:blur="onBlur" bind:confirm="onSubConfirm" />
+      </view>
+      <block wx:else>
+        <scroll-view type="custom" scroll-y style="height: {{containerHeight}}px;">
+          <grid-builder list="{{options}}" cross-axis-count="{{optionsColumns}}" cross-axis-gap="{{gap}}" main-axis-gap="{{gap}}">
+            <view slot:item slot:index class="card {{_.getClassName(selected, item)}}" style="height: {{itemHeight}}px;" data-index="{{index}}" bind:tap="handle">
+              <t-icon wx:if="{{_.contain(selected, item.value)}}" name="check" t-class="card__icon" ariaHidden="{{true}}" />
+              <text overflow="ellipsis" max-lines="3">{{replenish[item.value] || item.label}}</text>
+            </view>
+          </grid-builder>
+        </scroll-view>
+      </block>
     </view>
+    <view style="height: {{offset}}px;"></view>
   </t-popup>
 </view>

+ 17 - 8
miniprogram/components/form-ruler/form-ruler.ts

@@ -29,22 +29,31 @@ Component({
     multipleSlots: true,
   },
   properties: {
-    value: { type: Number, value: Number.NaN },
+    value: { type: String, value: '' },
     min: { type: Number, value: 0 },
     max: { type: Number, value: 1 },
     precision: { type: Number, value: 0.1 },
-    defaultValue: { type: Number, value: Number.NaN }
+    defaultValue: { type: Number, value: Number.NaN },
+    line: { type: Array, value: [] },
   },
   observers: {
+    'precision'(precision: number) {
+      const scale = Math.pow(10, Math.ceil(Math.log10(1 / precision)));
+      this.setData({ scale });
+    },
     'min, max, precision'(...args: [number, number, number]) {
       this._updateAxis(args);
     },
-    'defaultValue, min, max, precision'(value, min, max, precision) {
+    'defaultValue, min, max, precision, line'(value, min, max, precision, line) {
       const offset = precision >= 1 ? precision : 0
-      if (!value || Number.isNaN(value)) {
+      if (!value || Number.isNaN(+value)) {
+        if (Array.isArray(line)) {
+          min = line[0]?.value ?? min;
+          max = line[1]?.value ?? max;
+        }
         value = Math.floor((max - min - offset) / 2 + min)
       }
-      this._scrollValue(value + offset);
+      this._scrollValue(value);
     }
   },
   lifetimes: {
@@ -54,7 +63,7 @@ Component({
   },
   data: {
     prefix,
-    initialValue: '',
+    initialValue: '', scale: 0,
     axis: [] as Axis,
     rect: { width: 0, height: 0, gap: 5 }
   },
@@ -80,9 +89,9 @@ Component({
     },
     _updateValue(index?: number) {
       const value = this.data.axis[index ?? 0]?.value
-      this.setData({ value })
+      this.setData({ value: value.toFixed(Math.log10(this.data.scale)) })
     },
-    _scrollValue(value: number) {
+    _scrollValue(value: number | string) {
       const index = this.data.axis.findIndex(item => item.value === value);
       clearTimeout((this as any).lock);
       (this as any).lock = setTimeout(() => { this.setData({ initialValue: `${prefix}${index}` }); }, 300);

+ 3 - 2
miniprogram/core/wxs/dictionary.wxs

@@ -11,9 +11,10 @@ function getDictionaryLabel() {
   if (arguments.length !== 3) return '';
   var options = getDictionaryOptions(arguments[0], arguments[1]);
   var keys = options.map(function (item) { return item.value; });
-  var index = keys.indexOf(arguments[2]);
+  var v_l = arguments[2] ? arguments[2].split(':') : [];
+  var index = keys.indexOf(v_l[0]);
 
-  return index !== -1 ? options[index].label : ''
+  return index !== -1 ? v_l[1] || options[index].label : ''
 };
 
 module.exports = {

+ 32 - 0
miniprogram/lib/use/use-location.ts

@@ -0,0 +1,32 @@
+import type { LocationType } from "../wx/location";
+import { getFuzzyLocation } from "../wx/location";
+import request from "../request/method";
+
+
+export async function useLocation(type?: LocationType) {
+  const scope = 'scope.userFuzzyLocation';
+  const { authSetting } = await wx.getSetting();
+  if (!authSetting?.[scope]) {
+    await wx.authorize({ scope })
+  }
+
+  const location = await getFuzzyLocation(type);
+
+  const {prov, city} = await request({
+    url: `/surplus/getAddrByLatLong`,
+    method: 'GET',
+    params: {
+      longitude: location.longitude,
+      latitude: location.latitude,
+      type,
+    },
+    transform({ data }) { return data; }
+  });
+  
+
+  return {
+    longitude: location.longitude,
+    latitude: location.latitude,
+    description: `${prov}${city}`.replace(/省|市|自治区|特别行政区|壮族|回族|维吾尔/g,''),
+  }
+}

+ 9 - 0
miniprogram/lib/wx/location.ts

@@ -0,0 +1,9 @@
+import { withResolvers } from "../promise";
+
+export type LocationType = 'wgs84' | 'gcj02';
+
+export function getFuzzyLocation(type?: LocationType) {
+  const { promise, resolve, reject } = withResolvers<WechatMiniprogram.GetFuzzyLocationSuccessCallbackResult>();
+  wx.getFuzzyLocation({ type: type ?? 'gcj02', success: resolve, fail: reject });
+  return promise;
+}

+ 6 - 1
miniprogram/lib/wx/network.ts

@@ -23,7 +23,12 @@ export function request<T extends Data>(option: Option<T>) {
 type UploadOption = Omit<WechatMiniprogram.UploadFileOption, 'success' | 'fail' | 'complete'>;
 export function upload<T extends string>(option: UploadOption, fn: (data: string) => T): Request<T> {
   const { promise, resolve, reject } = withResolvers<T, Request<T>>();
-  const task = wx.uploadFile({ ...option, success(res) { resolve(fn(res.data)); }, fail: reject, });
+  const task = wx.uploadFile({
+    ...option, success(res) {
+      if (res.statusCode !== 200) reject({ errMsg: `上传失败(${res.statusCode})` })
+      else resolve(fn(res.data));
+    }, fail: reject,
+  });
   promise.abort = () => {
     task.abort();
     task.offHeadersReceived();

+ 5 - 0
miniprogram/module/article/components/diet-card/diet-card.json

@@ -0,0 +1,5 @@
+{
+  "renderer": "skyline",
+  "component": true,
+  "usingComponents": {}
+}

+ 51 - 0
miniprogram/module/article/components/diet-card/diet-card.scss

@@ -0,0 +1,51 @@
+/* module/article/components/diet-card/diet-card.wxss */
+.list-card-item {
+  position: relative;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  min-height: 125px;
+  padding: 12px;
+  box-sizing: border-box;
+
+  .cover {
+    flex: none;
+    width: 100px;
+    height: 100px;
+    margin-right: 16px;
+    border-radius: 12px;
+  }
+
+  .content {
+    flex: auto;
+    display: flex;
+    flex-direction: column;
+    height: 100px;
+    justify-content: space-evenly;
+  }
+
+  .title {
+    font-size: 16px;
+    color: #fff;
+  }
+
+  .description {
+    font-size: 14px;
+    color: #A2AFB2;
+
+    &-2 {
+      font-size: 12px;
+      color: #34A76B;
+    }
+  }
+
+  .border-gradient {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 1px;
+    transform: translateY(-1px);
+    background: linear-gradient(90deg, rgba(15, 34, 38, 0) 0%, var(--primary-color, #38FF6E) 50%, rgba(15, 34, 38, 0) 100%);
+  }
+}

+ 20 - 0
miniprogram/module/article/components/diet-card/diet-card.ts

@@ -0,0 +1,20 @@
+// module/article/components/diet-card/diet-card.ts
+Component({
+  properties: {
+    item: { type: Object, value: {} },
+    index: { type: Number, value: 0 },
+    replace: { type: Boolean, value: false },
+  },
+  data: {},
+  methods: {
+    toInfoPage(event: WechatMiniprogram.TouchEvent) {
+      const id = event.currentTarget.id;
+      if (this.data.replace) {
+        wx.redirectTo({ url: `/module/article/pages/diet-info/diet-info?id=${id}` });
+      } else {
+        wx.navigateTo({ url: `/module/article/pages/diet-info/diet-info?id=${id}` })
+          .then(res => { res.eventChannel.emit('load', this.data.item) })
+      }
+    }
+  }
+})

+ 12 - 0
miniprogram/module/article/components/diet-card/diet-card.wxml

@@ -0,0 +1,12 @@
+<!--module/article/components/diet-card/diet-card.wxml-->
+<view class="list-card-item" id="{{item.id}}" bind:tap="toInfoPage">
+  <view class="border-gradient" wx:if="{{index}}">
+    <!--边框-->
+  </view>
+  <image class="cover" src="{{item.photo}}" mode="aspectFill" fade-in />
+  <view class="content">
+    <text class="title" max-lines="1" overflow="ellipsis">{{item.name}}</text>
+    <text class="description" max-lines="2" overflow="ellipsis" wx:if="{{item.book}}">{{item.book}}</text>
+    <text class="description-2" max-lines="1" overflow="ellipsis" wx:if="{{item.effect}}">{{item.effect}}</text>
+  </view>
+</view>

+ 5 - 0
miniprogram/module/article/components/science-card/science-card.json

@@ -0,0 +1,5 @@
+{
+  "renderer": "skyline",
+  "component": true,
+  "usingComponents": {}
+}

+ 51 - 0
miniprogram/module/article/components/science-card/science-card.scss

@@ -0,0 +1,51 @@
+/* module/article/components/science-card/science-card.wxss */
+.list-card-item {
+  position: relative;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  min-height: 125px;
+  padding: 12px;
+  box-sizing: border-box;
+
+  .cover {
+    flex: none;
+    width: 100px;
+    height: 100px;
+    margin-right: 16px;
+    border-radius: 12px;
+  }
+
+  .content {
+    flex: auto;
+    display: flex;
+    flex-direction: column;
+    height: 100px;
+    justify-content: space-evenly;
+  }
+
+  .title {
+    font-size: 16px;
+    color: #fff;
+  }
+
+  .description {
+    font-size: 14px;
+    color: #A2AFB2;
+
+    &-2 {
+      font-size: 12px;
+      color: #34A76B;
+    }
+  }
+
+  .border-gradient {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 1px;
+    transform: translateY(-1px);
+    background: linear-gradient(90deg, rgba(15, 34, 38, 0) 0%, var(--primary-color, #38FF6E) 50%, rgba(15, 34, 38, 0) 100%);
+  }
+}

+ 20 - 0
miniprogram/module/article/components/science-card/science-card.ts

@@ -0,0 +1,20 @@
+// module/article/components/science-card/science-card.ts
+Component({
+  properties: {
+    item: { type: Object, value: {} },
+    index: { type: Number, value: 0 },
+    replace: { type: Boolean, value: false },
+  },
+  data: {},
+  methods: {
+    toInfoPage(event: WechatMiniprogram.TouchEvent) {
+      const id = event.currentTarget.id;
+      if (this.data.replace) {
+        wx.redirectTo({ url: `/module/article/pages/science-info/science-info?id=${id}` });
+      } else {
+        wx.navigateTo({ url: `/module/article/pages/science-info/science-info?id=${id}` })
+          .then(res => { res.eventChannel.emit('load', this.data.item) })
+      }
+    }
+  }
+})

+ 11 - 0
miniprogram/module/article/components/science-card/science-card.wxml

@@ -0,0 +1,11 @@
+<!--module/article/components/science-card/science-card.wxml-->
+<view class="list-card-item" id="{{item.id}}" bind:tap="toInfoPage">
+  <view class="border-gradient" wx:if="{{index}}">
+    <!--边框-->
+  </view>
+  <image class="cover" src="{{item.briefImg}}" mode="aspectFill" fade-in />
+  <view class="content">
+    <text class="title" max-lines="3" overflow="ellipsis">{{item.title}}</text>
+    <text class="description-2" max-lines="1" overflow="ellipsis" wx:if="{{item.readVolume}}">{{item.readVolume}}人阅读</text>
+  </view>
+</view>

+ 7 - 0
miniprogram/module/article/pages/diet-info/diet-info.json

@@ -0,0 +1,7 @@
+{
+  "renderer": "skyline",
+  "component": true,
+  "usingComponents": {
+    "diet-card": "../../components/diet-card/diet-card"
+  }
+}

+ 29 - 0
miniprogram/module/article/pages/diet-info/diet-info.scss

@@ -0,0 +1,29 @@
+@import "../../../../themes/page.scss";
+@import "../../searc-list.scss";
+/* module/diet/pages/diet-info/diet-info.wxss */
+
+
+.list-wrapper {
+  .list-title {
+    padding: 12px 12px 0;
+  }
+
+  .box-wrapper {
+    margin: 6px 12px;
+    padding: 16px;
+    font-size: 14px;
+    border-radius: 4px;
+    box-shadow: inset 0px 0px 12px * 3 0px rgba(52, 167, 107, 0.38);
+  }
+
+  .item-wrapper {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin: 0 12px;
+    min-height: 40px;
+    font-size: 14px;
+    color: #E3E3E3;
+    border-bottom: 1px dotted #34A76B;
+  }
+}

+ 37 - 0
miniprogram/module/article/pages/diet-info/diet-info.ts

@@ -0,0 +1,37 @@
+import PageContainerBehavior from "../../../../core/behavior/page-container.behavior";
+import tickleBehavior, { getTickleContext } from "../../../../core/behavior/tickle.behavior";
+import { getDietMethod } from "../../request";
+// module/diet/pages/diet-info/diet-info.ts
+Component({
+  behaviors: [
+    PageContainerBehavior,
+    tickleBehavior,
+  ],
+  lifetimes: {
+    attached() {
+      const channel = this.getOpenerEventChannel();
+      channel.on?.('load', (data: AnyObject) => {
+        this.setData({ title: data?.name ?? '' });
+      })
+      this.load();
+    }
+  },
+  properties: {
+    id: { type: String, value: '' },
+  },
+  data: {
+    title: ''
+  },
+  methods: {
+    async load() {
+      wx.showLoading({ title: '加载中' });
+      try {
+        const dataset = await getDietMethod(this.data.id);
+        this.setData({ dataset, title: dataset.name });
+      } catch (error) {
+        getTickleContext.call(this).showWarnMessage(error.errMsg);
+      }
+      wx.hideLoading();
+    },
+  }
+})

+ 31 - 0
miniprogram/module/article/pages/diet-info/diet-info.wxml

@@ -0,0 +1,31 @@
+<!--module/diet/pages/diet-info/diet-info.wxml-->
+<t-navbar title="{{title}}" left-arrow />
+<scroll-view class="page-scroll__container" type="list" scroll-y style="{{containerStyle}}">
+  <image wx:if="{{dataset.photo}}" src="{{dataset.photo}}" mode="widthFix" style="width: 100%;"></image>
+  <!-- <view class="list-wrapper">
+    <view class="list-title">食谱介绍</view>
+    <view class="box-wrapper">{{dataset.book}}</view>
+  </view> -->
+  <view class="list-wrapper">
+    <view class="list-title">中医功效</view>
+    <view class="box-wrapper">{{dataset.effectText}}</view>
+  </view>
+  <view class="list-wrapper" wx:if="{{dataset.itemList.length}}">
+    <view class="list-title">食材</view>
+    <view class="item-wrapper" wx:for="{{dataset.itemList}}" wx:key="ingredientName">
+      <view>{{item.ingredientName}}</view>
+      <view>{{item.dose || ''}}{{item.unit || ''}}</view>
+    </view>
+  </view>
+  <view class="list-wrapper">
+    <view class="list-title">制作方法</view>
+    <view class="box-wrapper">{{dataset.method}}</view>
+  </view>
+  <view class="list-wrapper" wx:if="{{dataset.recommendedList.length}}">
+    <view class="list-title">相关推荐</view>
+    <diet-card wx:for="{{dataset.recommendedList}}" wx:key="id" item="{{item}}" index="{{index}}" replace="{{true}}"></diet-card>
+  </view>
+</scroll-view>
+
+<view style="height: {{container.safeBottomOffset}}px;flex: none;"></view>
+<t-message id="{{$messageId}}" />

+ 7 - 0
miniprogram/module/article/pages/diet-list/diet-list.json

@@ -0,0 +1,7 @@
+{
+  "renderer": "skyline",
+  "component": true,
+  "usingComponents": {
+    "diet-card": "../../components/diet-card/diet-card"
+  }
+}

+ 4 - 0
miniprogram/module/article/pages/diet-list/diet-list.scss

@@ -0,0 +1,4 @@
+@import '../../../../themes/page.scss';
+@import '../../searc-list.scss';
+
+/* module/diet/pages/diet-list/diet-list.wxss */

+ 76 - 0
miniprogram/module/article/pages/diet-list/diet-list.ts

@@ -0,0 +1,76 @@
+import PageContainerBehavior from "../../../../core/behavior/page-container.behavior";
+import tickleBehavior, { getTickleContext } from "../../../../core/behavior/tickle.behavior";
+import { getDietListMethod } from "../../request";
+// module/diet/pages/diet-list/diet-list.ts
+Component({
+  behaviors: [
+    PageContainerBehavior,
+    tickleBehavior,
+  ],
+  lifetimes: {
+    attached() {
+      wx.showLoading({ title: '加载中' });
+      this.load();
+    }
+  },
+  properties: {
+    classify: { type: String, value: '' },
+  },
+  data: {
+    keyword: '',
+    searchInputProps: {
+      placeholderStyle: 'color: #929292;',
+      cursorColor: '#34A76B',
+      iconColor: '#34A76B',
+      focus: false,
+      placeholder: '输入搜索名称',
+    },
+    title: '',
+    dataset: [] as AnyObject[],
+    page: 1, size: 20, total: 0, loading: false, isLastPage: false,
+  },
+  observers: {
+    'classify'(value) {
+      const ref = { tonic: '药膳查询', tea: '中医茶饮' } as any;
+      this.setData({ title: ref[value] || '' })
+    },
+    'page,size,total'(page, size, total) {
+      this.setData({ isLastPage: page * size >= total })
+    }
+  },
+  methods: {
+    async load() {
+      try {
+        const { data, total } = await getDietListMethod(1, this.data.size, {
+          keyword: this.data.keyword,
+          classify: <any>this.data.classify,
+        });
+        this.setData({ dataset: data, total });
+      } catch (error) {
+        getTickleContext.call(this).showWarnMessage(error.errMsg);
+      }
+      wx.hideLoading();
+    },
+    async loadMore() {
+      if (this.data.isLastPage || this.data.loading) return;
+      this.setData({ loading: true });
+      try {
+        const _page = this.data.page + 1;
+        const { data, total } = await getDietListMethod(_page, this.data.size, {
+          keyword: this.data.keyword,
+          classify: <any>this.data.classify,
+        });
+        this.setData({ dataset: [...this.data.dataset, ...data], total, page: _page });
+      } catch (error) {
+        getTickleContext.call(this).showWarnMessage(error.errMsg);
+      }
+      this.setData({ loading: false });
+    },
+    onConfirmSearchInput(event: WechatMiniprogram.InputConfirm) {
+      const _keyword = this.data.keyword;
+      this.setData({ keyword: event.detail.value });
+      wx.showLoading({ title: '搜索中' });
+      this.load().catch(() => { this.setData({ keyword: _keyword }) });
+    }
+  }
+})

+ 18 - 0
miniprogram/module/article/pages/diet-list/diet-list.wxml

@@ -0,0 +1,18 @@
+<!--module/diet/pages/diet-list/diet-list.wxml-->
+<t-navbar title="{{title}}" left-arrow />
+<view class="search-wrapper" bindtap="onTapSearchInput">
+  <icon class="search-icon" data-button="focus" type="search" size="14" color="{{searchInputProps.iconColor}}"></icon>
+  <input class="search-input" value="{{keyword}}" focus="{{searchInputProps.focus}}" placeholder="{{searchInputProps.placeholder}}" placeholder-style="{{searchInputProps.placeholderStyle}}" cursor-color="{{searchInputProps.cursorColor}}" confirm-type="search" bindconfirm="onConfirmSearchInput" />
+</view>
+<view style="flex: auto;overflow: hidden;">
+  <scroll-view class="list-wrapper scrollable" scroll-y type="custom" enhanced bind:scrolltolower="loadMore">
+    <list-builder list="{{dataset}}" child-height="125">
+      <diet-card slot:item slot:index item="{{item}}" index="{{index}}"></diet-card>
+    </list-builder>
+    <view class="tips" wx:if="{{loading}}">加载中...</view>
+    <view class="tips" wx:elif="{{isLastPage}}">没有更多的了</view>
+  </scroll-view>
+</view>
+
+<view style="height: {{container.safeBottomOffset}}px;flex: none;"></view>
+<t-message id="{{$messageId}}" />

+ 5 - 0
miniprogram/module/article/pages/science-info/science-info.json

@@ -0,0 +1,5 @@
+{
+  "renderer": "webview",
+  "component": true,
+  "usingComponents": {}
+}

+ 15 - 0
miniprogram/module/article/pages/science-info/science-info.scss

@@ -0,0 +1,15 @@
+@import "../../../../themes/page.scss";
+/* module/diet/pages/science-info/science-info.wxss */
+
+.title {
+  margin: 12px 12px 6px;
+  font-size: 16px;
+}
+
+.description {
+  display: flex;
+  justify-content: space-between;
+  padding: 0 12px;
+  font-size: 12px;
+  color: #929292;
+}

+ 37 - 0
miniprogram/module/article/pages/science-info/science-info.ts

@@ -0,0 +1,37 @@
+import PageContainerBehavior from "../../../../core/behavior/page-container.behavior";
+import tickleBehavior, { getTickleContext } from "../../../../core/behavior/tickle.behavior";
+import { getScienceMethod } from "../../request";
+// module/diet/pages/science-info/science-info.ts
+Component({
+  behaviors: [
+    PageContainerBehavior,
+    tickleBehavior,
+  ],
+  lifetimes: {
+    attached() {
+      const channel = this.getOpenerEventChannel();
+      channel.on?.('load', (data: AnyObject) => {
+        this.setData({ title: data?.title ?? '' });
+      })
+      this.load();
+    }
+  },
+  properties: {
+    id: { type: String, value: '' },
+  },
+  data: {
+    title: ''
+  },
+  methods: {
+    async load() {
+      wx.showLoading({ title: '加载中' });
+      try {
+        const dataset = await getScienceMethod(this.data.id);
+        this.setData({ dataset, title: dataset.name });
+      } catch (error) {
+        getTickleContext.call(this).showWarnMessage(error.errMsg);
+      }
+      wx.hideLoading();
+    }
+  }
+})

+ 18 - 0
miniprogram/module/article/pages/science-info/science-info.wxml

@@ -0,0 +1,18 @@
+<!--module/diet/pages/science-info/science-info.wxml-->
+<!-- <web-view src="https://mp.weixin.qq.com/"></web-view> -->
+
+<web-view wx:if="{{dataset.content}}" src="{{dataset.content}}"></web-view>
+<block v-else>
+  <t-navbar title="中医科普" left-arrow />
+  <scroll-view class="page-scroll__container" type="list" scroll-y style="{{containerStyle}}">
+    <image wx:if="{{dataset.briefImg}}" src="{{dataset.briefImg}}" mode="widthFix" style="width: 100%;"></image>
+    <view class="title">{{dataset.title}}</view>
+    <view class="description">
+      <view></view>
+      <view wx:if="{{dataset.readVolume}}">{{dataset.readVolume}}人阅读</view>
+    </view>
+  </scroll-view>
+</block>
+
+<view style="height: {{container.safeBottomOffset}}px;flex: none;"></view>
+<t-message id="{{$messageId}}" />

+ 7 - 0
miniprogram/module/article/pages/science-list/science-list.json

@@ -0,0 +1,7 @@
+{
+  "renderer": "skyline",
+  "component": true,
+  "usingComponents": {
+    "science-card": "../../components/science-card/science-card"
+  }
+}

+ 4 - 0
miniprogram/module/article/pages/science-list/science-list.scss

@@ -0,0 +1,4 @@
+@import '../../../../themes/page.scss';
+@import '../../searc-list.scss';
+
+/* module/diet/pages/science-list/science-list.wxss */

+ 81 - 0
miniprogram/module/article/pages/science-list/science-list.ts

@@ -0,0 +1,81 @@
+import PageContainerBehavior from "../../../../core/behavior/page-container.behavior";
+import tickleBehavior, { getTickleContext } from "../../../../core/behavior/tickle.behavior";
+import { getScienceListMethod } from "../../request";
+// module/diet/pages/science-list/science-list.ts
+Component({
+  behaviors: [
+    PageContainerBehavior,
+    tickleBehavior,
+  ],
+  lifetimes: {
+    attached() {
+      wx.showLoading({ title: '加载中' });
+      this.load();
+    }
+  },
+  properties: {
+    classify: { type: String, value: '' },
+  },
+  data: {
+    keyword: '',
+    searchInputProps: {
+      placeholderStyle: 'color: #929292;',
+      cursorColor: '#34A76B',
+      iconColor: '#34A76B',
+      focus: false,
+      placeholder: '输入搜索名称',
+    },
+    title: '',
+    dataset: [] as AnyObject[],
+    page: 1, size: 20, total: 0, loading: false, isLastPage: false,
+  },
+  observers: {
+    'classify'(value) {
+      const ref = { tonic: '药膳查询', tea: '中医茶饮' } as any;
+      this.setData({ title: ref[value] || '' })
+    },
+    'page,size,total'(page, size, total) {
+      this.setData({ isLastPage: page * size >= total })
+    }
+  },
+  methods: {
+    async load() {
+      try {
+        const { data, total } = await getScienceListMethod(1, this.data.size, {
+          keyword: this.data.keyword,
+        });
+        this.setData({ dataset: data, total });
+      } catch (error) {
+        getTickleContext.call(this).showWarnMessage(error.errMsg);
+      }
+      wx.hideLoading();
+    },
+    async loadMore() {
+      if (this.data.isLastPage || this.data.loading) return;
+      this.setData({ loading: true });
+      try {
+        const _page = this.data.page + 1;
+        const { data, total } = await getScienceListMethod(_page, this.data.size, {
+          keyword: this.data.keyword,
+        });
+        this.setData({ dataset: [...this.data.dataset, ...data], total, page: _page });
+      } catch (error) {
+        getTickleContext.call(this).showWarnMessage(error.errMsg);
+      }
+      this.setData({ loading: false });
+    },
+    onConfirmSearchInput(event: WechatMiniprogram.InputConfirm) {
+      const _keyword = this.data.keyword;
+      this.setData({ keyword: event.detail.value });
+      wx.showLoading({ title: '搜索中' });
+      this.load().catch(() => { this.setData({ keyword: _keyword }) });
+    },
+    toInfoPage(event: WechatMiniprogram.TouchEvent) {
+      const id = event.currentTarget.id;
+      wx.navigateTo({ url: `/module/article/pages/science-info/science-info?id=${id}` })
+        .then(res => {
+          res.eventChannel.emit('load', this.data.dataset.find(item => item.id.toString() === id))
+        })
+    }
+  }
+})

+ 18 - 0
miniprogram/module/article/pages/science-list/science-list.wxml

@@ -0,0 +1,18 @@
+<!--module/diet/pages/science-list/science-list.wxml-->
+<t-navbar title="中医科普" left-arrow />
+<view class="search-wrapper" bindtap="onTapSearchInput">
+  <icon class="search-icon" data-button="focus" type="search" size="14" color="{{searchInputProps.iconColor}}"></icon>
+  <input class="search-input" value="{{keyword}}" focus="{{searchInputProps.focus}}" placeholder="{{searchInputProps.placeholder}}" placeholder-style="{{searchInputProps.placeholderStyle}}" cursor-color="{{searchInputProps.cursorColor}}" confirm-type="search" bindconfirm="onConfirmSearchInput" />
+</view>
+<view style="flex: auto;overflow: hidden;">
+  <scroll-view class="list-wrapper scrollable" scroll-y type="custom" enhanced bind:scrolltolower="loadMore">
+    <list-builder list="{{dataset}}" child-height="125">
+      <science-card slot:item slot:index item="{{item}}" index="{{index}}"></science-card>
+    </list-builder>
+    <view class="tips" wx:if="{{loading}}">加载中...</view>
+    <view class="tips" wx:elif="{{isLastPage}}">没有更多的了</view>
+  </scroll-view>
+</view>
+
+<view style="height: {{container.safeBottomOffset}}px;flex: none;"></view>
+<t-message id="{{$messageId}}" />

+ 42 - 0
miniprogram/module/article/request.ts

@@ -0,0 +1,42 @@
+import { Get, Post } from "../../lib/request/method";
+
+export function getDietListMethod(page: number, size: number, query?: { keyword?: string, classify: 'tonic' | 'tea' }) {
+  return Post(`/dietRecipe/pageDietRecipe`, { page, limit: size, classify: query?.classify, keyWord: query?.keyword }, {
+    transform({ data }: AnyObject) {
+      return {
+        total: data.total, page: data?.current ?? page, size: data?.size ?? size,
+        data: data.records
+      };
+    }
+  })
+}
+
+
+export function getDietMethod(id: string) {
+  return Post(`/dietRecipe/dietRecipeDetail`, { id }, {
+    transform({ data }: AnyObject) {
+      return data;
+    }
+  })
+}
+
+export function getScienceListMethod(page: number, size: number, query?: { keyword?: string }) {
+  return Post(`/psarticle/pagePsarticle?pageNum=${page}&pageSize=${size}&keyword=${query?.keyword}`, void 0, {
+    transform({ data }: AnyObject) {
+      return {
+        total: data.total, page: data?.current ?? page, size: data?.size ?? size,
+        data: data.data.map((item: AnyObject) => (item.id = item.popularScienceArticleId, item))
+      };
+    }
+  })
+}
+
+export function getScienceMethod(id: string) {
+  return Get(`/psarticle/getPsarticleDetailById`, {
+    params: { popularScienceArticleId: id },
+    transform({ data }: AnyObject) {
+      const { popularScienceArticleId: id, ..._data } = data;
+      return { id, ..._data };
+    }
+  })
+}

+ 42 - 0
miniprogram/module/article/searc-list.scss

@@ -0,0 +1,42 @@
+.search-wrapper {
+  display: flex;
+  align-items: center;
+  margin: 12px;
+  padding: 4px 8px;
+  border: 1px solid #34A76B;
+  border-radius: 8px;
+
+  .search-icon {
+    flex: none;
+    margin-right: 4px;
+  }
+
+  .search-input {
+    flex: auto;
+    color: #fff;
+  }
+}
+
+page {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+}
+
+.scrollable {
+  height: 100%;
+}
+
+.list-wrapper {
+  .list-title {
+    font-size: 16px;
+    color: #34A76B;
+  }
+
+}
+
+.tips {
+  text-align: center;
+  font-size: 12px;
+  color: #A2AFB2;
+}

File diff suppressed because it is too large
+ 0 - 0
miniprogram/module/charts/ec-canvas/echarts.min.js


+ 156 - 0
miniprogram/module/charts/record-index/chalk.theme.ts

@@ -0,0 +1,156 @@
+const theme = {
+  "color": [
+    "#72ccff",
+    "#f7c5a0"
+  ],
+  "backgroundColor": "#0f2226",
+  "textStyle": {},
+  "title": {
+    "textStyle": {
+      "color": "#38ff6e"
+    },
+    "subtextStyle": {
+      "color": "#dddddd"
+    }
+  },
+  "line": {
+    "itemStyle": {
+      // "borderWidth": "4"
+    },
+    "lineStyle": {
+      "width": "2"
+    },
+    "symbolSize": 6,
+    "symbol": "circle",
+    "smooth": true
+  },
+  "categoryAxis": {
+    "axisLine": {
+      "show": true,
+      "lineStyle": {
+        "color": "#666666"
+      }
+    },
+    "axisTick": {
+      "show": true,
+      "lineStyle": {
+        "color": "#333"
+      }
+    },
+    "axisLabel": {
+      "show": true,
+      "color": "#aaaaaa"
+    },
+    "splitLine": {
+      "show": false
+    }
+  },
+  "valueAxis": {
+    "axisLine": {
+      "show": true,
+      "lineStyle": {
+        "color": "#666666"
+      }
+    },
+    "axisTick": {
+      "show": true,
+      "lineStyle": {
+        "color": "#333"
+      }
+    },
+    "axisLabel": {
+      "show": true,
+      "color": "#aaaaaa"
+    },
+    "splitLine": {
+      "show": true,
+      "lineStyle": {
+        "color": "#666666"
+      }
+    }
+  },
+  "timeAxis": {
+    "axisLine": {
+      "show": true,
+      "lineStyle": {
+        "color": "#666666"
+      }
+    },
+    "axisTick": {
+      "show": false,
+      "lineStyle": {
+        "color": "#333"
+      }
+    },
+    "axisLabel": {
+      "show": true,
+      "color": "#aaaaaa"
+    },
+    "splitLine": {
+      "show": true,
+      "lineStyle": {
+        "color": [
+          "#666666"
+        ]
+      }
+    },
+    "splitArea": {
+      "show": false,
+      "areaStyle": {
+        "color": [
+          "rgba(250,250,250,0.05)",
+          "rgba(200,200,200,0.02)"
+        ]
+      }
+    }
+  },
+  "legend": {
+    "textStyle": {
+      "color": "#ffffff"
+    }
+  },
+  "tooltip": {
+    "axisPointer": {
+      "lineStyle": {
+        "color": "#cccccc",
+        "width": 1
+      },
+      "crossStyle": {
+        "color": "#cccccc",
+        "width": 1
+      }
+    }
+  },
+  "visualMap": {
+    "color": [
+      "#fc97af",
+      "#72ccff",
+      "#fc97af"
+    ]
+  },
+  "dataZoom": {
+    "backgroundColor": "rgba(255,255,255,0)",
+    "dataBackgroundColor": "rgba(114,204,255,1)",
+    "fillerColor": "rgba(114,204,255,0.2)",
+    "handleColor": "#72ccff",
+    "handleSize": "100%",
+    "textStyle": {
+      "color": "#333333"
+    }
+  },
+  "markPoint": {
+    "label": {
+      "color": "#293441"
+    },
+    "emphasis": {
+      "label": {
+        "color": "#293441"
+      }
+    }
+  }
+}
+
+export default [
+  'chalk',
+  theme,
+]

+ 47 - 100
miniprogram/module/charts/record-index/health-index.ts

@@ -1,93 +1,34 @@
 function gather(
   data: App.Health.Index.Data[],
-  transform: (model: App.Health.Index.Model) => any,
+  transform: (model: App.Health.Index.Model, index?: number) => any,
   skip = false,
-  link = { 血压: [1, 2], }
+  // link = { 血压: ['收缩压', '舒张压'], }
+  link = {}
 ) {
   const ref = ((link) => {
     const ref: Record<string, { id: string; name: string; }> = {};
     Object.entries(link).forEach(([name, keys]) => {
       const _ = { id: name, name };
-      keys.forEach(key => { ref[key] = _; })
+      (keys as AnyArray).forEach(key => { ref[key] = _; })
     })
     return ref;
   })(link);
 
-  const cache = new Map<{ id: string; name: string; }, App.Health.Index.Ruler[]>();
+  const cache = new Map<{ id: string; name: string; }, App.Health.Index.Chart[]>();
   for (const item of data) {
     if (skip && item.isAuto) continue;
     const model = createHealthIndex(item);
-    const _ = ref[model.id] ?? { id: model.id, name: model.name };
+    const _ = ref[model.name] ?? { id: model.id, name: model.name };
     if (ref[model.id]) model.name = model.name.replace(_.name, '');
     if (cache.has(_)) {
-      cache.get(_)?.push(transform(model))
+      cache.get(_)?.push(transform(model, cache.get(_)?.length));
     } else {
-      cache.set(_, [transform(model)])
+      cache.set(_, [transform(model)]);
     }
   }
   return [...cache].map(gather => ({ ...gather[0], options: gather[1] }))
 }
 
-export function healthIndex2Ruler(model: App.Health.Index.Model): App.Health.Index.Ruler {
-  let { scope, range, precision, values, ...ruler } = model;
-  const min = range?.[0] ?? Math.floor(scope[0] * 0.5);
-  const max = range?.[1] ?? Math.floor(scope[1] * 1.5);
-  precision = ((diff) => {
-    if (diff < 5) return 0.01;
-    if (diff < 10) return 0.1;
-    else return 1;
-  })(max - min)
-  return {
-    ...ruler, min, max, precision,
-    markLine: [
-      { value: scope[0], style: {} },
-      { value: scope[1], style: {} },
-    ],
-    value: values?.slice(-1)[0]?.value,
-  }
-}
-
-export function healthIndex2Progress(model: App.Health.Index.Model[]): AnyObject[] {
-  return model.filter((item) => item.values?.length).map(item => {
-    const { scope, range, values } = item;
-    const { value, abnormal, description = '' } = values?.slice(-1)[0]!
-    const length = range[1] - range[0];
-
-    const scopeOffsetLeft = `${Math.floor((scope[0] - range[0]) * 100 / length)}%`;
-    const scopeOffsetRight = `${100 - Math.floor((scope[1] - range[0]) * 100 / length)}%`;
-
-    let valueOffset = Math.floor((value - range[0]) * 100 / length);
-    let valueOffsetLeft = '0';
-    let valueOffsetRight = '0';
-    let valueType;
-
-    if (value > scope[1]) {
-      valueType = 'big';
-      valueOffsetLeft = scopeOffsetLeft;
-      valueOffsetRight = `${100 - valueOffset}%`;
-    } else if (value < scope[0]) {
-      valueType = 'small';
-      valueOffsetLeft = `${valueOffset}%`;
-      valueOffsetRight = scopeOffsetRight;
-    } else {
-      valueType = 'normal';
-      valueOffsetLeft = scopeOffsetLeft;
-      valueOffsetRight = scopeOffsetRight;
-    }
-
-    return {
-      name: item.name, unit: item.unit,
-      value, min: scope[0], max: scope[1],
-      abnormal, description: description?.replace(item.name, ''),
-      scopeOffsetLeft, scopeOffsetRight,
-      valueType, valueOffset: `${valueOffset}%`,
-      valueOffsetLeft, valueOffsetRight,
-    }
-  })
-}
-
-
-
 export function createHealthIndex(data: App.Health.Index.Data): App.Health.Index.Model {
   const { quotaId: id, minVal, maxVal, inputMin, inputMax, inputPrecision, patientQuotaRecordDTOS, ...model } = data;
   const scope = [+minVal, +maxVal] as const;
@@ -105,58 +46,64 @@ export function createHealthIndex(data: App.Health.Index.Data): App.Health.Index
   } as App.Health.Index.Model
 }
 
-
-export function transformHealthIndex2Ruler(data: App.Health.Index.Data[]): { id: string, name: string, options: App.Health.Index.Ruler[] }[] {
-  // const ref = ((link) => {
-  //   const ref: Record<string, { id: string; name: string; }> = {};
-  //   Object.entries(link).forEach(([name, keys]) => {
-  //     const _ = { id: name, name };
-  //     keys.forEach(key => { ref[key] = _; })
-  //   })
-  //   return ref;
-  // })({ 血压: [1, 2], })
-  // const cache = new Map<{ id: string; name: string; }, App.Health.Index.Ruler[]>();
-  // for (const item of data) {
-  //   if (item.isAuto) continue;
-  //   const model = createHealthIndex(item);
-  //   const _ = ref[model.id] ?? { id: model.id, name: model.name };
-  //   if (ref[model.id]) model.name = model.name.replace(_.name, '');
-  //   if (cache.has(_)) {
-  //     cache.get(_)?.push(healthIndex2Ruler(model))
-  //   } else {
-  //     cache.set(_, [healthIndex2Ruler(model)])
-  //   }
-  // }
-  // return [...cache].map(gather => ({ ...gather[0], options: gather[1] }))
-  return gather(data, healthIndex2Ruler, true);
-}
-
-export function healthIndex2Chart(model: App.Health.Index.Model): AnyObject {
-  return {
+export function healthIndex2Chart(model: App.Health.Index.Model, index = 0): AnyObject {
+  const data = model.values?.map(t => [t.date, t.value]) ?? [];
+  const yAxis = {
+    name: model.unit,
+    nameTextStyle: { color: '#ccc' },
+    type: 'value', scale: true,
+    min: model.range[0],
+    max: Math.min(
+      model.range[1],
+      Math.floor(Math.max(model.scope[1], ...data.map((item: AnyArray) => item[1])) * 2)
+    ),
+    minInterval: model.precision >= 1 ? model.precision : void 0,
+    axisLine: { show: true }
+  }
+  const series = {
     name: model.name,
+    yAxisIndex: 0,
     type: 'line', smooth: true,
-    data: model.values?.map(t => [t.date, t.value]),
+    data,
     markLine: {
       data: model.scope.map(value => { return { yAxis: value } })
     }
   }
+  const visualMap = {
+    show: false,
+    type: 'piecewise',
+    pieces: [
+      { gt: model.scope[1] },
+      { gte: model.scope[0], lte: model.scope[1] },
+      { lt: model.scope[0] }
+    ]
+  }
+
+  return { yAxis, series, visualMap }
 }
 
 export function transformHealthIndex2Chart(data: App.Health.Index.Data[]): AnyObject[] {
   const list = gather(data, healthIndex2Chart)
+  console.log(list, 'transformHealthIndex2Chart-->list');
+
   const charts = [];
   for (const item of list) {
     const xAxis = { type: 'category', axisLabel: { overflow: 'breakAll' } }
-    const yAxis = { type: 'value', scale: true, }
-    const series = item.options.filter((item: any) => item.data?.length)
+    const yAxis = [], series = [], visualMap = [];
+    for (const option of item.options) {
+      yAxis.push(option.yAxis);
+      visualMap.push(option.visualMap);
+      if (option.series.data?.length) series.push(option.series);
+    }
     if (!series.length) continue;
     charts.push({
       id: item.id,
       title: { text: `${item.name}` },
       tooltip: { trigger: 'axis' },
       legend: { show: series.length > 1 },
-      xAxis, yAxis,
-      series,
+      grid: { bottom: 25 },
+      xAxis, yAxis: yAxis[0],
+      series, visualMap,
     })
   }
   return charts;

+ 3 - 0
miniprogram/module/charts/record-index/record-index.scss

@@ -1,4 +1,7 @@
 /* module/health/pages/record-index/record-index.wxss */
+page {
+  background-color: #0F2226;
+}
 .chart {
   margin: 12px 0;
 }

+ 4 - 1
miniprogram/module/charts/record-index/record-index.ts

@@ -1,6 +1,9 @@
 import * as echarts from '../ec-canvas/echarts.min';
+import Theme from './chalk.theme';
 import { healthIndexReportMethod } from './request';
 
+echarts.registerTheme(...Theme);
+
 // module/health/pages/record-index/record-index.ts
 Component({
   lifetimes: {
@@ -39,7 +42,7 @@ Component({
           return {
             id: option.id,
             onInit(canvas: any, width: number, height: number, dpr: number) {
-              const ec = echarts.init(canvas, null, {
+              const ec = echarts.init(canvas, Theme[0], {
                 width: width,
                 height: height,
                 devicePixelRatio: dpr

BIN
miniprogram/module/chats/assets/face-1.png


+ 3 - 0
miniprogram/module/chats/components/guide/guide.ts

@@ -30,6 +30,9 @@ Component({
         }
       });
     },
+    handleD() {
+      if (this.data.active) wx.navigateBack();
+    },
 
     _update(result: string[]) {
       if (result.length) {

+ 2 - 1
miniprogram/module/chats/components/guide/guide.wxml

@@ -17,7 +17,8 @@
     </t-cell>
     <t-cell t-class="cell-border-gradient {{_.getClassName(active)}}" title="1、健康分析" hover="{{active}}" bind:tap="handleA"></t-cell>
     <t-cell t-class="cell-border-gradient {{_.getClassName(active)}}" title="2、指标信息管理" hover="{{active}}" bind:tap="handleB"></t-cell>
-    <t-cell t-class="{{_.getClassName(active)}}" title="3、健康信息管理" bordered="{{false}}" hover="{{active}}" bind:tap="handleC"></t-cell>
+    <t-cell t-class="cell-border-gradient {{_.getClassName(active)}}" title="3、健康信息管理" hover="{{active}}" bind:tap="handleC"></t-cell>
+    <t-cell t-class="{{_.getClassName(active)}}" title="4、返回首页" bordered="{{false}}" hover="{{active}}" bind:tap="handleD"></t-cell>
   </view>
 </view>
 

+ 4 - 0
miniprogram/module/chats/components/message-select/message-select.scss

@@ -82,4 +82,8 @@
     top: 1.5px;
     z-index: 1;
   }
+}
+
+.item.disabled {
+  opacity: 0.5;
 }

+ 13 - 20
miniprogram/module/chats/components/message-select/message-select.ts

@@ -24,11 +24,14 @@ Component({
     subMultiple: false,
     itemHeight: 48,
 
-    result: '',
+    result: '', hasSelected: false,
   },
   observers: {
     'payload.options'(options) {
       this.setData({ options });
+    },
+    'options'(options: AnyArray) {
+      this.setData({ hasSelected: options.some(option => option.checked) });
     }
   },
   methods: {
@@ -51,24 +54,6 @@ Component({
 
       if (checked && !multiple) { this.onConfirm(); }
     },
-    async handle(event: HandleEvent) {
-      const { mark: { type, item, index } } = event;
-      if (!item || !this.data.active) return;
-
-      switch (type) {
-        case 'options':
-          const options = this._handle(this.data.options, item, index, this.data.payload.multiple);
-          this.setData({ options });
-          break;
-        case 'sub':
-          const subOptions = this._handle(this.data.subOptions, item, index, this.data.multiple);
-          this.setData({ subOptions });
-          break;
-      }
-
-      console.log(this.data);
-
-    },
     _handle(options: Option[], item: Option, index: number, multiple?: boolean) {
       const checked = !item.checked;
       if (checked) {
@@ -113,7 +98,8 @@ Component({
     onCancel() { },
     onConfirm() { },
     onSubmit() {
-      if (!this.data.options.some(option => option.checked)) {
+      if (this.data.result) return;
+      if (!this.data.hasSelected) {
         wx.showToast({ title: '请至少选择一项', icon: 'error' })
       } else {
         console.log(this.data.options);
@@ -129,6 +115,13 @@ Component({
           .join('; ');
         this.setData({ result })
 
+        this.triggerEvent('next', { options: this.data.options });
+      }
+    },
+    onSkip() {
+      if (this.data.result || this.data.hasSelected) return;
+      if (!this.data.payload.required) {
+        this.setData({ result: '都没有' });
         this.triggerEvent('next', { options: this.data.options });
       }
     }

+ 6 - 12
miniprogram/module/chats/components/message-select/message-select.wxml

@@ -32,8 +32,12 @@
         </view>
       </grid-builder>
     </scroll-view>
-    <view wx:if="{{payload.multiple}}" class="chat-card__handle {{active ? '' : 'disabled'}}">
-      <view class="item" bind:tap="onSubmit">
+    <view wx:if="{{payload.multiple || !payload.required}}" class="chat-card__handle {{active ? '' : 'disabled'}}">
+      <view wx:if="{{!payload.required}}" class="item {{hasSelected ? 'disabled': ''}}" bind:tap="onSkip">
+        <image src="../../assets/button-1.bg.png" mode="aspectFit" />
+        <view style="color: #d4d4d4;">都没有</view>
+      </view>
+      <view wx:if="{{payload.multiple}}" class="item" bind:tap="onSubmit">
         <image src="../../assets/button-1.bg.png" mode="aspectFit" />
         <view>提交</view>
       </view>
@@ -66,15 +70,5 @@
         </view>
       </grid-builder>
     </scroll-view>
-
-
-    <!-- <scroll-view type="list" scroll-y style="{{_.maxHeight(subOptions)}}">
-      <view class="options-wrapper" mark:type="sub" bind:tap="handle">
-        <view wx:for="{{subOptions}}" wx:key="id" class="card {{_.getClassName(item)}}" style="height: {{itemHeight}}px;" mark:item="{{item}}" mark:index="{{index}}">
-          <t-icon wx:if="{{item.checked}}" name="check" t-class="card__icon" ariaHidden="{{true}}" />
-          <text overflow="ellipsis" max-lines="3">{{item.name}}</text>
-        </view>
-      </view>
-    </scroll-view> -->
   </view>
 </t-popup>

+ 49 - 46
miniprogram/module/chats/components/questionnaire/questionnaire.ts

@@ -19,9 +19,6 @@ Component({
   lifetimes: {
     attached() { this._start(); }
   },
-  /**
-   * 组件的属性列表
-   */
   properties: {
 
   },
@@ -59,68 +56,74 @@ Component({
       this._next()
     },
     _start() {
-      this._createMessage({
-        id: `start`, type: 'system',
-        payload: { title: '对话管家 已进入聊天', date: this.data._timestamp },
-      }, {
+      this.setData({
         [`_next.dialogId`]: '',
         [`_next.questions`]: [],
+        _timestamp: Date.now(),
       })
       this._next();
     },
     _end() {
-      this._createMessage({
-        id: `end`, type: 'system',
-        payload: { title: '对话管家 已结束聊天', date: Date.now() },
-      }, {
+      this.setData({
         [`_next.dialogId`]: '',
         [`_next.questions`]: [],
       })
       this.triggerEvent('next', { component: 'guide', scroll: true });
     },
     async _next() {
-      const { data } = await Post(`/dialogueManage/dialog`, this.data._next);
-      data.nextQuestions?.forEach((question: any, index: number) => {
-        if (question.css === 'tongue') {
-          this._createMessage({
-            id: `${question.classify}.${question.id}.${index}`, type: 'analysis',
-            payload: { title: question.title }
-          });
-        } else if (question.css === 'text') {
-          this._createMessage({
-            id: `${question.classify}.${question.id}.${index}`, type: 'text',
-            payload: { title: question.content }
-          });
-        } else if (['select', 'checkbox'].includes(question.css)) {
+      try {
+        const { data } = await Post(`/dialogueManage/dialog`, this.data._next);
+        data.nextQuestions?.forEach((question: any, index: number) => {
+          if (question.css === 'tongue') {
+            this._createMessage({
+              id: `${question.classify}.${question.id}.${index}`, type: 'analysis',
+              payload: { title: question.title }
+            });
+          } else if (question.css === 'text') {
+            this._createMessage({
+              id: `${question.classify}.${question.id}.${index}`, type: 'text',
+              payload: { title: question.content }
+            });
+          } else if (['select', 'checkbox'].includes(question.css)) {
+            this._createMessage({
+              id: `${question.classify}.${question.id}.${index}`, type: 'select',
+              payload: {
+                title: question.title,
+                options: question.options,
+                multiple: question.css === 'checkbox',
+                required: question.required
+              }
+            })
+          } else if (question.over) {
+            return this._end();
+          }
+        });
+        if (data.classify === 'report') {
+          const diff = dayjs().diff(this.data._timestamp, 'm');
           this._createMessage({
-            id: `${question.classify}.${question.id}.${index}`, type: 'select',
+            id: 'report', type: 'report',
             payload: {
-              title: question.title,
-              options: question.options,
-              multiple: question.css === 'checkbox',
+              title: `本次问答已结束,历史${diff || 1}分钟,非常感谢您的配合!请查看您本次的健康评估情况。`,
+              url: `/module/health/pages/report/report?id=${data.healthAnalysisReportId}`
             }
           })
-        } else if (question.over) {
-          return this._end();
         }
-      });
-      if (data.classify === 'report') {
-        const diff = dayjs().diff(this.data._timestamp, 'm');
-        this._createMessage({
-          id: 'report', type: 'report',
-          payload: {
-            title: `本次问答已结束,历史${diff || 1}分钟,非常感谢您的配合!请查看您本次的健康评估情况。`,
-            url: `/module/health/pages/report/report?id=${data.healthAnalysisReportId}`
-          }
+        if (data.over) return this._end();
+
+        this.setData({
+          [`_next.dialogId`]: data.dialogId,
+          [`_next.questions`]: data.nextQuestions,
         })
+        this.triggerEvent('to');
+      } catch (error) {
+        console.log(error);
+        const date = Date.now();
+        this._createMessage({
+          id: `system-${date}`, type: 'system',
+          payload: { date, title: error.errMsg ?? `分析错误,请重试!`, }
+        });
+        this._end();
       }
-      if (data.over) return this._end();
-
-      this.setData({
-        [`_next.dialogId`]: data.dialogId,
-        [`_next.questions`]: data.nextQuestions,
-      })
-      this.triggerEvent('to');
     },
 
     _createMessage(body: Message, data?: Record<string, any>) {

+ 1 - 1
miniprogram/module/chats/components/questionnaire/questionnaire.wxml

@@ -8,9 +8,9 @@
 </wxs>
 <!--module/chats/components/questionnaire/questionnaire.wxml-->
 <block wx:for="{{messages}}" wx:key="id">
-  <message-system wx:if="{{_.show(item,'system')}}" active="{{_.active(lastId,item.id)}}" id="{{item.id}}" payload="{{item.payload}}" bind:next="handle" />
   <message-analysis wx:if="{{_.show(item,'analysis')}}" active="{{_.active(lastId,item.id)}}" id="{{item.id}}" payload="{{item.payload}}" bind:next="handle" />
   <message-select wx:elif="{{_.show(item,'select')}}" active="{{_.active(lastId,item.id)}}" id="{{item.id}}" payload="{{item.payload}}" bind:next="handle" />
   <message-text wx:elif="{{_.show(item,'text')}}" active="{{_.active(lastId,item.id)}}" id="{{item.id}}" payload="{{item.payload}}" bind:next="handle" />
   <message-report wx:elif="{{_.show(item,'report')}}" active="{{_.active(lastId,item.id)}}" id="{{item.id}}" payload="{{item.payload}}" bind:next="handle" />
+  <message-system wx:elif="{{_.show(item,'system')}}" active="{{_.active(lastId,item.id)}}" id="{{item.id}}" payload="{{item.payload}}" />
 </block>

+ 14 - 17
miniprogram/module/chats/pages/analysis/analysis.ts

@@ -10,7 +10,7 @@ Component({
   data: {
     uploadList: [
       { target: 'tongueImgUrl', required: true, label: '舌面图', src: '../../assets/tongue-1.png' },
-      { target: 'tongueBackImgUrl', required: false, label: '舌下图', src: '../../assets/tongue-2.png' },
+      { target: 'tongueBackImgUrl', required: true, label: '舌下图', src: '../../assets/tongue-2.png' },
       { target: 'faceImgUrl', required: false, label: '面部图', src: '../../assets/face-1.png' },
     ],
     thumbnail: [] as string[],
@@ -30,7 +30,7 @@ Component({
         case 'preview':
           break;
         case 'upload':
-          this._chooseMedia(index).then(src => this._uploadMedia(index, src))
+          this._chooseMedia(index).then(src => src && this._uploadMedia(index, src))
           break;
         case 'upload:delete':
           this._deleteMedia(index);
@@ -44,11 +44,7 @@ Component({
           this.setData({ [`thumbnail.${index}`]: src });
           return src;
         })
-        .catch(({ errMsg }) => {
-          const message = (<AnyObject>{ 'chooseMedia:fail cancel': '取消上传' })[errMsg] ?? errMsg;
-          wx.showToast({ title: message, icon: 'none' });
-          throw { errMsg };
-        })
+        .catch(() => null);
     },
     _deleteMedia(index: number) {
       this.setData({
@@ -57,32 +53,33 @@ Component({
       })
     },
     _uploadMedia(index: number, src?: string) {
+      this.setData({ [`_queue.${index}`]: true });
       src ??= this.data.thumbnail[index];
       upload({
         params: { name: 'file', file: src! },
         transform({ data }) { return (<any>data).url; }
       }).then(src => {
         this.setData({ [`original.${index}`]: src })
-      }, () => {
-        wx.showToast({ title: '上传失败', icon: 'error' })
+      }, (error) => {
+        wx.showToast({ title: error?.errMsg ?? '上传失败', icon: 'error' })
         this.setData({
           [`thumbnail.${index}`]: '',
           [`original.${index}`]: '',
         })
+      }).then(() => {
+        this.setData({ [`_queue.${index}`]: false });
       })
     },
     onSubmit() {
       const data = [];
       for (let index = 0; index < this.data.uploadList.length; index++) {
         const item = this.data.uploadList[index];
-        if (item.required) {
-          if (this.data._queue[index]) {
-            wx.showToast({ title: `请等待${item.label}上传完成`, icon: 'loading' });
-            return;
-          } else if (!this.data.original[index]) {
-            wx.showToast({ title: `请上传${item.label}`, icon: 'none' });
-            return;
-          }
+        if (this.data._queue[index]) {
+          wx.showToast({ title: `请等待图片上传完毕`, icon: 'none' });
+          return;
+        } else if (item.required && !this.data.original[index]) {
+          wx.showToast({ title: `请上传${item.label}`, icon: 'none' });
+          return;
         }
         if (this.data.original[index]) data.push({ target: item.target, src: this.data.original[index], })
       }

+ 21 - 0
miniprogram/module/chats/pages/index/index.scss

@@ -4,4 +4,25 @@
 
 .chat {
   padding: 24px 12px;
+}
+
+.system-wrapper {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  margin: 6px 0;
+
+  .title {
+    padding: 4px 12px;
+    font-size: 12px;
+    color: #7F96A7;
+    background-color: #1B4F34;
+    border-radius: 5px;
+  }
+
+  .date {
+    font-size: 12px;
+    color: #999;
+    margin-bottom: 6px;
+  }
 }

+ 5 - 0
miniprogram/module/chats/pages/index/index.ts

@@ -1,3 +1,4 @@
+import dayjs from "dayjs";
 import PageContainerBehavior from "../../../../core/behavior/page-container.behavior";
 
 // module/chats/pages/index/index.ts
@@ -22,6 +23,9 @@ Component({
   behaviors: [PageContainerBehavior],
   lifetimes: {
     attached() {
+      this.setData({
+        date: dayjs().format('MM-DD HH:mm:ss'),
+      })
       const component = this.data.component as 'guide' | 'questionnaire'
       this.handle({ detail: { component, scroll: true } });
     },
@@ -35,6 +39,7 @@ Component({
     component: { type: String, value: 'guide' }
   },
   data: {
+    date: '',
     messages: {} as Record<number, Message>,
     lastId: '',
   },

+ 4 - 0
miniprogram/module/chats/pages/index/index.wxml

@@ -9,6 +9,10 @@
 <!--module/chats/pages/index/index.wxml-->
 <t-navbar title="对话管家" left-arrow />
 <scroll-view id="scrollview" class="page-scroll__container" style="{{containerStyle}}" type="list" scroll-y enhanced="{{true}}" enable-passive scroll-into-view="{{lastId}}" scroll-into-view-alignment="end">
+  <view class="system-wrapper">
+    <view class="date">{{date}}</view>
+    <view class="title">对话管家 已进入聊天</view>
+  </view>
   <block wx:for="{{messages}}" wx:key="id">
     <chat-guide wx:if="{{_.show(item, 'guide')}}" id="{{item.id}}" active="{{_.active(lastId, item.id)}}" bind:next="handle" bind:to="scrollIntoView" />
     <chat-questionnaire wx:if="{{_.show(item, 'questionnaire')}}" id="{{item.id}}" active="{{_.active(lastId, item.id)}}" bind:next="handle" bind:to="scrollIntoView" />

+ 4 - 0
miniprogram/module/health/components/card-report/card-report.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 0 - 0
miniprogram/module/health/components/card-report/card-report.scss


+ 11 - 0
miniprogram/module/health/components/card-report/card-report.ts

@@ -0,0 +1,11 @@
+// module/health/components/card-report/card-report.ts
+Component({
+  options: {
+    multipleSlots: true,
+  },
+  properties: {
+    dataset: { type: Object, value: {} },
+  },
+  data: {},
+  methods: {}
+})

+ 1 - 0
miniprogram/module/health/components/card-report/card-report.wxml

@@ -0,0 +1 @@
+<!--module/health/components/card-report/card-report.wxml-->

+ 4 - 0
miniprogram/module/health/components/field-ruler/field-ruler.scss

@@ -33,4 +33,8 @@
     content: '/';
     margin: 0 4px;
   }
+}
+
+.popup__content {
+  padding-bottom: 40px;
 }

+ 1 - 1
miniprogram/module/health/components/field-ruler/field-ruler.ts

@@ -36,6 +36,6 @@ Component({
     },
     onCancel() {
       this.setData({ visible: false })
-    }
+    },
   }
 })

+ 9 - 3
miniprogram/module/health/components/field-ruler/field-ruler.wxml

@@ -1,9 +1,15 @@
+<wxs module="_">
+  module.exports.formatter = function (value, precision) {
+    console.log(value, precision);
+    return value;
+  }
+</wxs>
 <!--module/health/components/field-ruler/field-ruler.wxml-->
 <view class="field-picker" bind:tap="onShow">
   <view class="field-picker__inner">
     <view wx:if="{{selected.length}}" class="scrollbar">
       <view class="item" wx:for="{{options}}" wx:key="*this">
-        <text name="{{item.name}}">{{selected[index]}}</text>
+        <text name="{{item.name}}">{{_.formatter(selected[index], options[index].precision)}}</text>
         <text style="margin-left: 2px;">{{item.unit}}</text>
       </view>
     </view>
@@ -21,8 +27,8 @@
     <view class="btn btn--confirm" aria-role="button" bind:tap="onConfirm">确定</view>
   </view>
   <view class="popup__content">
-    <form-ruler wx:for="{{options}}" min="{{item.min}}" max="{{item.max}}" precision="{{item.precision}}" model:value="{{scrollValue[index]}}" default-value="{{visible ? selected[index] : 0}}">
-      <view slot="before">{{item.label}}</view>
+    <form-ruler wx:for="{{options}}" min="{{item.min}}" max="{{item.max}}" precision="{{item.precision}}" model:value="{{scrollValue[index]}}" default-value="{{visible ? selected[index] : 0}}" line="{{item.markLine}}">
+      <view slot="before" wx:if="{{item.name !== title}}">{{item.name}}</view>
       <view slot="after">{{item.unit}}</view>
     </form-ruler>
   </view>

+ 6 - 0
miniprogram/module/health/components/horizontal-scrollable/horizontal-scrollable.json

@@ -0,0 +1,6 @@
+{
+  "component": true,
+  "usingComponents": {
+    "t-icon": "tdesign-miniprogram/icon/icon"
+  }
+}

+ 44 - 0
miniprogram/module/health/components/horizontal-scrollable/horizontal-scrollable.scss

@@ -0,0 +1,44 @@
+/* module/health/components/horizontal-scrollable/horizontal-scrollable.wxss */
+.scheme {
+  &__card-list {
+    margin-left: -8px;
+    overflow: hidden;
+  }
+
+  &__card {
+    flex: none;
+    padding: 0 8px;
+
+    image {
+      height: 90px;
+    }
+  }
+
+  image+&__title {
+    margin-top: 4px;
+  }
+
+  &__title {
+    font-size: 14px;
+    color: #34A76B;
+  }
+
+  &__media {
+    position: relative;
+    min-height: 90px;
+
+    .icon {
+      position: absolute;
+      right: 4px;
+      bottom: 4px;
+      background-color: rgba(0, 0, 0, 0.2);
+      border-radius: 50%;
+    }
+  }
+}
+
+.scrollable {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: nowrap;
+}

+ 53 - 0
miniprogram/module/health/components/horizontal-scrollable/horizontal-scrollable.ts

@@ -0,0 +1,53 @@
+// module/health/components/horizontal-scrollable/horizontal-scrollable.ts
+const { shared, spring, runOnJS } = wx.worklet;
+Component({
+  lifetimes: {
+    ready() {
+    },
+    attached() {
+      const x = shared(0);
+      const offset = shared(1);
+      (<any>this).applyAnimatedStyle('.scrollable', () => {
+        'worklet';
+        return { transform: `translate(${x.value}px)`, };
+      });
+      (<any>this).x = x;
+      (<any>this).offset = offset;
+    }
+  },
+  properties: {
+    dataset: { type: Array, value: [] },
+  },
+  data: {
+
+  },
+  methods: {
+    handleHorizontalDrag(event: { deltaX: number }) {
+      'worklet';
+      if ((<any>this).offset.value === 1) runOnJS(this.start.bind(this))();
+      else {
+        const value = Math.max((<any>this).x.value + event.deltaX, (<any>this).offset.value);
+        (<any>this).x.value = Math.min(value, 0)
+      }
+    },
+
+    start() {
+      console.log('触发');
+
+      const query = this.createSelectorQuery()
+      query.select('.scrollable').boundingClientRect();
+      query.selectAll('.scheme__card').boundingClientRect();
+      query.exec(([box, sub]) => {
+        const subWidth = sub[sub.length - 1].right - sub[0].left;
+        const boxWidth = box.width;
+        (<any>this).offset.value = subWidth > boxWidth ? box.width - subWidth : 0;
+      })
+    },
+    preview(event: WechatMiniprogram.TouchEvent) {
+      const url = event.target.dataset?.url ?? '';
+      const sources = this.data.dataset.filter(item => ['image', 'video'].includes(item.type));
+      const current = Math.max(sources.findIndex(item=>item.url === url), 0);
+      wx.previewMedia({sources, current})
+    }
+  }
+})

+ 14 - 0
miniprogram/module/health/components/horizontal-scrollable/horizontal-scrollable.wxml

@@ -0,0 +1,14 @@
+<!--module/health/components/horizontal-scrollable/horizontal-scrollable.wxml-->
+<horizontal-drag-gesture-handler onGestureEvent="handleHorizontalDrag">
+  <view class="scheme__card-list" bind:tap="start">
+    <view class="scrollable">
+      <view class="scheme__card" wx:for="{{dataset}}" wx:key="id">
+        <view class="scheme__media {{item.type}}" wx:if="{{item.url}}">
+          <image src="{{item.poster}}" mode="heightFix" catch:tap="preview" data-url="{{item.url}}" />
+          <t-icon class="icon" wx:if="{{item.type === 'video'}}" name="play-circle" size="48rpx" catch:tap="preview" data-url="{{item.url}}"></t-icon>
+        </view>
+        <view class="scheme__title">{{item.title}}</view>
+      </view>
+    </view>
+  </view>
+</horizontal-drag-gesture-handler>

+ 2 - 0
miniprogram/module/health/components/report-health-index/report-health-index.scss

@@ -1,5 +1,6 @@
 @import "../../../../themes//t.cell.scss";
 @import "../../report-common.scss";
+@import "../../../../themes/card.scss";
 /* module/health/components/report-health-index/report-health-index.wxss */
 
 .health-index {
@@ -18,6 +19,7 @@
     position: relative;
     height: $size * 6;
     font-size: 12px;
+    padding: 8px 0;
 
     .value {
       position: absolute;

+ 8 - 2
miniprogram/module/health/components/report-health-patient/report-health-patient.scss

@@ -1,5 +1,6 @@
-@import "../../../../themes//t.cell.scss";
+@import "../../../../themes/t.cell.scss";
 @import "../../report-common.scss";
+@import "../../../../themes/card.scss";
 /* module/health/components/report-health-patient/report-health-patient.wxss */
 
 .card-body {
@@ -14,6 +15,7 @@
   display: flex;
   flex-direction: row;
   align-items: center;
+  font-size: 14px;
 
   .avatar {
     flex: none;
@@ -25,7 +27,7 @@
   }
 
   .title {
-    font-size: 14px;
+    font-size: 16px;
   }
 }
 
@@ -62,4 +64,8 @@
 
 .primary {
   color: #34A76B;
+}
+
+.tag + .tag {
+  margin-left: 4px;
 }

+ 7 - 5
miniprogram/module/health/components/report-health-patient/report-health-patient.wxml

@@ -2,7 +2,7 @@
 
 <!--module/health/components/report-health-patient/report-health-patient.wxml-->
 <view class="card-wrapper">
-  <t-cell t-class="card-header cell-border-gradient" t-class-description="patient-info-content">
+  <t-cell t-class="card-header no-body" bordered="{{false}}" t-class-description="patient-info-content">
     <block slot="description">
       <user-avatar class="avatar" size="60px"></user-avatar>
       <view class="content">
@@ -12,9 +12,11 @@
             <text>个人简介:</text>
             <text class="text-item">{{dictionary.label($dictionaries, 'sys_user_sex', dataset.sex)}}</text>
             <text class="text-item">{{dataset.age}}岁</text>
-            <text class="text-item" wx:for="{{specialPeriod && dictionary.label($dictionaries,'women_special_period', item) !== '无'}}" wx:key="*this">{{dictionary.label($dictionaries,'women_special_period', item)}}</text>
+            <block wx:for="{{specialPeriod}}" wx:key="*this">
+              <text class="text-item" wx:if="{{dictionary.label($dictionaries,'women_special_period', item) !== '无'}}">{{dictionary.label($dictionaries,'women_special_period', item)}}</text>
+            </block>
             <text class="text-item" wx:for="{{job}}" wx:key="*this">{{dictionary.label($dictionaries, 'job', item)}}</text>
-            <text class="text-item">手机号:{{dataset.phone}}</text>
+            <!-- <text class="text-item">手机号:{{dataset.phone}}</text> -->
             <text class="text-item">身份证号:{{dataset.cardno}}</text>
           </span>
         </view>
@@ -65,14 +67,14 @@
       </view>
       <span class="row">
         <text>喜好口味:</text>
-        <t-tag variant="outline" wx:for="{{hobbyFlavor}}" wx:key="*this">
+        <t-tag t-class="tag" variant="outline" wx:for="{{hobbyFlavor}}" wx:key="*this">
           {{dictionary.label($dictionaries, 'hobby_flavor', item)}}
         </t-tag>
         <t-tag variant="outline" wx:if="{{!hobbyFlavor.length}}">无</t-tag>
       </span>
       <span class="row">
         <text>食物过敏:</text>
-        <t-tag variant="outline" wx:for="{{foodAllergy}}" wx:key="*this">
+        <t-tag t-class="tag" variant="outline" wx:for="{{foodAllergy}}" wx:key="*this">
           {{dictionary.label($dictionaries, 'food_allergy', item)}}
         </t-tag>
         <t-tag variant="outline" wx:if="{{!foodAllergy.length}}">无</t-tag>

+ 6 - 5
miniprogram/module/health/components/report-health-scheme/report-health-scheme.wxml

@@ -8,11 +8,12 @@
     </view>
     <view class="row row-2">
       <view class="label">方案内容</view>
-      <scroll-view class="scrollable" type="list" scroll-y bounces="{{false}}">
-        <span class="value" wx:for="{{dataset.types}}" wx:key="*this">
-          <text wx:if="{{item.type}}">{{item.type}}</text>
-          <text wx:if="{{item.summary}}">{{item.summary}}</text>
-        </span>
+      <scroll-view class="scrollable" type="custom" scroll-y bounces="{{false}}">
+        <grid-builder list="{{dataset.types}}" cross-axis-count="2" cross-axis-gap="8" main-axis-gap="8">
+          <view slot:item slot:index style="height: 24px;">
+            <view>{{item.type}}</view>
+          </view>
+        </grid-builder>
       </scroll-view>
     </view>
   </view>

+ 2 - 1
miniprogram/module/health/components/report-health-status/report-health-status.json

@@ -6,6 +6,7 @@
     "t-cell": "tdesign-miniprogram/cell/cell",
     "t-icon": "tdesign-miniprogram/icon/icon",
     "t-loading": "tdesign-miniprogram/loading/loading",
-    "t-empty": "tdesign-miniprogram/empty/empty"
+    "t-empty": "tdesign-miniprogram/empty/empty",
+    "card-report": "../../components/card-report/card-report"
   }
 }

+ 1 - 0
miniprogram/module/health/components/report-health-status/report-health-status.scss

@@ -1,5 +1,6 @@
 @import "../../../../themes/t.cell.scss";
 @import "../../report-common.scss";
+@import "../../../../themes/card.scss";
 
 /* module/health/components/report-health-status/report-health-status.wxss */
 

+ 23 - 14
miniprogram/module/health/components/report-health-status/report-health-status.wxml

@@ -36,21 +36,30 @@
 
     <view slot="description" wx:if="{{!loading && !dataset}}">暂无数据</view>
   </t-cell>
-
-  <view class="card-body">
+  <view class="card-body report-wrapper">
     <view class="content-wrapper">
-      <view class="row title primary">
-        <view>{{dataset.willillStateName}}</view>
-      </view>
-      <span class="row">
-        <text class="text-item" wx:if="{{dataset.willillDegreeName}}">{{dataset.willillDegreeName}}</text>
-        <text class="text-item" wx:if="{{dataset.willillSocialName}}">{{dataset.willillSocialName}}</text>
-        <text class="text-item" wx:if="{{dataset.willillFunctionName}}">{{dataset.willillFunctionName}}</text>
-      </span>
-      <span class="row primary">
-        <text>体质:</text>
-        <text>{{dataset.constitutionGroupName}}</text>
-      </span>
+      <block wx:if="dataset">
+        <span class="row">
+          <text>结果显示您是:</text>
+          <text>{{dataset.willillStateName}}</text>
+        </span>
+        <span class="row">
+          <text>程度:</text>
+          <text>{{dataset.willillDegreeName}}</text>
+        </span>
+        <!-- <span class="row">
+            <text>类型:</text>
+            <text>{{dataset.willillSocialName}}</text>
+          </span> -->
+        <span class="row">
+          <text>表现:</text>
+          <text>{{dataset.willillFunctionName}}</text>
+        </span>
+        <span class="row">
+          <text>体质:</text>
+          <text>{{dataset.constitutionGroupName}}</text>
+        </span>
+      </block>
     </view>
     <image class="image-wrapper" src="../../assets/image/health-report.png" mode="aspectFill" />
   </view>

+ 2 - 1
miniprogram/module/health/pages/home/home.json

@@ -8,6 +8,7 @@
     "report-health-index": "../../components/report-health-index/report-health-index",
     "report-health-status": "../../components/report-health-status/report-health-status",
     "report-health-patient": "../../components/report-health-patient/report-health-patient",
-    "report-health-scheme": "../../components/report-health-scheme/report-health-scheme"
+    "report-health-scheme": "../../components/report-health-scheme/report-health-scheme",
+    "card-report": "../../components/card-report/card-report"
   }
 }

+ 12 - 22
miniprogram/module/health/pages/home/home.scss

@@ -1,32 +1,22 @@
 @import '../../../../themes/page.scss';
 @import '../../../../themes/draggable-sheet.scss';
 @import "../../report-common.scss";
+@import "../../../../themes/card.scss";
 /* module/health/pages/home/home.wxss */
 
-
-
-
-
-.bar {
-  height: 60px;
-  box-sizing: border-box;
-  background-color: #fff;
-  border-top-left-radius: 25px;
-  border-top-right-radius: 25px;
+.card-body.report-wrapper {
   display: flex;
+  flex-direction: row;
   align-items: center;
-  justify-content: center;
-  padding-bottom: 10px;
-  box-shadow: 0px -3px 0 1px rgba(0, 0, 0, 0.12);
-}
 
-.bar-shadow-placeholder {
-  height: 30px;
-}
+  .content-wrapper {
+    flex: auto;
+  }
 
-.indicator {
-  background-color: rgb(190, 186, 186);
-  border-radius: 3px;
-  height: 4px;
-  width: 80px;
+  .image-wrapper {
+    flex: none;
+    width: 128px;
+    height: 128px;
+    margin-left: 8px;
+  }
 }

+ 27 - 4
miniprogram/module/health/pages/home/home.ts

@@ -19,6 +19,9 @@ Component({
       this.getHealthIndex();
     }
   },
+  pageLifetimes: {
+    hide() { this.hideDraggableSheet({}); }
+  },
   properties: {},
   observers: {
     'healthReport.data'(data) {
@@ -81,7 +84,12 @@ Component({
         });
       }
     },
-
+    onDraggableSizeUpdate(e) {
+      'worklet'
+      if (e.pixels < 1) {
+        wx.worklet.runOnJS(this.hideDraggableSheet.bind(this))()
+      }
+    },
     async showReportList() {
       if (!this.data.healthReportList.loaded) {
         wx.showLoading({ title: '加载中' })
@@ -103,17 +111,32 @@ Component({
           animated: true,
           duration: 300,
           easingFunction: 'ease'
-        })
+        });
+        this.setData({ shade: 'shade' });
       } else {
-        wx.showToast({ title: '暂无报告记录' })
+        wx.showToast({ title: '暂无更新记录' })
       }
     },
+    hideDraggableSheet(e?: any) {
+      if (e) {
+        getDraggableSheetContext.call(this).scrollTo({
+          size: 0,
+          animated: true,
+          duration: 300,
+          easingFunction: 'ease'
+        });
+      }
+      this.setData({ shade: '' });
+    },
 
     toSchemePage(event: WechatMiniprogram.TouchEvent) { toSchemePage(event.mark?.id); },
     toReportPage(event: WechatMiniprogram.TouchEvent) { toReportPage(event.mark?.id); },
     toPatientPage: toPatientPage,
     toHealthIndexListPage() {
-      wx.navigateTo({ url: `/module/health/pages/record-index/record-index` })
+      wx.navigateTo({ url: `/module/charts/record-index/record-index` })
+    },
+    toHealthStatusListPage() {
+      wx.navigateTo({ url: `/module/health/pages/status-record/status-record` })
     },
   }
 })

+ 6 - 3
miniprogram/module/health/pages/home/home.wxml

@@ -7,11 +7,13 @@
       <t-icon t-class="icon" name="chevron-right-double-s" size="24px" />
     </view>
   </report-health-patient>
+
+
   <report-health-status dataset="{{healthReport.data}}" loading="{{healthReport.loading}}" message="{{healthReport.message}}" mark:id="{{healthId}}" bind:info="toReportPage">
-    <!-- <view class="extra-warapper" slot="extra-status" catch:tap="">
+    <view class="extra-warapper" slot="extra-status" catch:tap="toHealthStatusListPage">
       <text>更新记录</text>
       <t-icon t-class="icon" name="chevron-right-double-s" size="24px" />
-    </view> -->
+    </view>
     <view class="extra-warapper" slot="extra-report" catch:tap="showReportList">
       <text>更新记录</text>
       <t-icon t-class="icon" name="chevron-right-double-s" size="24px" />
@@ -28,7 +30,8 @@
 
 <t-message id="{{$messageId}}"></t-message>
 
-<draggable-sheet class="draggable-sheet-wrapper health-report-list" style="height: {{container.height}}px;" initial-child-size="0" min-child-size="0" max-child-size="0.8" snap="{{true}}" snap-sizes="{{[0.5]}}">
+<view wx:if="{{shade}}" class="draggable-sheet-shade" catch:tap="hideDraggableSheet"></view>
+<draggable-sheet class="draggable-sheet-wrapper health-report-list" style="height: {{container.height}}px;" initial-child-size="0" min-child-size="0" max-child-size="0.8" snap="{{true}}" snap-sizes="{{[0.5]}}" worklet:onsizeupdate="onDraggableSizeUpdate">
   <scroll-view class="scrollable draggable-sheet-container" type="custom" scroll-y associative-container="draggable-sheet" show-scrollbar="{{false}}" bounces="{{false}}">
     <sticky-section>
       <sticky-header>

+ 47 - 43
miniprogram/module/health/pages/report/report.scss

@@ -1,62 +1,66 @@
 @import '../../../../themes/page.scss';
 @import "../../../../themes/t.cell.scss";
 @import "../../report-common.scss";
+@import "../../../../themes/card.scss";
 /* module/health/pages/report/report.wxss */
 
-.report-data-wrapper {
-  font-size: 14px;
+.card-body.report-wrapper {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+
+  .content-wrapper {
+    flex: auto;
+  }
+
+  .image-wrapper {
+    flex: none;
+    width: 128px;
+    height: 128px;
+    margin-left: 8px;
+  }
+}
+
+.talbel-wrapper {
+  $border: 1px solid#999;
+  margin-top: 12px;
+  border: $border;
+  border-radius: 8px;
 
-  .card-body {
+  .talbel-row {
     display: flex;
     flex-direction: row;
     align-items: center;
 
-    .content-wrapper {
-      flex: auto;
+    &+.talbel-row {
+      border-top: $border;
     }
 
-    .image-wrapper {
-      flex: none;
-      width: 128px;
-      height: 128px;
-      margin-left: 8px;
+    &.title {
+      font-weight: 600;
     }
-  }
 
-  .title {
-    text-align: center;
-  }
+    .label {
+      flex: none;
+      padding: 4px;
+      width: 100px;
+    }
 
-  .primary {
-    color: #34A76B;
+    .value {
+      flex: 2 2 50%;
+      min-height: 28px;
+      padding: 4px;
+      border-left: $border;
+      box-sizing: border-box;
+    }
   }
-
-  .talbel-wrapper {
-    $border: 1px solid#999;
-    margin-top: 12px;
-    border: $border;
-    border-radius: 8px;
-
-    .talbel-row {
-      display: flex;
-      flex-direction: row;
-      align-items: center;
-
-      &+.talbel-row {
-        border-top: $border;
-      }
-
-      .label {
-        flex: none;
-        padding: 4px;
-        width: 100px;
-      }
-
-      .value {
-        float: auto;
-        padding: 4px;
-        border-left: $border;
-      }
+  &.t-2 {
+    .value {
+      border-left: none;
+    }
+    .v-1 {
+      border-left: $border;
+      border-right: $border;
     }
   }
 }

+ 24 - 2
miniprogram/module/health/pages/report/report.ts

@@ -23,11 +23,27 @@ Component({
     dataset: null,
     schemeId: '',
     healthIndex: { data: [], loading: false, message: '' },
+    tongueAnalysis: [] as AnyArray,
   },
   observers: {
     'dataset'(data) {
       const has = data?.isHaveConditioningProgram === 'Y' && data?.isConfirmConditioningProgram === 'Y';
-      this.setData({ schemeId: has ? this.data.id : '' })
+
+      const keys = [
+        'tongueColor', 'tongueCoatingColor',
+        'tongueShape', 'tongueCoating',
+        'bodyFluid', 'sublingualVein'
+      ]
+      const tongueAnalysis = [] as AnyObject[];
+      for (const key of keys) {
+        const actualList = data?.[key]?.['actualList'] ?? []
+        tongueAnalysis.push(...actualList.filter((item: AnyObject) => item.contrast !== 's'))
+      }
+
+      this.setData({
+        schemeId: has ? this.data.id : '',
+        tongueAnalysis
+      })
     }
   },
   methods: {
@@ -57,6 +73,12 @@ Component({
         });
       }
     },
-    toSchemePage() { toSchemePage(this.data.schemeId); }
+    toSchemePage() { toSchemePage(this.data.schemeId); },
+    toTongueAnalysisResult() {
+      wx.navigateTo({ url: `/module/health/pages/tongue-analysis/tongue-analysis` })
+        .then(res => {
+          res.eventChannel.emit('load', { dataset: this.data.tongueAnalysis });
+        });
+    }
   }
 })

+ 114 - 19
miniprogram/module/health/pages/report/report.wxml

@@ -3,7 +3,7 @@
 
 <scroll-view class="page-scroll__container" type="list" scroll-y style="{{containerStyle}}">
   <view class="card-wrapper">
-    <t-cell t-class="card-header {{schemeId ? '' : 'cell-border-gradient'}}" bordered="{{!schemeId}}">
+    <t-cell t-class="card-header no-body {{schemeId ? '' : 'cell-border-gradient'}}" bordered="{{!schemeId}}">
       <view slot="title">报告日期:{{dataset.reportTime}}</view>
       <form-button slot="right-icon" index="1" bind:tap="toSchemePage">调理方案</form-button>
     </t-cell>
@@ -13,8 +13,9 @@
       <text>请找医生获取中医调理方案</text>
     </view>
   </view>
-  <view class="card-wrapper report-data-wrapper">
-    <view class="card-body">
+
+  <view class="card-wrapper">
+    <view class="card-body report-wrapper">
       <view class="content-wrapper">
         <block wx:if="dataset">
           <span class="row">
@@ -25,10 +26,10 @@
             <text>程度:</text>
             <text>{{dataset.willillDegreeName}}</text>
           </span>
-          <span class="row">
+          <!-- <span class="row">
             <text>类型:</text>
             <text>{{dataset.willillSocialName}}</text>
-          </span>
+          </span> -->
           <span class="row">
             <text>表现:</text>
             <text>{{dataset.willillFunctionName}}</text>
@@ -42,7 +43,8 @@
       <image class="image-wrapper" src="../../assets/image/health-report.png" mode="aspectFill" />
     </view>
   </view>
-  <view class="card-wrapper report-data-wrapper">
+
+  <view class="card-wrapper">
     <view class="card-body">
       <view class="content-wrapper">
         <block wx:if="dataset">
@@ -61,6 +63,10 @@
             <view class="label">形体特征</view>
             <view class="value">{{dataset.constitutionGroupPhysicalCharacteristics}}</view>
           </view>
+          <view class="talbel-row">
+            <view class="label">精神特征</view>
+            <view class="value">{{dataset.constitutionGroupPsychicCharacteristics}}</view>
+          </view>
           <view class="talbel-row">
             <view class="label">常见表现</view>
             <view class="value">{{dataset.constitutionGroupCommonManifestations}}</view>
@@ -70,8 +76,8 @@
             <view class="value">{{dataset.constitutionGroupDiseaseTendency}}</view>
           </view>
           <view class="talbel-row">
-            <view class="label">外界适应能力</view>
-            <view class="value">{{dataset.constitutionGroupPsychicCharacteristics}}</view>
+            <view class="label">环境适应能力</view>
+            <view class="value">{{dataset.constitutionGroupAdaptability}}</view>
           </view>
         </view>
       </view>
@@ -79,31 +85,120 @@
   </view>
 
   <view class="card-wrapper">
-    <t-cell t-class="card-header cell-border-gradient" title="舌象分析" description="{{dataset.tongueAnalysisResult}}" />
-    <view class="picture-wrapper">
-      <image src="{{dataset.upImg}}" mode="aspectFit" />
-      <image src="{{dataset.downImg}}" mode="aspectFit" />
+    <t-cell t-class="card-header cell-border-gradient" title="舌象分析">
+      <block wx:if="{{tongueAnalysis.length}}">
+        <view class="extra-warapper" slot="right-icon" catch:tap="toTongueAnalysisResult">
+          <text>详情</text>
+          <t-icon t-class="icon" name="chevron-right-double-s" size="24px" />
+        </view>
+      </block>
+    </t-cell>
+    <view class="card-body">
+      <!-- <view>&emsp;&emsp;{{dataset.tongueAnalysisResult}}</view> -->
+      <view class="picture-wrapper">
+        <image src="{{dataset.upImg}}" mode="aspectFit" />
+        <image src="{{dataset.downImg}}" mode="aspectFit" />
+      </view>
+
+      <view class="talbel-wrapper t-2">
+        <view class="talbel-row title">
+          <view class="label">舌象维度</view>
+          <view class="value v-1">检测结果</view>
+          <view class="value">标准值</view>
+        </view>
+        <view class="talbel-row">
+          <view class="label">舌色</view>
+          <view class="value v-1">
+            <span style="margin: 0 2px;" wx:for="{{dataset.tongueColor.actualList}}" wx:key="actualValue">
+              <text>{{item.actualValue}}</text>
+              <text wx:if="{{item.contrast !== 's'}}">({{item.contrast}})</text>
+            </span>
+          </view>
+          <view class="value">{{dataset.tongueColor.standardValue}}</view>
+        </view>
+        <view class="talbel-row">
+          <view class="label">苔色</view>
+          <view class="value v-1">
+            <span style="margin: 0 2px;" wx:for="{{dataset.tongueCoatingColor.actualList}}" wx:key="actualValue">
+              <text>{{item.actualValue}}</text>
+              <text wx:if="{{item.contrast !== 's'}}">({{item.contrast}})</text>
+            </span>
+          </view>
+          <view class="value">{{dataset.tongueCoatingColor.standardValue}}</view>
+        </view>
+        <view class="talbel-row">
+          <view class="label">舌形</view>
+          <view class="value v-1">
+            <span style="margin: 0 2px;" wx:for="{{dataset.tongueShape.actualList}}" wx:key="actualValue">
+              <text>{{item.actualValue}}</text>
+              <text wx:if="{{item.contrast !== 's'}}">({{item.contrast}})</text>
+            </span>
+          </view>
+          <view class="value">{{dataset.tongueShape.standardValue}}</view>
+        </view>
+        <view class="talbel-row">
+          <view class="label">苔质</view>
+          <view class="value v-1">
+            <span style="margin: 0 2px;" wx:for="{{dataset.tongueCoating.actualList}}" wx:key="actualValue">
+              <text>{{item.actualValue}}</text>
+              <text wx:if="{{item.contrast !== 's'}}">({{item.contrast}})</text>
+            </span>
+          </view>
+          <view class="value">{{dataset.tongueCoating.standardValue}}</view>
+        </view>
+        <view class="talbel-row">
+          <view class="label">津液</view>
+          <view class="value v-1">
+            <span style="margin: 0 2px;" wx:for="{{dataset.bodyFluid.actualList}}" wx:key="actualValue">
+              <text>{{item.actualValue}}</text>
+              <text wx:if="{{item.contrast !== 's'}}">({{item.contrast}})</text>
+            </span>
+          </view>
+          <view class="value">{{dataset.bodyFluid.standardValue}}</view>
+        </view>
+        <view class="talbel-row">
+          <view class="label">舌下</view>
+          <view class="value v-1">
+            <span style="margin: 0 2px;" wx:for="{{dataset.sublingualVein.actualList}}" wx:key="actualValue">
+              <text>{{item.actualValue}}</text>
+              <text wx:if="{{item.contrast !== 's'}}">({{item.contrast}})</text>
+            </span>
+          </view>
+          <view class="value">{{dataset.sublingualVein.standardValue}}</view>
+        </view>
+      </view>
     </view>
   </view>
-  <view class="card-wrapper">
-    <t-cell t-class="card-header cell-border-gradient" title="面象分析" description="{{dataset.faceAnalysisResult}}" />
-    <view class="picture-wrapper">
-      <image src="{{dataset.faceImg}}" mode="aspectFit" />
+  <view class="card-wrapper" wx:if="{{dataset.faceAnalysisResult || dataset.faceImg}}">
+    <t-cell t-class="card-header cell-border-gradient" title="面象分析" />
+    <view class="card-body">
+      <view>&emsp;&emsp;{{dataset.faceAnalysisResult}}</view>
+      <view class="picture-wrapper" wx:if="{{dataset.faceImg}}">
+        <image src="{{dataset.faceImg}}" mode="aspectFit" />
+      </view>
     </view>
   </view>
   <view class="card-wrapper">
     <t-cell t-class="card-header cell-border-gradient" title="中医证素" />
+    <!-- <view class="card-body">{{dataset.factorItemSummary}}</view> -->
     <view class="card-body">
-      {{dataset.factorItemSummary}}
+      <view class="row" wx:for="{{dataset.factorItems}}" wx:key="*this">
+        <view class="primary">{{item.factorItemName}}</view>
+        <view style="margin-top: 4px;" wx:if="{{item.factorItemDescription}}">&emsp;&emsp;{{item.factorItemDescription}}</view>
+      </view>
     </view>
   </view>
   <view class="card-wrapper">
     <t-cell t-class="card-header cell-border-gradient" title="中医证型" />
+    <!-- <view class="card-body">{{dataset.diagnoseSyndromeSummary}}</view> -->
     <view class="card-body">
-      {{dataset.diagnoseSyndromeSummary}}
+      <view class="row" wx:for="{{dataset.diagnoseSyndromes}}" wx:key="*this">
+        <view class="primary">{{item.diagnoseSyndromeName}}</view>
+        <view style="margin-top: 4px;" wx:if="{{item.diagnoseSyndromeAnalysis}}">&emsp;&emsp;{{item.diagnoseSyndromeAnalysis}}</view>
+      </view>
     </view>
   </view>
-  
+
   <report-health-index dataset="{{healthIndex.data}}" loading="{{healthIndex.loading}}" message="{{healthIndex.message}}">
   </report-health-index>
 </scroll-view>

+ 2 - 1
miniprogram/module/health/pages/scheme/scheme.json

@@ -7,6 +7,7 @@
     "t-icon": "tdesign-miniprogram/icon/icon",
     "report-health-index": "../../components/report-health-index/report-health-index",
     "report-health-status": "../../components/report-health-status/report-health-status",
-    "form-button": "../../../../components/button/button"
+    "form-button": "../../../../components/button/button",
+    "horizontal-scrollable": "../../components/horizontal-scrollable/horizontal-scrollable"
   }
 }

+ 38 - 0
miniprogram/module/health/pages/scheme/scheme.scss

@@ -1,11 +1,49 @@
 @import '../../../../themes/page.scss';
 @import "../../../../themes/t.cell.scss";
 @import "../../report-common.scss";
+@import "../../../../themes/card.scss";
+
 /* module/health/pages/scheme/scheme.wxss */
 .card-wrapper {
   --button-line-1: 10px;
 }
+
+.scheme {
+  &__item {
+    margin-top: 8px;
+
+    &-title {
+      font-size: 14px;
+      text-align: center;
+      margin-bottom: 8px;
+    }
+  }
+
+  &__title {
+    font-size: 14px;
+    font-weight: 600;
+    color: #34A76B;
+  }
+
+  &__description {
+    display: flex;
+    flex-direction: row;
+    flex-wrap: wrap;
+    margin: 6px 0;
+  }
+}
+
+
+
 .scheme-wrapper {
+  image {
+    height: 90px;
+  }
+
+
+
+
+
   &+.scheme-wrapper {
     margin-top: 12px;
   }

+ 2 - 0
miniprogram/module/health/pages/scheme/scheme.ts

@@ -28,6 +28,8 @@ Component({
       wx.showLoading({ title: '加载中' });
       try {
         const dataset = await healthSchemeMethod(id);
+        console.log(dataset, '1-->');
+        
         this.setData({ dataset });
       } catch (error) {
         console.log(error);

+ 13 - 5
miniprogram/module/health/pages/scheme/scheme.wxml

@@ -3,18 +3,26 @@
 
 <scroll-view class="page-scroll__container" type="list" scroll-y style="{{containerStyle}}">
   <view class="card-wrapper">
-    <t-cell t-class="card-header" bordered="{{false}}">
+    <t-cell t-class="card-header no-body" bordered="{{false}}">
       <view slot="title">方案日期:{{dataset.reportTime}}</view>
       <form-button slot="right-icon" index="1" bind:tap="toReportPage">健康分析报告</form-button>
     </t-cell>
   </view>
 
 
-  <view class="card-wrapper" wx:for="{{dataset.types}}" wx:key="*this">
-    <t-cell wx:if="{{item.type}}" t-class="card-header cell-border-gradient" title="{{item.type}}" />
+  <view class="card-wrapper" wx:for="{{dataset.children}}" wx:key="title">
+    <t-cell t-class="card-header cell-border-gradient" title="{{item.title}}" />
     <view class="card-body">
-      <view class="scheme-wrapper" wx:for="{{item.groups}}" wx:key="*this">
-        <view class="name">{{item.name}}</view>
+      <view class="scheme__item" wx:for="{{item.children}}" wx:key="title">
+        <view class="scheme__item-title scheme__title" wx:if="{{item.title}}">{{item.title}}</view>
+        <horizontal-scrollable dataset="{{item.children}}"></horizontal-scrollable>
+        <view class="scheme__description" wx:if="{{item.description}}">{{item.description}}</view>
+        <span class="scheme__description" wx:for="{{item.descriptions}}" wx:key="id">
+          <text class="scheme__title">【{{item.name}}】</text>
+          <text>{{item.description}}</text>
+        </span>
+      </view>
+      <view class="scheme-wrapper">
         <view class="media-container">
           <view class="media" wx:for="{{item.media}}" wx:key="*this">
             <view class="name">{{item.name}}</view>

+ 5 - 0
miniprogram/module/health/pages/status-record/status-record.json

@@ -0,0 +1,5 @@
+{
+  "renderer": "skyline",
+  "component": true,
+  "usingComponents": {}
+}

+ 56 - 0
miniprogram/module/health/pages/status-record/status-record.scss

@@ -0,0 +1,56 @@
+@import '../../../../themes/page.scss';
+/* module/health/pages/status-record/status-record.wxss */
+
+.wrapper {
+  display: flex;
+  $border: 1px solid #34FFC6;
+
+
+  &.portrait {
+    flex-direction: row;
+    flex-wrap: nowrap;
+    border-top: $border;
+    border-bottom: $border;
+
+    .header {
+      flex: none;
+      border-right: $border;
+
+      .row {
+        width: 90px;
+      }
+    }
+
+    .scrollable {
+      flex: auto;
+    }
+
+    .row {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      width: 120px;
+      padding: 4px;
+      box-sizing: border-box;
+
+      &.odd {
+        background-color: #113B38;
+      }
+    }
+    .col {
+      font-size: 14px;
+    }
+    .col+.col {
+      border-left: $border;
+    }
+  }
+}
+
+.highlight {
+  color: #FD3F3F;
+}
+
+image {
+  width: 100%;
+  height: 100%;
+}

+ 87 - 0
miniprogram/module/health/pages/status-record/status-record.ts

@@ -0,0 +1,87 @@
+import tickleBehavior, { getTickleContext } from "../../../../core/behavior/tickle.behavior";
+// module/health/pages/status-record/status-record.ts
+import { getStatusRecordMethod } from "../../request";
+import { getPageOrientation } from "../../../../lib/wx/page";
+
+interface Column {
+  label: string;
+  value: string;
+  width?: number;
+  height?: number;
+  type?: 'picture' | 'text';
+}
+const defaultColumns: Column[] = [
+  { label: '', value: 'reportTime', height: 50 },
+  { label: '症状', value: 'pickedSymptom' },
+  { label: '联想症状', value: 'algorithmInferSymptom' },
+  { label: '舌面', value: 'upImg', type: 'picture', width: 120, height: 120 },
+  { label: '舌底', value: 'downImg', type: 'picture', width: 120, height: 120 },
+  { label: '面部', value: 'faceImg', type: 'picture', width: 120, height: 120 },
+]
+
+Component({
+  behaviors: [
+    tickleBehavior,
+  ],
+  lifetimes: {
+    attached() { this.load(); }
+  },
+  properties: {},
+  data: {
+    orientation: '' as 'portrait' | 'landscape',
+    containerStyle: '',
+    columns: [] as Column[],
+  },
+  methods: {
+    async getColumns() {
+      const { orientation, width, height, left = 0, right = 0, } = getPageOrientation();
+
+      const columns = defaultColumns;
+      let maxLines = 3;
+
+      if (orientation === 'portrait') {
+        const { defaultHeight, count } = columns.reduce((obj, column) => {
+          if (column.height) {
+            obj.defaultHeight += column.height;
+            obj.count += 1;
+          }
+          return obj;
+        }, { defaultHeight: 0, count: 0 })
+        const length = columns.length - count;
+        const rowHeight = Math.floor((height - defaultHeight) / length);
+        for (const column of columns) { if (column.height == null) column.height = rowHeight; }
+
+        maxLines = Math.floor(rowHeight / 22);
+      }
+
+      this.setData({
+        columns,
+        orientation,
+        containerStyle: `width: ${width}px;height: ${height}px;margin-left: ${left}px;margin-right: ${right}px;`,
+
+        maxLines,
+      })
+    },
+    async load() {
+      wx.showLoading({ title: '加载中' });
+      try {
+        await this.getColumns();
+        const { data } = await getStatusRecordMethod();
+        this.setData({ dataset: data });
+      } catch (error) {
+        getTickleContext.call(this).showErrorMessage(error.errMsg)
+      }
+      wx.hideLoading();
+    },
+    loadMore() {
+      console.log('加载更多');
+
+    },
+    preview(event: WechatMiniprogram.TouchEvent) {
+      const { url, item } = event.target.dataset
+      const sources = [item.upImg, item.downImg, item.faceImg].filter(Boolean);
+      const current = sources.findIndex(s => s === url);
+      if (current !== -1) wx.previewMedia({ sources: sources.map(url => ({ url, type: 'image' })), current });
+    }
+  }
+})

+ 27 - 0
miniprogram/module/health/pages/status-record/status-record.wxml

@@ -0,0 +1,27 @@
+<wxs module="_">
+  module.exports.getClassName = function (value, prev, key) {
+    // return key !== 'reportTime' && prev && value[key] !== prev[key] ? 'highlight' : ''
+    return '';
+  }
+</wxs>
+<!--module/health/pages/status-record/status-record.wxml-->
+<t-navbar title="健康状况更新记录" left-arrow />
+<view class="wrapper {{orientation}}" style="{{containerStyle}}">
+  <view class="header">
+    <block wx:for="{{columns}}" wx:key="value" wx:for-index="i" wx:for-item="r">
+      <view class="row {{i === 0 ? 'title' : i % 2 ? 'odd' : 'even'}}" style="height: {{r.height}}px;">{{r.label}}</view>
+    </block>
+  </view>
+  <scroll-view class="scrollable" type="list" scroll-x enable-flex style="flex-direction: row;" bind:scrolltolower="loadMore">
+    <view class="col" wx:for="{{dataset}}" wx:key="index">
+      <block wx:for="{{columns}}" wx:key="value" wx:for-index="i" wx:for-item="r">
+        <image wx:if="{{r.type==='picture'}}" style="height: {{r.height}}px;width: {{r.width}}px;" src="{{item[r.value]}}" mode="aspectFit" data-url="{{item[r.value]}}" data-item="{{item}}" catch:tap="preview"></image>
+        <view wx:else class="row {{i === 0 ? 'title' : i % 2 ? 'odd' : 'even'}} {{_.getClassName(item, dataset[index+1], r.value)}}" style="height: {{r.height}}px;">
+          <text max-lines="{{maxLines}}" overflow="ellipsis">{{item[r.value]}}</text>
+        </view>
+      </block>
+    </view>
+  </scroll-view>
+</view>
+
+<t-message id="{{$messageId}}" />

+ 2 - 5
miniprogram/module/health/pages/status/status.ts

@@ -23,14 +23,11 @@ Component({
       wx.showLoading({ title: '加载中' });
       try {
         const health = await Post<ResponseData, App.Health.Index.Data[]>(`/patientQuota/getCurQuoval`, {}, {
-          transform({ data }) { console.log(data,'12300-->');
+          transform({ data }) {
            return transformHealthIndex2Ruler(data) }
         });
         this.setData({ health })
       } catch (error) {
-      console.log(error, '123-->');
-      
-        
         getTickleContext.call(this).showErrorMessage(error.errMsg, 0)
       }
       wx.hideLoading()
@@ -41,7 +38,7 @@ Component({
       const model = Object
         .entries(values)
         .filter(([, value]) => value)
-        .map(([quotaId, quotaVal]) => ({ quotaId: quotaId, quotaVal: +quotaVal }))
+        .map(([quotaId, quotaVal]) => ({ quotaId, quotaVal }))
 
       if (model.length) {
         wx.showLoading({ title: `提交中` })

+ 1 - 1
miniprogram/module/health/pages/status/status.wxml

@@ -6,7 +6,7 @@
       <field-ruler class="cell-field__inner full" slot="note" title="{{item.name}}" options="{{item.options}}"></field-ruler>
     </t-cell>
     <block wx:if="{{health.length}}">
-      <view class="tips">温馨提示:指标信息越完善,我们分析的越精</view>
+      <view class="tips">温馨提示:指标信息越完善,我们分析的越精</view>
       <form-button block index="2"></form-button>
     </block>
   </form>

+ 8 - 0
miniprogram/module/health/pages/tongue-analysis/tongue-analysis.json

@@ -0,0 +1,8 @@
+{
+  "renderer": "skyline",
+  "component": true,
+  "usingComponents": {
+    "t-cell": "tdesign-miniprogram/cell/cell",
+    "t-tag": "tdesign-miniprogram/tag/tag"
+  }
+}

+ 16 - 0
miniprogram/module/health/pages/tongue-analysis/tongue-analysis.scss

@@ -0,0 +1,16 @@
+@import '../../../../themes/page.scss';
+@import "../../../../themes/t.cell.scss";
+@import "../../../../themes/card.scss";
+
+/* module/health/pages/tongue-analysis/tongue-analysis.wxss */
+.picture-wrapper {
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+
+  image {
+    margin: 12px;
+    width: 128px;
+    height: 128px;
+  }
+}

+ 17 - 0
miniprogram/module/health/pages/tongue-analysis/tongue-analysis.ts

@@ -0,0 +1,17 @@
+import PageContainerBehavior from "../../../../core/behavior/page-container.behavior";
+// module/health/pages/tongue-analysis/tongue-analysis.ts
+Component({
+  behaviors: [PageContainerBehavior],
+  lifetimes: {
+    attached() {
+      this.getOpenerEventChannel().on('load', (data) => {
+        this.setData(data);
+      })
+    }
+  },
+  properties: {},
+  data: {
+    dataset: [] as AnyArray,
+  },
+  methods: {}
+})

+ 24 - 0
miniprogram/module/health/pages/tongue-analysis/tongue-analysis.wxml

@@ -0,0 +1,24 @@
+<!--module/health/pages/tongue-analysis/tongue-analysis.wxml-->
+<t-navbar title="异常舌象分析" left-arrow />
+
+<scroll-view class="page-scroll__container" type="list" scroll-y style="{{containerStyle}}">
+  <view class="card-wrapper" wx:for="{{dataset}}" wx:key="actualValue">
+    <t-cell t-class="card-header cell-border-gradient" title="{{item.actualValue}}" />
+    <view class="card-body">
+      <view class="picture-wrapper">
+        <image wx:if="{{item.splitImage}}" src="{{item.splitImage}}" mode="aspectFit" />
+        <view style="display: flex;flex-direction: column;">
+          <t-tag style="margin: 4px 0;" wx:for="{{item.attrs}}" wx:key="*this" theme="danger">{{item}}</t-tag>
+        </view>
+      </view>
+      <span class="row" wx:if="{{item.features}}">
+        <text class="label primary">【特征】</text>
+        <text>{{item.features}}</text>
+      </span>
+      <span class="row" wx:if="{{item.clinicalSignificance}}">
+        <text class="primary">【临床意义】</text>
+        <text>{{item.clinicalSignificance}}</text>
+      </span>
+    </view>
+  </view>
+</scroll-view>

+ 70 - 48
miniprogram/module/health/request.ts

@@ -40,7 +40,7 @@ export function healthIndexMethod(id?: string) {
   const transform = ({ data }: AnyObject) => Array.isArray(data) ? data.map(item => createHealthIndex(item)) : [];
 
   return id
-    ? Get(`/analysisManage/getQuovalByHarId`, { params: { healthAnalysisReportId: id }, transform })
+    ? Get(`/analysisManage/getLast7Day`, { params: { healthAnalysisReportId: id }, transform })
     : Post(`/patientQuota/getCurQuoval`, {}, { transform })
 }
 
@@ -48,61 +48,83 @@ export function healthSchemeMethod(id: string) {
   const transform = ({ data }: AnyObject) => {
     return {
       reportTime: dayjs(data?.time).format('YYYY年MM月DD日'),
-      types: Array.isArray(data?.types) ? data.types.map((item: AnyObject) => {
+      children: data?.types?.map((item: AnyObject, i: number) => {
         return {
-          type: item.type || '',
-          summary: item.summary?.replace(/null/g, '') || '',
-          groups: item.groups?.map((group: AnyObject) => {
-            const descriptions = group.description?.split('\n').filter(Boolean) ?? [];
-            const media = [] as any[];
-            const items = group.items;
-            if (Array.isArray(items)) {
-              let medicine = [] as any[];
-              const fn = () => {
-                const str = medicine.map(item => [item.name, [item.doase, item.unit].filter(Boolean).join('')].filter(Boolean).join(' ')).join('; ')
-                descriptions.push(`${str}`)
-                medicine = [];
-              }
-              for (const item of items) {
-                const { type, imgUrl, mediaUrl, name, description } = item;
-                if (type !== 'medicine' && medicine.length) fn();
-                switch (type) {
+          title: item?.type || '',
+          children: item?.groups?.map((item: AnyObject, j: number) => {
+            return {
+              title: item?.name || '',
+              descriptions: item?.attrs?.map((attr: AnyArray, k: number) => {
+                return {...attr, id: `description-${i}-${j}-${k}`,}
+              }) ?? [], 
+              children: item?.items?.map((item: AnyObject, k: number) => {
+                switch (item?.type) {
                   case 'img':
-                    if (mediaUrl || imgUrl) media.push({
-                      type: 'picture',
-                      mediaUrl: mediaUrl ?? imgUrl,
-                      imgUrl: imgUrl ?? mediaUrl,
-                      name: name ?? '',
-                      description: description ?? ''
-                    })
-                    break;
+                    return {
+                      id: `${item?.type}-${i}-${j}-${k}`,
+                      type: item.imgUrl ? 'image' : null,
+                      poster: item.imgUrl,
+                      url: item.mediaUrl ?? item.imgUrl,
+                      title: item.name,
+                      description: item.description,
+                    }
                   case 'video':
-                    if (mediaUrl) media.push({
-                      type: 'video',
-                      mediaUrl: mediaUrl,
-                      imgUrl: imgUrl,
-                      name: name ?? '',
-                      description: description ?? ''
-                    });
-                    break;
-                  case 'text':
-                    descriptions.push(`${name}: ${description}`)
-                    break;
+                    return {
+                      id: `${item?.type}-${i}-${j}-${k}`,
+                      type: item.mediaUrl ? 'video' : null,
+                      poster: item.imgUrl,
+                      url: item.mediaUrl,
+                      title: item.name,
+                      description: item.description,
+                    }
+                  case 'acupoint':
+                    return {
+                      id: `${item?.type}-${i}-${j}-${k}`,
+                      type: item.imgUrl ? 'image' : item.name ? 'text' : null,
+                      poster: item.imgUrl,
+                      url: item.mediaUrl ?? item.imgUrl,
+                      title: [item.name, `${item.doase || ''}${item.unit || ''}`].filter(Boolean).join(' '),
+                      description: item.description,
+                    }
                   case 'medicine':
-                    medicine.push(item);
-                    break;
+                    return {
+                      id: `${item?.type}-${i}-${j}-${k}`,
+                      type: item.imgUrl ? 'image' : item.name ? 'text' : null,
+                      poster: item.imgUrl,
+                      url: item.mediaUrl ?? item.imgUrl,
+                      title: [item.name, `${item.doase || ''}${item.unit || ''}`].filter(Boolean).join(' '),
+                      description: item.description,
+                    }
+                  default:
+                    return {
+                      id: `${item?.type}-${i}-${j}-${k}`,
+                      type: null
+                    }
                 }
-              }
-              if (medicine.length) fn();
-            }
-            return {
-              name: group.name,
-              descriptions, media,
+              }).filter((item: AnyObject) => item.type) ?? [],
+              description: item?.description,
             }
-          })
+          }) ?? [],
         }
-      }) : []
+      }) ?? []
     }
   };
   return Get(`/analysisManage/getCondProgDetailById`, { params: { healthAnalysisReportId: id }, transform })
+}
+
+
+export function getStatusRecordMethod() {
+  return Post(`/analysisManage/pageHarStatu`, {}, {
+    transform({ data }: AnyObject) {
+      return {
+        total: data.total,
+        data: data.data?.map((item: AnyObject) => {
+          return {
+            reportTime: dayjs(item.analysisEndTime).format('YY/MM/DD'),
+            ...item
+          }
+        })
+      };
+    }
+  })
 }

+ 13 - 6
miniprogram/module/health/tools/health-index.ts

@@ -46,10 +46,13 @@ export function healthIndex2Progress(model: App.Health.Index.Model[]): AnyObject
   return model.filter((item) => item.values?.length).map(item => {
     const { scope, range, values } = item;
     const { value, abnormal, description = '' } = values?.slice(-1)[0]!
+    // 修订 range[1] 值
+    range[1] = Math.min(range[1], Math.floor(Math.max(value, scope[1]) * 1.3));
+
     const length = range[1] - range[0];
 
-    const scopeOffsetLeft = `${Math.floor((scope[0] - range[0]) * 100 / length)}%`;
-    const scopeOffsetRight = `${100 - Math.floor((scope[1] - range[0]) * 100 / length)}%`;
+    const scopeOffsetLeft = `${(scope[0] - range[0]) * 100 / length}%`;
+    const scopeOffsetRight = `${100 - (scope[1] - range[0]) * 100 / length}%`;
 
     let valueOffset = Math.floor((value - range[0]) * 100 / length);
     let valueOffsetLeft = '0';
@@ -70,7 +73,7 @@ export function healthIndex2Progress(model: App.Health.Index.Model[]): AnyObject
       valueOffsetRight = scopeOffsetRight;
     }
 
-    return {
+    const a = {
       name: item.name, unit: item.unit,
       value, min: scope[0], max: scope[1],
       abnormal, description: description?.replace(item.name, ''),
@@ -78,6 +81,9 @@ export function healthIndex2Progress(model: App.Health.Index.Model[]): AnyObject
       valueType, valueOffset: `${valueOffset}%`,
       valueOffsetLeft, valueOffsetRight,
     }
+    console.log(a, '12345-->', length);
+    
+    return a;
   })
 }
 
@@ -87,16 +93,17 @@ export function createHealthIndex(data: App.Health.Index.Data): App.Health.Index
   const { quotaId: id, minVal, maxVal, inputMin, inputMax, inputPrecision, patientQuotaRecordDTOS, ...model } = data;
   const scope = [+minVal, +maxVal] as const;
   const range = [
-    +inputMin! || Math.floor(scope[0] * 0.75),
-    +inputMax! || Math.floor(scope[1] * 1.25),
+    +inputMin!/*! || Math.floor(scope[0] * 0.75)*/,
+    +inputMax!/*! || Math.floor(scope[1] * 1.25)*/,
   ]
   return {
     ...model, id,
     precision: ({ 0: 0.01, 1: 0.1, 2: 2, 3: 1 } as any)[inputPrecision ?? 3],
     range, scope,
     values: patientQuotaRecordDTOS?.map(item => {
-      return { value: +item.quotaVal, abnormal: item.abnormal, description: item.abnormalDesc, date: item.time2 }
+      return { value: item.quotaVal, abnormal: item.abnormal, description: item.abnormalDesc, date: item.time2 }
     })?.filter(item => !!item.value),
+    defaultValue: 12
   } as App.Health.Index.Model
 }
 

Some files were not shown because too many files changed in this diff