cc12458 1 год назад
Родитель
Сommit
c1845423a1

+ 8 - 2
miniprogram/app.json

@@ -18,8 +18,7 @@
         "pages/home/home",
         "pages/report/report",
         "pages/status/status",
-        "pages/scheme/scheme",
-        "pages/record-index/record-index"
+        "pages/scheme/scheme"
       ]
     },
     {
@@ -29,6 +28,13 @@
         "pages/user-certification/user-certification",
         "pages/user-edit/user-edit"
       ]
+    },
+    {
+      "name": "charts",
+      "root": "module/charts",
+      "pages": [
+        "record-index/record-index"
+      ]
     }
   ],
   "preloadRule": {

+ 284 - 0
miniprogram/module/charts/ec-canvas/ec-canvas.js

@@ -0,0 +1,284 @@
+import WxCanvas from './wx-canvas';
+import * as echarts from './echarts.min';
+
+let ctx;
+
+function compareVersion(v1, v2) {
+  v1 = v1.split('.')
+  v2 = v2.split('.')
+  const len = Math.max(v1.length, v2.length)
+
+  while (v1.length < len) {
+    v1.push('0')
+  }
+  while (v2.length < len) {
+    v2.push('0')
+  }
+
+  for (let i = 0; i < len; i++) {
+    const num1 = parseInt(v1[i])
+    const num2 = parseInt(v2[i])
+
+    if (num1 > num2) {
+      return 1
+    } else if (num1 < num2) {
+      return -1
+    }
+  }
+  return 0
+}
+
+Component({
+  properties: {
+    canvasId: {
+      type: String,
+      value: 'ec-canvas'
+    },
+
+    ec: {
+      type: Object
+    },
+
+    forceUseOldCanvas: {
+      type: Boolean,
+      value: false
+    }
+  },
+
+  data: {
+    isUseNewCanvas: false
+  },
+
+  ready: function () {
+    // Disable prograssive because drawImage doesn't support DOM as parameter
+    // See https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html
+    echarts.registerPreprocessor(option => {
+      if (option && option.series) {
+        if (option.series.length > 0) {
+          option.series.forEach(series => {
+            series.progressive = 0;
+          });
+        }
+        else if (typeof option.series === 'object') {
+          option.series.progressive = 0;
+        }
+      }
+    });
+
+    if (!this.data.ec) {
+      console.warn('组件需绑定 ec 变量,例:<ec-canvas id="mychart-dom-bar" '
+        + 'canvas-id="mychart-bar" ec="{{ ec }}"></ec-canvas>');
+      return;
+    }
+
+    if (!this.data.ec.lazyLoad) {
+      this.init();
+    }
+  },
+
+  methods: {
+    init: function (callback) {
+      const version = wx.getSystemInfoSync().SDKVersion
+
+      const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0;
+      const forceUseOldCanvas = this.data.forceUseOldCanvas;
+      const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas;
+      this.setData({ isUseNewCanvas });
+
+      if (forceUseOldCanvas && canUseNewCanvas) {
+        console.warn('开发者强制使用旧canvas,建议关闭');
+      }
+
+      if (isUseNewCanvas) {
+        // console.log('微信基础库版本大于2.9.0,开始使用<canvas type="2d"/>');
+        // 2.9.0 可以使用 <canvas type="2d"></canvas>
+        this.initByNewWay(callback);
+      } else {
+        const isValid = compareVersion(version, '1.9.91') >= 0
+        if (!isValid) {
+          console.error('微信基础库版本过低,需大于等于 1.9.91。'
+            + '参见:https://github.com/ecomfe/echarts-for-weixin'
+            + '#%E5%BE%AE%E4%BF%A1%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82');
+          return;
+        } else {
+          console.warn('建议将微信基础库调整大于等于2.9.0版本。升级后绘图将有更好性能');
+          this.initByOldWay(callback);
+        }
+      }
+    },
+
+    initByOldWay(callback) {
+      // 1.9.91 <= version < 2.9.0:原来的方式初始化
+      ctx = wx.createCanvasContext(this.data.canvasId, this);
+      const canvas = new WxCanvas(ctx, this.data.canvasId, false);
+
+      if (echarts.setPlatformAPI) {
+        echarts.setPlatformAPI({
+          createCanvas: () => canvas,
+        });
+      } else {
+        echarts.setCanvasCreator(() => canvas);
+      };
+      // const canvasDpr = wx.getSystemInfoSync().pixelRatio // 微信旧的canvas不能传入dpr
+      const canvasDpr = 1
+      var query = wx.createSelectorQuery().in(this);
+      query.select('.ec-canvas').boundingClientRect(res => {
+        if (typeof callback === 'function') {
+          this.chart = callback(canvas, res.width, res.height, canvasDpr);
+        }
+        else if (this.data.ec && typeof this.data.ec.onInit === 'function') {
+          this.chart = this.data.ec.onInit(canvas, res.width, res.height, canvasDpr);
+        }
+        else {
+          this.triggerEvent('init', {
+            canvas: canvas,
+            width: res.width,
+            height: res.height,
+            canvasDpr: canvasDpr // 增加了dpr,可方便外面echarts.init
+          });
+        }
+      }).exec();
+    },
+
+    initByNewWay(callback) {
+      // version >= 2.9.0:使用新的方式初始化
+      const query = wx.createSelectorQuery().in(this)
+      query
+        .select('.ec-canvas')
+        .fields({ node: true, size: true })
+        .exec(res => {
+          const canvasNode = res[0].node
+          this.canvasNode = canvasNode
+
+          const canvasDpr = wx.getSystemInfoSync().pixelRatio
+          const canvasWidth = res[0].width
+          const canvasHeight = res[0].height
+
+          const ctx = canvasNode.getContext('2d')
+
+          const canvas = new WxCanvas(ctx, this.data.canvasId, true, canvasNode)
+          if (echarts.setPlatformAPI) {
+            echarts.setPlatformAPI({
+              createCanvas: () => canvas,
+              loadImage: (src, onload, onerror) => {
+                if (canvasNode.createImage) {
+                  const image = canvasNode.createImage();
+                  image.onload = onload;
+                  image.onerror = onerror;
+                  image.src = src;
+                  return image;
+                }
+                console.error('加载图片依赖 `Canvas.createImage()` API,要求小程序基础库版本在 2.7.0 及以上。');
+                // PENDING fallback?
+              }
+            })
+          } else {
+            echarts.setCanvasCreator(() => canvas)
+          }
+
+          if (typeof callback === 'function') {
+            this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr)
+          } else if (this.data.ec && typeof this.data.ec.onInit === 'function') {
+            this.chart = this.data.ec.onInit(canvas, canvasWidth, canvasHeight, canvasDpr)
+          } else {
+            this.triggerEvent('init', {
+              canvas: canvas,
+              width: canvasWidth,
+              height: canvasHeight,
+              dpr: canvasDpr
+            })
+          }
+        })
+    },
+    canvasToTempFilePath(opt) {
+      if (this.data.isUseNewCanvas) {
+        // 新版
+        const query = wx.createSelectorQuery().in(this)
+        query
+          .select('.ec-canvas')
+          .fields({ node: true, size: true })
+          .exec(res => {
+            const canvasNode = res[0].node
+            opt.canvas = canvasNode
+            wx.canvasToTempFilePath(opt)
+          })
+      } else {
+        // 旧的
+        if (!opt.canvasId) {
+          opt.canvasId = this.data.canvasId;
+        }
+        ctx.draw(true, () => {
+          wx.canvasToTempFilePath(opt, this);
+        });
+      }
+    },
+
+    touchStart(e) {
+      if (this.chart && e.touches.length > 0) {
+        var touch = e.touches[0];
+        var handler = this.chart.getZr().handler;
+        handler.dispatch('mousedown', {
+          zrX: touch.x,
+          zrY: touch.y,
+          preventDefault: () => {},
+          stopImmediatePropagation: () => {},
+          stopPropagation: () => {}
+        });
+        handler.dispatch('mousemove', {
+          zrX: touch.x,
+          zrY: touch.y,
+          preventDefault: () => {},
+          stopImmediatePropagation: () => {},
+          stopPropagation: () => {}
+        });
+        handler.processGesture(wrapTouch(e), 'start');
+      }
+    },
+
+    touchMove(e) {
+      if (this.chart && e.touches.length > 0) {
+        var touch = e.touches[0];
+        var handler = this.chart.getZr().handler;
+        handler.dispatch('mousemove', {
+          zrX: touch.x,
+          zrY: touch.y,
+          preventDefault: () => {},
+          stopImmediatePropagation: () => {},
+          stopPropagation: () => {}
+        });
+        handler.processGesture(wrapTouch(e), 'change');
+      }
+    },
+
+    touchEnd(e) {
+      if (this.chart) {
+        const touch = e.changedTouches ? e.changedTouches[0] : {};
+        var handler = this.chart.getZr().handler;
+        handler.dispatch('mouseup', {
+          zrX: touch.x,
+          zrY: touch.y,
+          preventDefault: () => {},
+          stopImmediatePropagation: () => {},
+          stopPropagation: () => {}
+        });
+        handler.dispatch('click', {
+          zrX: touch.x,
+          zrY: touch.y,
+          preventDefault: () => {},
+          stopImmediatePropagation: () => {},
+          stopPropagation: () => {}
+        });
+        handler.processGesture(wrapTouch(e), 'end');
+      }
+    }
+  }
+});
+
+function wrapTouch(event) {
+  for (let i = 0; i < event.touches.length; ++i) {
+    const touch = event.touches[i];
+    touch.offsetX = touch.x;
+    touch.offsetY = touch.y;
+  }
+  return event;
+}

+ 5 - 0
miniprogram/module/charts/ec-canvas/ec-canvas.json

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

+ 4 - 0
miniprogram/module/charts/ec-canvas/ec-canvas.wxml

@@ -0,0 +1,4 @@
+<!-- 新的:接口对其了H5 -->
+<canvas wx:if="{{isUseNewCanvas}}" type="2d" class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas>
+<!-- 旧的 -->
+<canvas wx:else class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas>

+ 4 - 0
miniprogram/module/charts/ec-canvas/ec-canvas.wxss

@@ -0,0 +1,4 @@
+.ec-canvas {
+  width: 100%;
+  height: 100%;
+}

Разница между файлами не показана из-за своего большого размера
+ 34 - 0
miniprogram/module/charts/ec-canvas/echarts.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
miniprogram/module/charts/ec-canvas/echarts.min.js


+ 111 - 0
miniprogram/module/charts/ec-canvas/wx-canvas.js

@@ -0,0 +1,111 @@
+export default class WxCanvas {
+  constructor(ctx, canvasId, isNew, canvasNode) {
+    this.ctx = ctx;
+    this.canvasId = canvasId;
+    this.chart = null;
+    this.isNew = isNew
+    if (isNew) {
+      this.canvasNode = canvasNode;
+    }
+    else {
+      this._initStyle(ctx);
+    }
+
+    // this._initCanvas(zrender, ctx);
+
+    this._initEvent();
+  }
+
+  getContext(contextType) {
+    if (contextType === '2d') {
+      return this.ctx;
+    }
+  }
+
+  // canvasToTempFilePath(opt) {
+  //   if (!opt.canvasId) {
+  //     opt.canvasId = this.canvasId;
+  //   }
+  //   return wx.canvasToTempFilePath(opt, this);
+  // }
+
+  setChart(chart) {
+    this.chart = chart;
+  }
+
+  addEventListener() {
+    // noop
+  }
+
+  attachEvent() {
+    // noop
+  }
+
+  detachEvent() {
+    // noop
+  }
+
+  _initCanvas(zrender, ctx) {
+    zrender.util.getContext = function () {
+      return ctx;
+    };
+
+    zrender.util.$override('measureText', function (text, font) {
+      ctx.font = font || '12px sans-serif';
+      return ctx.measureText(text);
+    });
+  }
+
+  _initStyle(ctx) {
+    ctx.createRadialGradient = () => {
+      return ctx.createCircularGradient(arguments);
+    };
+  }
+
+  _initEvent() {
+    this.event = {};
+    const eventNames = [{
+      wxName: 'touchStart',
+      ecName: 'mousedown'
+    }, {
+      wxName: 'touchMove',
+      ecName: 'mousemove'
+    }, {
+      wxName: 'touchEnd',
+      ecName: 'mouseup'
+    }, {
+      wxName: 'touchEnd',
+      ecName: 'click'
+    }];
+    eventNames.forEach(name => {
+      this.event[name.wxName] = e => {
+        const touch = e.touches[0];
+        this.chart.getZr().handler.dispatch(name.ecName, {
+          zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
+          zrY: name.wxName === 'tap' ? touch.clientY : touch.y,
+          preventDefault: () => {},
+          stopImmediatePropagation: () => {},
+          stopPropagation: () => {}
+        });
+      };
+    });
+  }
+
+  set width(w) {
+    if (this.canvasNode) this.canvasNode.width = w
+  }
+  set height(h) {
+    if (this.canvasNode) this.canvasNode.height = h
+  }
+
+  get width() {
+    if (this.canvasNode)
+      return this.canvasNode.width
+    return 0
+  }
+  get height() {
+    if (this.canvasNode)
+      return this.canvasNode.height
+    return 0
+  }
+}

+ 163 - 0
miniprogram/module/charts/record-index/health-index.ts

@@ -0,0 +1,163 @@
+function gather(
+  data: App.Health.Index.Data[],
+  transform: (model: App.Health.Index.Model) => any,
+  skip = false,
+  link = { 血压: [1, 2], }
+) {
+  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;
+  })(link);
+
+  const cache = new Map<{ id: string; name: string; }, App.Health.Index.Ruler[]>();
+  for (const item of data) {
+    if (skip && 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(transform(model))
+    } else {
+      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;
+  const range = [
+    +inputMin! || Math.floor(scope[0] * 0.75),
+    +inputMax! || Math.floor(scope[1] * 1.25),
+  ]
+  return {
+    ...model, id,
+    precision: (+inputPrecision!) || 1,
+    range, scope,
+    values: patientQuotaRecordDTOS?.map(item => {
+      return { value: +item.quotaVal, abnormal: item.abnormal, description: item.abnormalDesc, date: item.time2 }
+    })?.filter(item => !!item.value),
+  } 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 {
+    name: model.name,
+    type: 'line', smooth: true,
+    data: model.values?.map(t => [t.date, t.value]),
+    markLine: {
+      data: model.scope.map(value => { return { yAxis: value } })
+    }
+  }
+}
+
+export function transformHealthIndex2Chart(data: App.Health.Index.Data[]): AnyObject[] {
+  const list = gather(data, healthIndex2Chart)
+  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)
+    if (!series.length) continue;
+    charts.push({
+      id: item.id,
+      title: { text: `${item.name}` },
+      tooltip: { trigger: 'axis' },
+      legend: { show: series.length > 1 },
+      xAxis, yAxis,
+      series,
+    })
+  }
+  return charts;
+}

+ 9 - 0
miniprogram/module/charts/record-index/record-index.json

@@ -0,0 +1,9 @@
+{
+  "renderer": "webview",
+  "navigationStyle": "default",
+  "navigationBarTitleText": "指标更新记录",
+  "component": true,
+  "usingComponents": {
+    "ec-canvas": "../ec-canvas/ec-canvas"
+  }
+}

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

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

+ 57 - 0
miniprogram/module/charts/record-index/record-index.ts

@@ -0,0 +1,57 @@
+import * as echarts from '../ec-canvas/echarts.min';
+import { healthIndexReportMethod } from './request';
+
+// module/health/pages/record-index/record-index.ts
+Component({
+  lifetimes: {
+    attached() {
+      this._initRect();
+      this._getData();
+    }
+  },
+  properties: {
+
+  },
+  data: {
+    gap: 0,
+    scale: 1,
+    rect: { width: 0, height: 0 },
+
+    charts: [] as string[],
+  },
+  methods: {
+    _initRect() {
+      const { windowWidth, pixelRatio } = wx.getWindowInfo()
+      const { right } = wx.getMenuButtonBoundingClientRect();
+      const gap = windowWidth - right;
+
+      const width = windowWidth - gap * 2;
+      const height = Math.floor(width * 3 / 4);
+      this.setData({
+        rect: { width, height, },
+        scale: pixelRatio, gap
+      })
+    },
+    async _getData() {
+      const charts = await healthIndexReportMethod();
+      this.setData({
+        charts: charts.map((option: any) => {
+          return {
+            id: option.id,
+            onInit(canvas: any, width: number, height: number, dpr: number) {
+              const ec = echarts.init(canvas, null, {
+                width: width,
+                height: height,
+                devicePixelRatio: dpr
+              });
+              canvas.setChart(ec);
+              ec.setOption(option);
+
+              return ec;
+            }
+          }
+        })
+      });
+    },
+  }
+})

+ 8 - 0
miniprogram/module/charts/record-index/record-index.wxml

@@ -0,0 +1,8 @@
+<!--module/health/pages/record-index/record-index.wxml-->
+<view style="padding: 0 {{gap}}px;">
+  <block wx:for="{{charts}}" wx:key="*this">
+    <view class="chart" style="width: {{rect.width}}px;height: {{rect.height}}px;">
+      <ec-canvas id="dom-{{item.id}}" canvas-id="canvas-{{item.id}}" ec="{{ item }}"></ec-canvas>
+    </view>
+  </block>
+</view>

+ 8 - 0
miniprogram/module/charts/record-index/request.ts

@@ -0,0 +1,8 @@
+import { Post } from "../../../lib/request/method"
+import { transformHealthIndex2Chart } from "./health-index";
+
+export function healthIndexReportMethod() {
+  return Post(`/patientQuota/getQuovalRecord`, {}, {
+    transform({ data }) { return transformHealthIndex2Chart(<any[]>data) }
+  })
+}

+ 0 - 6
miniprogram/module/health/request.ts

@@ -44,12 +44,6 @@ export function healthIndexMethod(id?: string) {
     : Post(`/patientQuota/getCurQuoval`, {}, { transform })
 }
 
-export function healthIndexReportMethod() {
-  return Post(`/patientQuota/getQuovalRecord`, {}, {
-    transform({ data }) { return transformHealthIndex2Chart(<any[]>data) }
-  })
-}
-
 export function healthSchemeMethod(id: string) {
   const transform = ({ data }: AnyObject) => {
     return {

+ 1 - 1
tsconfig.json

@@ -30,7 +30,7 @@
   },
   "include": [
     "./**/*.ts"
-, "miniprogram/module/health/components/ec-canvas/echarts.js", "miniprogram/module/health/components/ec-canvas/wx-canvas.js", "miniprogram/module/health/components/ec-canvas/ec-canvas.js"  ],
+, "miniprogram/module/charts/ec-canvas/echarts.js", "miniprogram/module/charts/ec-canvas/wx-canvas.js", "miniprogram/module/charts/ec-canvas/ec-canvas.js"  ],
   "exclude": [
     "node_modules"
   ]

Некоторые файлы не были показаны из-за большого количества измененных файлов