Bläddra i källkod

feat(chart): add useEcharts and useApexChart demo

vben 4 år sedan
förälder
incheckning
21d0ed92df

+ 4 - 4
README.md

@@ -68,7 +68,7 @@
 
 ### UI 框架
 
-- [Tailwind CSS](https://tailwindcss.com/)
+- [Tailwind CSS](https://tailwindcss.com/) - 2.0.0-beta.5 已删除
 - [Ant Design Vue 2.0](https://2x.antdv.com/docs/vue/introduce-cn/)
 
 ### 图标
@@ -215,11 +215,10 @@ yarn clean:lib # 删除node_modules,兼容window系统
 - [x] 树组件
 - [x] 图片预览组件
 - [x] 表格组件
+- [x] 图表库
 
 ## 正在开发的功能
 
-- [ ] 全局错误处理
-- [ ] 图表库
 - [ ] 数字动画
 - [ ] 主题配置
 - [ ] 富文本组件
@@ -227,6 +226,7 @@ yarn clean:lib # 删除node_modules,兼容window系统
 - [ ] 上传组件
 - [ ] 数据导入导出
 - [ ] 黑暗主题
+- [ ] 全局错误处理
 - [ ] 打包 Gzip
 - [ ] 抽取生产环境配置文件
 - [ ] 系统性能优化
@@ -235,7 +235,7 @@ yarn clean:lib # 删除node_modules,兼容window系统
 
 ## 加入我们
 
-`VUE-VBEN-ADMIN` 是完全开源免费的项目,旨在帮助开发者更方便地进行中大型管理系统开发,同时也提供 QQ 交流群(项目刚起步,人数较少,有兴趣的可以加群一起讨论),使用问题欢迎在群内提问。
+`Vue-Vben-Aadmin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供 QQ 交流群(项目刚起步,人数较少,有兴趣的可以加群一起讨论),使用问题欢迎在群内提问。
 
 - QQ 群 `569291866`
 

+ 3 - 0
package.json

@@ -22,7 +22,9 @@
   "dependencies": {
     "@iconify/iconify": "^2.0.0-rc.1",
     "ant-design-vue": "^2.0.0-beta.10",
+    "apexcharts": "^3.22.0",
     "axios": "^0.20.0",
+    "echarts": "^4.9.0",
     "lodash-es": "^4.17.15",
     "mockjs": "^1.1.0",
     "nprogress": "^0.2.0",
@@ -41,6 +43,7 @@
     "@iconify/json": "^1.1.233",
     "@ls-lint/ls-lint": "^1.9.2",
     "@purge-icons/generated": "^0.4.1",
+    "@types/echarts": "^4.8.0",
     "@types/fs-extra": "^9.0.1",
     "@types/inquirer": "^7.3.1",
     "@types/koa-static": "^4.0.1",

+ 37 - 0
src/hooks/web/useApexCharts.ts

@@ -0,0 +1,37 @@
+import { useTimeout } from '/@/hooks/core/useTimeout';
+import { tryOnUnmounted } from '/@/utils/helper/vueHelper';
+import { ref, unref, Ref, nextTick } from 'vue';
+
+import ApexCharts from 'apexcharts';
+
+export function useApexCharts(elRef: Ref<HTMLDivElement>) {
+  const chartInstanceRef = ref<Nullable<ApexCharts>>(null);
+
+  function setOptions(options: any) {
+    const el = unref(elRef);
+
+    if (!el || !unref(el)) {
+      return;
+    }
+    chartInstanceRef.value = new ApexCharts(el, options);
+
+    const chartInstance = unref(chartInstanceRef);
+
+    nextTick(() => {
+      useTimeout(() => {
+        chartInstance && chartInstance.render();
+      }, 30);
+    });
+  }
+
+  tryOnUnmounted(() => {
+    const chartInstance = unref(chartInstanceRef);
+    if (!chartInstance) {
+      return;
+    }
+    chartInstanceRef.value = null;
+  });
+  return {
+    setOptions,
+  };
+}

+ 79 - 0
src/hooks/web/useECharts.ts

@@ -0,0 +1,79 @@
+import { useTimeout } from '/@/hooks/core/useTimeout';
+import { tryOnUnmounted } from '/@/utils/helper/vueHelper';
+import { ref, unref, Ref, nextTick } from 'vue';
+import type { EChartOption, ECharts } from 'echarts';
+import echarts from 'echarts';
+import { useDebounce } from '/@/hooks/core/useDebounce';
+import { useEvent } from '/@/hooks/event/useEvent';
+import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
+
+export type { EChartOption, ECharts };
+export function useECharts(
+  elRef: Ref<HTMLDivElement>,
+  theme: 'light' | 'dark' | 'default' = 'light'
+) {
+  const chartInstanceRef = ref<Nullable<ECharts>>(null);
+  let resizeFn: Fn = resize;
+
+  const [debounceResize] = useDebounce(resize, 200);
+  resizeFn = debounceResize;
+
+  function init() {
+    const el = unref(elRef);
+
+    if (!el || !unref(el)) {
+      return;
+    }
+    chartInstanceRef.value = echarts.init(el, theme);
+    useEvent({
+      el: window,
+      name: 'resize',
+      listener: resizeFn,
+    });
+    const { widthRef, screenEnum } = useBreakpoint();
+    if (unref(widthRef) <= screenEnum.MD) {
+      useTimeout(() => {
+        resizeFn();
+      }, 0);
+    }
+  }
+
+  function setOptions(options: any, clear = true) {
+    // function setOptions(options: EChartOption, clear = true) {
+    let chartInstance = unref(chartInstanceRef);
+
+    if (!chartInstance) {
+      init();
+      chartInstance = chartInstance = unref(chartInstanceRef);
+      if (!chartInstance) {
+        return;
+      }
+    }
+    clear && chartInstance.clear();
+    nextTick(() => {
+      useTimeout(() => {
+        chartInstance && chartInstance.setOption(options);
+      }, 30);
+    });
+  }
+
+  function resize() {
+    const chartInstance = unref(chartInstanceRef);
+    if (!chartInstance) {
+      return;
+    }
+    chartInstance.resize();
+  }
+  tryOnUnmounted(() => {
+    const chartInstance = unref(chartInstanceRef);
+    if (!chartInstance) {
+      return;
+    }
+    chartInstance.dispose();
+    chartInstanceRef.value = null;
+  });
+  return {
+    setOptions,
+    echarts,
+  };
+}

+ 2 - 2
src/layouts/default/index.tsx

@@ -15,7 +15,7 @@ import { useFullContent } from '/@/hooks/web/useFullContent';
 import LockPage from '/@/views/sys/lock/index.vue';
 
 import './index.less';
-import { userStore } from '/@/store/modules/user';
+// import { userStore } from '/@/store/modules/user';
 export default defineComponent({
   name: 'DefaultLayout',
   setup() {
@@ -52,7 +52,7 @@ export default defineComponent({
     // const { currentRoute } = useRouter();
     onMounted(() => {
       // Each refresh will request the latest user information, if you don’t need it, you can delete it
-      userStore.getUserInfoAction({ userId: userStore.getUserInfoState.userId });
+      // userStore.getUserInfoAction({ userId: userStore.getUserInfoState.userId });
     });
 
     // Get project configuration

+ 10 - 1
src/router/menus/index.ts

@@ -16,8 +16,17 @@ import iframeDemo from './modules/demo/iframe';
 import compDemo from './modules/demo/comp';
 import permissionDemo from './modules/demo/permission';
 import featDemo from './modules/demo/feat';
+import chartsDemo from './modules/demo/charts';
 
-const menuModules = [dashboardDemo, featDemo, exceptionDemo, iframeDemo, compDemo, permissionDemo];
+const menuModules = [
+  dashboardDemo,
+  featDemo,
+  exceptionDemo,
+  iframeDemo,
+  compDemo,
+  permissionDemo,
+  chartsDemo,
+];
 
 // ===========================
 // ==========Helper===========

+ 33 - 0
src/router/menus/modules/demo/charts.ts

@@ -0,0 +1,33 @@
+import type { MenuModule } from '/@/router/types.d';
+const menu: MenuModule = {
+  orderNo: 500,
+  menu: {
+    name: '图表',
+    path: '/charts',
+    children: [
+      {
+        path: '/echarts',
+        name: 'Echarts',
+        children: [
+          {
+            path: '/map',
+            name: '地图',
+          },
+          {
+            path: '/line',
+            name: '折线图',
+          },
+          {
+            path: '/pie',
+            name: '饼图',
+          },
+        ],
+      },
+      {
+        path: '/apexChart',
+        name: 'ApexChart',
+      },
+    ],
+  },
+};
+export default menu;

+ 2 - 0
src/router/routes/index.ts

@@ -11,6 +11,7 @@ import iframeDemo from './modules/demo/iframe';
 import compDemo from './modules/demo/comp';
 import permissionDemo from './modules/demo/permission';
 import featDemo from './modules/demo/feat';
+import chartsDemo from './modules/demo/charts';
 
 const routeModuleList: AppRouteModule[] = [
   exceptionDemo,
@@ -19,6 +20,7 @@ const routeModuleList: AppRouteModule[] = [
   compDemo,
   featDemo,
   permissionDemo,
+  chartsDemo,
 ];
 
 export const asyncRoutes = [

+ 60 - 0
src/router/routes/modules/demo/charts.ts

@@ -0,0 +1,60 @@
+import type { AppRouteModule } from '/@/router/types';
+
+import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant';
+
+export default {
+  layout: {
+    path: '/charts',
+    name: 'Charts',
+    component: PAGE_LAYOUT_COMPONENT,
+    redirect: '/charts/welcome',
+    meta: {
+      icon: 'ant-design:area-chart-outlined',
+      title: '图表库',
+    },
+  },
+
+  routes: [
+    {
+      path: '/echarts',
+      name: 'Echarts',
+      meta: {
+        title: 'Echarts',
+      },
+      children: [
+        {
+          path: 'map',
+          name: 'Map',
+          component: () => import('/@/views/demo/echarts/Map.vue'),
+          meta: {
+            title: '地图',
+          },
+        },
+        {
+          path: 'line',
+          name: 'Line',
+          component: () => import('/@/views/demo/echarts/Line.vue'),
+          meta: {
+            title: '折线图',
+          },
+        },
+        {
+          path: 'pie',
+          name: 'Pie',
+          component: () => import('/@/views/demo/echarts/Pie.vue'),
+          meta: {
+            title: '饼图',
+          },
+        },
+      ],
+    },
+    {
+      path: '/apexChart',
+      name: 'ApexChart',
+      meta: {
+        title: 'ApexChart',
+      },
+      component: () => import('/@/views/demo/echarts/apex/index.vue'),
+    },
+  ],
+} as AppRouteModule;

+ 126 - 0
src/views/demo/echarts/Line.vue

@@ -0,0 +1,126 @@
+<template>
+  <div class="p-4">
+    <div ref="chartRef" :style="{ height, width }" />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, PropType, ref, Ref, onMounted } from 'vue';
+
+  import echarts from 'echarts';
+  import { useECharts } from '/@/hooks/web/useECharts';
+  import { getLineData } from './data';
+
+  export default defineComponent({
+    props: {
+      width: {
+        type: String as PropType<string>,
+        default: '100%',
+      },
+      height: {
+        type: String as PropType<string>,
+        default: '80vh',
+      },
+    },
+    setup() {
+      const chartRef = ref<HTMLDivElement | null>(null);
+      const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
+      const { barData, lineData, category } = getLineData;
+      onMounted(() => {
+        setOptions({
+          backgroundColor: '#0f375f',
+          tooltip: {
+            trigger: 'axis',
+            axisPointer: {
+              type: 'shadow',
+              label: {
+                show: true,
+                backgroundColor: '#333',
+              },
+            },
+          },
+          legend: {
+            data: ['line', 'bar'],
+            textStyle: {
+              color: '#ccc',
+            },
+          },
+          xAxis: {
+            data: category,
+            axisLine: {
+              lineStyle: {
+                color: '#ccc',
+              },
+            },
+          },
+          yAxis: {
+            splitLine: { show: false },
+            axisLine: {
+              lineStyle: {
+                color: '#ccc',
+              },
+            },
+          },
+          series: [
+            {
+              name: 'line',
+              type: 'line',
+              smooth: true,
+              showAllSymbol: true,
+              symbol: 'emptyCircle',
+              symbolSize: 15,
+              data: lineData,
+            },
+            {
+              name: 'bar',
+              type: 'bar',
+              barWidth: 10,
+              itemStyle: {
+                normal: {
+                  barBorderRadius: 5,
+                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                    { offset: 0, color: '#14c8d4' },
+                    { offset: 1, color: '#43eec6' },
+                  ]),
+                },
+              },
+              data: barData,
+            },
+            {
+              name: 'line',
+              type: 'bar',
+              barGap: '-100%',
+              barWidth: 10,
+              itemStyle: {
+                normal: {
+                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                    { offset: 0, color: 'rgba(20,200,212,0.5)' },
+                    { offset: 0.2, color: 'rgba(20,200,212,0.2)' },
+                    { offset: 1, color: 'rgba(20,200,212,0)' },
+                  ]),
+                },
+              },
+              z: -12,
+              data: lineData,
+            },
+            {
+              name: 'dotted',
+              type: 'pictorialBar',
+              symbol: 'rect',
+              itemStyle: {
+                normal: {
+                  color: '#0f375f',
+                },
+              },
+              symbolRepeat: true,
+              symbolSize: [12, 4],
+              symbolMargin: 1,
+              z: -10,
+              data: lineData,
+            },
+          ],
+        });
+      });
+      return { chartRef };
+    },
+  });
+</script>

+ 91 - 0
src/views/demo/echarts/Map.vue

@@ -0,0 +1,91 @@
+<template>
+  <div class="p-4">
+    <div ref="chartRef" :style="{ height, width }" />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, PropType, ref, Ref, onMounted } from 'vue';
+
+  import { useECharts } from '/@/hooks/web/useECharts';
+  import { mapData } from './data';
+
+  import 'echarts/map/js/china';
+  export default defineComponent({
+    props: {
+      width: {
+        type: String as PropType<string>,
+        default: '100%',
+      },
+      height: {
+        type: String as PropType<string>,
+        default: '80vh',
+      },
+    },
+    setup() {
+      const chartRef = ref<HTMLDivElement | null>(null);
+      const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
+
+      onMounted(() => {
+        setOptions({
+          visualMap: {
+            min: 0,
+            max: 1000,
+            left: 'left',
+            top: 'bottom',
+            text: ['高', '低'],
+            calculable: false,
+            orient: 'horizontal',
+            inRange: {
+              color: ['#e0ffff', '#006edd'],
+              symbolSize: [30, 100],
+            },
+          },
+          tooltip: {
+            trigger: 'item',
+            backgroundColor: 'rgba(0, 0, 0, .6)',
+            textStyle: {
+              color: '#fff',
+              fontSize: 12,
+            },
+          },
+          series: [
+            {
+              name: 'iphone4',
+              type: 'map',
+              mapType: 'china',
+              label: {
+                normal: {
+                  show: true,
+                  textStyle: {
+                    color: 'rgb(249, 249, 249)',
+                    fontSize: 10,
+                  },
+                },
+                emphasis: {
+                  show: true,
+                  textStyle: {
+                    color: 'rgb(249, 249, 249)',
+                    fontSize: 14,
+                  },
+                },
+              },
+              itemStyle: {
+                normal: {
+                  label: { show: true },
+                  areaColor: '#2f82ce',
+                  borderColor: '#0DAAC1',
+                },
+                emphasis: {
+                  label: { show: true },
+                  areaColor: '#2f82ce',
+                },
+              },
+              data: mapData,
+            },
+          ],
+        });
+      });
+      return { chartRef };
+    },
+  });
+</script>

+ 149 - 0
src/views/demo/echarts/Pie.vue

@@ -0,0 +1,149 @@
+<template>
+  <div class="p-4">
+    <div ref="chartRef" :style="{ height, width }" />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, PropType, ref, Ref, onMounted } from 'vue';
+
+  import { useECharts } from '/@/hooks/web/useECharts';
+
+  export default defineComponent({
+    props: {
+      width: {
+        type: String as PropType<string>,
+        default: '100%',
+      },
+      height: {
+        type: String as PropType<string>,
+        default: '80vh',
+      },
+    },
+    setup() {
+      const chartRef = ref<HTMLDivElement | null>(null);
+      const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
+      const dataAll = [389, 259, 262, 324, 232, 176, 196, 214, 133, 370];
+      const yAxisData = [
+        '原因1',
+        '原因2',
+        '原因3',
+        '原因4',
+        '原因5',
+        '原因6',
+        '原因7',
+        '原因8',
+        '原因9',
+        '原因10',
+      ];
+      onMounted(() => {
+        setOptions({
+          backgroundColor: '#0f375f',
+          title: [
+            {
+              text: '各渠道投诉占比',
+              x: '2%',
+              y: '1%',
+              textStyle: { color: '#fff', fontSize: 14 },
+            },
+            {
+              text: '投诉原因TOP10',
+              x: '40%',
+              y: '1%',
+              textStyle: { color: '#fff', fontSize: 14 },
+            },
+            {
+              text: '各级别投诉占比',
+              x: '2%',
+              y: '50%',
+              textStyle: { color: '#fff', fontSize: 14 },
+            },
+          ],
+          grid: [{ x: '50%', y: '7%', width: '45%', height: '90%' }],
+          tooltip: {
+            formatter: '{b} ({c})',
+          },
+          xAxis: [
+            {
+              gridIndex: 0,
+              axisTick: { show: false },
+              axisLabel: { show: false },
+              splitLine: { show: false },
+              axisLine: { show: false },
+            },
+          ],
+          yAxis: [
+            {
+              gridIndex: 0,
+              interval: 0,
+              data: yAxisData.reverse(),
+              axisTick: { show: false },
+              axisLabel: { show: true },
+              splitLine: { show: false },
+              axisLine: { show: true, lineStyle: { color: '#6173a3' } },
+            },
+          ],
+          series: [
+            {
+              name: '各渠道投诉占比',
+              type: 'pie',
+              radius: '30%',
+              center: ['22%', '25%'],
+              color: ['#86c9f4', '#4da8ec', '#3a91d2', '#005fa6', '#315f97'],
+              data: [
+                { value: 335, name: '客服电话' },
+                { value: 310, name: '奥迪官网' },
+                { value: 234, name: '媒体曝光' },
+                { value: 135, name: '质检总局' },
+                { value: 105, name: '其他' },
+              ],
+              labelLine: { normal: { show: false } },
+              itemStyle: {
+                normal: {
+                  label: {
+                    show: true,
+                    formatter: '{b} \n ({d}%)',
+                    textStyle: { color: '#B1B9D3' },
+                  },
+                },
+              },
+            },
+            {
+              name: '各级别投诉占比',
+              type: 'pie',
+              radius: '30%',
+              center: ['22%', '75%'],
+              color: ['#86c9f4', '#4da8ec', '#3a91d2', '#005fa6', '#315f97'],
+              labelLine: { normal: { show: false } },
+              data: [
+                { value: 335, name: 'A级' },
+                { value: 310, name: 'B级' },
+                { value: 234, name: 'C级' },
+                { value: 135, name: 'D级' },
+              ],
+              itemStyle: {
+                normal: {
+                  label: {
+                    show: true,
+                    formatter: '{b} \n ({d}%)',
+                    textStyle: { color: '#B1B9D3' },
+                  },
+                },
+              },
+            },
+            {
+              name: '投诉原因TOP10',
+              type: 'bar',
+              xAxisIndex: 0,
+              yAxisIndex: 0,
+              barWidth: '45%',
+              itemStyle: { normal: { color: '#86c9f4' } },
+              label: { normal: { show: true, position: 'right', textStyle: { color: '#9EA7C4' } } },
+              data: dataAll.sort(),
+            },
+          ],
+        });
+      });
+      return { chartRef };
+    },
+  });
+</script>

+ 58 - 0
src/views/demo/echarts/apex/Area.vue

@@ -0,0 +1,58 @@
+<template>
+  <div ref="chartRef" :style="{ width: '100%' }" />
+</template>
+<script lang="ts">
+  import { defineComponent, ref, Ref, onMounted } from 'vue';
+
+  import { useApexCharts } from '/@/hooks/web/useApexCharts';
+
+  export default defineComponent({
+    setup() {
+      const chartRef = ref<HTMLDivElement | null>(null);
+      const { setOptions } = useApexCharts(chartRef as Ref<HTMLDivElement>);
+
+      onMounted(() => {
+        setOptions({
+          series: [
+            {
+              name: 'series1',
+              data: [31, 40, 28, 51, 42, 109, 100],
+            },
+            {
+              name: 'series2',
+              data: [11, 32, 45, 32, 34, 52, 41],
+            },
+          ],
+          chart: {
+            height: 350,
+            type: 'area',
+          },
+          dataLabels: {
+            enabled: false,
+          },
+          stroke: {
+            curve: 'smooth',
+          },
+          xaxis: {
+            type: 'datetime',
+            categories: [
+              '2018-09-19T00:00:00.000Z',
+              '2018-09-19T01:30:00.000Z',
+              '2018-09-19T02:30:00.000Z',
+              '2018-09-19T03:30:00.000Z',
+              '2018-09-19T04:30:00.000Z',
+              '2018-09-19T05:30:00.000Z',
+              '2018-09-19T06:30:00.000Z',
+            ],
+          },
+          tooltip: {
+            x: {
+              format: 'dd/MM/yy HH:mm',
+            },
+          },
+        });
+      });
+      return { chartRef };
+    },
+  });
+</script>

+ 52 - 0
src/views/demo/echarts/apex/Bar.vue

@@ -0,0 +1,52 @@
+<template>
+  <div ref="chartRef" :style="{ width: '100%' }" />
+</template>
+<script lang="ts">
+  import { defineComponent, ref, Ref, onMounted } from 'vue';
+
+  import { useApexCharts } from '/@/hooks/web/useApexCharts';
+
+  export default defineComponent({
+    setup() {
+      const chartRef = ref<HTMLDivElement | null>(null);
+      const { setOptions } = useApexCharts(chartRef as Ref<HTMLDivElement>);
+
+      onMounted(() => {
+        setOptions({
+          series: [
+            {
+              data: [400, 430, 448, 470, 540, 580, 690, 1100, 1200, 1380],
+            },
+          ],
+          chart: {
+            type: 'bar',
+            height: 350,
+          },
+          plotOptions: {
+            bar: {
+              horizontal: true,
+            },
+          },
+          dataLabels: {
+            enabled: false,
+          },
+          xaxis: {
+            categories: [
+              'South Korea',
+              'Canada',
+              'United Kingdom',
+              'Netherlands',
+              'Italy',
+              'France',
+              'Japan',
+              'United States',
+              'China',
+              'Germany',
+            ],
+          },
+        });
+      });
+      return { chartRef };
+    },
+  });
+</script>

+ 53 - 0
src/views/demo/echarts/apex/Line.vue

@@ -0,0 +1,53 @@
+<template>
+  <div ref="chartRef" :style="{ width: '100%' }" />
+</template>
+<script lang="ts">
+  import { defineComponent, ref, Ref, onMounted } from 'vue';
+
+  import { useApexCharts } from '/@/hooks/web/useApexCharts';
+
+  export default defineComponent({
+    setup() {
+      const chartRef = ref<HTMLDivElement | null>(null);
+      const { setOptions } = useApexCharts(chartRef as Ref<HTMLDivElement>);
+
+      onMounted(() => {
+        setOptions({
+          series: [
+            {
+              name: 'Desktops',
+              data: [10, 41, 35, 51, 49, 62, 69, 91, 148],
+            },
+          ],
+          chart: {
+            height: 350,
+            type: 'line',
+            zoom: {
+              enabled: false,
+            },
+          },
+          dataLabels: {
+            enabled: false,
+          },
+          stroke: {
+            curve: 'straight',
+          },
+          title: {
+            text: 'Product Trends by Month',
+            align: 'left',
+          },
+          grid: {
+            row: {
+              colors: ['#f3f3f3', 'transparent'], // takes an array which will be repeated on columns
+              opacity: 0.5,
+            },
+          },
+          xaxis: {
+            categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep'],
+          },
+        });
+      });
+      return { chartRef };
+    },
+  });
+</script>

+ 138 - 0
src/views/demo/echarts/apex/Mixed.vue

@@ -0,0 +1,138 @@
+<template>
+  <div ref="chartRef" :style="{ width: '100%' }" />
+</template>
+<script lang="ts">
+  import { defineComponent, ref, Ref, onMounted } from 'vue';
+
+  import { useApexCharts } from '/@/hooks/web/useApexCharts';
+
+  export default defineComponent({
+    setup() {
+      const chartRef = ref<HTMLDivElement | null>(null);
+      const { setOptions } = useApexCharts(chartRef as Ref<HTMLDivElement>);
+
+      onMounted(() => {
+        setOptions({
+          series: [
+            {
+              name: 'Income',
+              type: 'column',
+              data: [1.4, 2, 2.5, 1.5, 2.5, 2.8, 3.8, 4.6],
+            },
+            {
+              name: 'Cashflow',
+              type: 'column',
+              data: [1.1, 3, 3.1, 4, 4.1, 4.9, 6.5, 8.5],
+            },
+            {
+              name: 'Revenue',
+              type: 'line',
+              data: [20, 29, 37, 36, 44, 45, 50, 58],
+            },
+          ],
+          chart: {
+            height: 350,
+            type: 'line',
+            stacked: false,
+          },
+          dataLabels: {
+            enabled: false,
+          },
+          stroke: {
+            width: [1, 1, 4],
+          },
+          title: {
+            text: 'XYZ - Stock Analysis (2009 - 2016)',
+            align: 'left',
+            offsetX: 110,
+          },
+          xaxis: {
+            categories: [2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016],
+          },
+          yaxis: [
+            {
+              axisTicks: {
+                show: true,
+              },
+              axisBorder: {
+                show: true,
+                color: '#008FFB',
+              },
+              labels: {
+                style: {
+                  colors: '#008FFB',
+                },
+              },
+              title: {
+                text: 'Income (thousand crores)',
+                style: {
+                  color: '#008FFB',
+                },
+              },
+              tooltip: {
+                enabled: true,
+              },
+            },
+            {
+              seriesName: 'Income',
+              opposite: true,
+              axisTicks: {
+                show: true,
+              },
+              axisBorder: {
+                show: true,
+                color: '#00E396',
+              },
+              labels: {
+                style: {
+                  colors: '#00E396',
+                },
+              },
+              title: {
+                text: 'Operating Cashflow (thousand crores)',
+                style: {
+                  color: '#00E396',
+                },
+              },
+            },
+            {
+              seriesName: 'Revenue',
+              opposite: true,
+              axisTicks: {
+                show: true,
+              },
+              axisBorder: {
+                show: true,
+                color: '#FEB019',
+              },
+              labels: {
+                style: {
+                  colors: '#FEB019',
+                },
+              },
+              title: {
+                text: 'Revenue (thousand crores)',
+                style: {
+                  color: '#FEB019',
+                },
+              },
+            },
+          ],
+          tooltip: {
+            fixed: {
+              enabled: true,
+              position: 'topLeft', // topRight, topLeft, bottomRight, bottomLeft
+              offsetY: 30,
+              offsetX: 60,
+            },
+          },
+          legend: {
+            horizontalAlign: 'left',
+            offsetX: 40,
+          },
+        });
+      });
+      return { chartRef };
+    },
+  });
+</script>

+ 42 - 0
src/views/demo/echarts/apex/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="apex-demo p-4">
+    <div class="demo-box">
+      <Line />
+    </div>
+    <div class="demo-box">
+      <Bar />
+    </div>
+    <div class="demo-box">
+      <Area />
+    </div>
+    <div class="demo-box">
+      <Mixed />
+    </div>
+  </div>
+</template>
+<script>
+  import { defineComponent } from 'vue';
+
+  import Line from './Line.vue';
+  import Bar from './Bar.vue';
+  import Area from './Area.vue';
+  import Mixed from './Mixed.vue';
+  export default defineComponent({
+    components: { Line, Bar, Area, Mixed },
+    setup() {},
+  });
+</script>
+<style lang="less" scoped>
+  .apex-demo {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+
+    .demo-box {
+      width: 49%;
+      margin-bottom: 20px;
+      background: #fff;
+      border-radius: 10px;
+    }
+  }
+</style>

+ 189 - 0
src/views/demo/echarts/data.ts

@@ -0,0 +1,189 @@
+export const mapData: any = [
+  {
+    name: '北京',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '天津',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '上海',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '重庆',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '河北',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '河南',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '云南',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '辽宁',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '黑龙江',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '湖南',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '安徽',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '山东',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '新疆',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '江苏',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '浙江',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '江西',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '湖北',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '广西',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '甘肃',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '山西',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '内蒙古',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '陕西',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '吉林',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '福建',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '贵州',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '广东',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '青海',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '西藏',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '四川',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '宁夏',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '海南',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '台湾',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '香港',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+  {
+    name: '澳门',
+    value: Math.round(Math.random() * 1000),
+    tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
+  },
+];
+
+export const getLineData = (() => {
+  const category: any[] = [];
+  let dottedBase = +new Date();
+  const lineData: any[] = [];
+  const barData: any[] = [];
+
+  for (let i = 0; i < 20; i++) {
+    const date = new Date((dottedBase += 1000 * 3600 * 24));
+    category.push([date.getFullYear(), date.getMonth() + 1, date.getDate()].join('-'));
+    const b = Math.random() * 200;
+    const d = Math.random() * 200;
+    barData.push(b);
+    lineData.push(d + b);
+  }
+  return { barData, category, lineData };
+})();

+ 7 - 1
vite.config.ts

@@ -108,7 +108,13 @@ const viteConfig: UserConfig = {
   // 配置Dep优化行为
   // 会使用 rollup 对 包重新编译,将编译成符合 esm 模块规范的新的包放入 node_modules 下的 .
   optimizeDeps: {
-    include: ['ant-design-vue/es/locale/zh_CN', '@ant-design/icons-vue', 'moment/locale/zh-cn'],
+    include: [
+      'echarts',
+      'echarts/map/js/china',
+      'ant-design-vue/es/locale/zh_CN',
+      '@ant-design/icons-vue',
+      'moment/locale/zh-cn',
+    ],
   },
   // 本地跨域代理
   proxy: createProxy(VITE_PROXY),

+ 106 - 15
yarn.lock

@@ -373,9 +373,9 @@
   integrity sha512-ji5H04VjYtR4seIEgVVLPxg1KRhrFquOiyfPyLVS6vYPkuqV6bcWdssi05YSmf/OAzG4E7Qsg80/bOKyd5tYTw==
 
 "@iconify/json@^1.1.233":
-  version "1.1.239"
-  resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.239.tgz#bfef53a0a2e2fcd5549b421d551146254b1420b6"
-  integrity sha512-6c3k6+MDZ23FrZsTLhx6sc5XKqz54yKGYeSYRVpcLOeou6SkK3WhCo/eCZvfTCR6dnuXrCk736/vrtgknWdhpA==
+  version "1.1.240"
+  resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.240.tgz#c7f2e822fc9e09994d3fd02a0c25684978a1b9fb"
+  integrity sha512-FNeTQ/N9c63Zn+LQyli2vY1C5Mub64fcwvAzrph0Vk+Qzv8dfPgcZtvfK08hukWKOEBUhSjfasQIfnZ5CynkmQ==
 
 "@ls-lint/ls-lint@^1.9.2":
   version "1.9.2"
@@ -569,6 +569,13 @@
     "@types/keygrip" "*"
     "@types/node" "*"
 
+"@types/echarts@^4.8.0":
+  version "4.8.0"
+  resolved "https://registry.npmjs.org/@types/echarts/-/echarts-4.8.0.tgz#60499909599ecb08f8e53cfe441e8933396fe0e9"
+  integrity sha512-U/BSemNukeU3Ly6e9Pi+b+Pb0Y/aDmbFAPkX1skDpZu19N2RH1fYc7cM1WpAGj6f8nWbzvRnhj5JHHdg7Y4SFw==
+  dependencies:
+    "@types/zrender" "*"
+
 "@types/estree@*", "@types/estree@0.0.45":
   version "0.0.45"
   resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884"
@@ -685,9 +692,9 @@
     "@types/lodash" "*"
 
 "@types/lodash@*":
-  version "4.14.161"
-  resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.161.tgz#a21ca0777dabc6e4f44f3d07f37b765f54188b18"
-  integrity sha512-EP6O3Jkr7bXvZZSZYlsgt5DIjiGr0dXP1/jVEwVLTFgg0d+3lWVQkRavYVQszV7dYUwvg0B8R0MBDpcmXg7XIA==
+  version "4.14.162"
+  resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.162.tgz#65d78c397e0d883f44afbf1f7ba9867022411470"
+  integrity sha512-alvcho1kRUnnD1Gcl4J+hK0eencvzq9rmzvFPRmP5rPHx9VVsJj6bKLTATPVf9ktgv4ujzh7T+XWKp+jhuODig==
 
 "@types/lru-cache@^5.1.0":
   version "5.1.0"
@@ -794,6 +801,11 @@
   resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"
   integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==
 
+"@types/zrender@*":
+  version "4.0.0"
+  resolved "https://registry.npmjs.org/@types/zrender/-/zrender-4.0.0.tgz#a6806f12ec4eccaaebd9b0d816f049aca6188fbd"
+  integrity sha512-s89GOIeKFiod2KSqHkfd2rzx+T2DVu7ihZCBEBnhFrzvQPUmzvDSBot9Fi1DfMQm9Odg+rTqoMGC38RvrwJK2w==
+
 "@types/zxcvbn@^4.4.0":
   version "4.4.0"
   resolved "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.0.tgz#fbc1d941cc6d9d37d18405c513ba6b294f89b609"
@@ -994,9 +1006,9 @@ aggregate-error@^3.0.0:
     indent-string "^4.0.0"
 
 ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4:
-  version "6.12.5"
-  resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da"
-  integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==
+  version "6.12.6"
+  resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+  integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
   dependencies:
     fast-deep-equal "^3.1.1"
     fast-json-stable-stringify "^2.0.0"
@@ -1095,6 +1107,18 @@ anymatch@~3.1.1:
     normalize-path "^3.0.0"
     picomatch "^2.0.4"
 
+apexcharts@^3.22.0:
+  version "3.22.0"
+  resolved "https://registry.npmjs.org/apexcharts/-/apexcharts-3.22.0.tgz#df6f1030d0d5bba605069907102d2c261afe97cb"
+  integrity sha512-DDh2eXnAEA8GoKU/hdicOaS2jzGehXwv8Bj1djYYudkeQzEdglFoWsVyIxff+Ds7+aUtVAJzd/9ythZuyyIbXQ==
+  dependencies:
+    svg.draggable.js "^2.2.2"
+    svg.easing.js "^2.0.0"
+    svg.filter.js "^2.0.2"
+    svg.pathmorphing.js "^0.1.3"
+    svg.resize.js "^1.4.3"
+    svg.select.js "^3.0.1"
+
 arg@^4.1.0:
   version "4.1.3"
   resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
@@ -1445,9 +1469,9 @@ camelcase@^5.0.0, camelcase@^5.3.1:
   integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
 
 caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001135:
-  version "1.0.30001146"
-  resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001146.tgz#c61fcb1474520c1462913689201fb292ba6f447c"
-  integrity sha512-VAy5RHDfTJhpxnDdp2n40GPPLp3KqNrXz1QqFv4J64HvArKs8nuNMOWkB3ICOaBTU/Aj4rYAo/ytdQDDFF/Pug==
+  version "1.0.30001147"
+  resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001147.tgz#84d27e5b691a8da66e16887b34c78dacf3935f00"
+  integrity sha512-CPyN875geYk46eIqPl5jlmotCr5YZC2KxIVfb4z0FrNfLxPM+MyodWD2irJGDG8vUUE1fmg3De9vt8uaC6Nf6w==
 
 ccount@^1.0.0:
   version "1.0.5"
@@ -2292,6 +2316,13 @@ duplexer@0.1.1:
   resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
   integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=
 
+echarts@^4.9.0:
+  version "4.9.0"
+  resolved "https://registry.npmjs.org/echarts/-/echarts-4.9.0.tgz#a9b9baa03f03a2a731e6340c55befb57a9e1347d"
+  integrity sha512-+ugizgtJ+KmsJyyDPxaw2Br5FqzuBnyOWwcxPKO6y0gc5caYcfnEUIlNStx02necw8jmKmTafmpHhGo4XDtEIA==
+  dependencies:
+    zrender "4.3.2"
+
 ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -2371,9 +2402,9 @@ esbuild@^0.6.10:
   integrity sha512-InRdL/Q96pUucPqovJzvuLhquZr6jOn81FDVwFjCKz1rYKIm9OdOC+7Fs4vr6x48vKBl5LzKgtjU39BUpO636A==
 
 esbuild@^0.7.1:
-  version "0.7.13"
-  resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.7.13.tgz#ca614002e70814d548dd3243eb3ccc5d93833112"
-  integrity sha512-IHBqE0blYhBw3fSgaQOHI1hdxq5OJgzdU/Tg0FPIZyag6eHCB5I8wirz1Oyse7W+TwCcFeCxMNLhdyjzdFACcw==
+  version "0.7.14"
+  resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.7.14.tgz#9de555e75669187c2315317fbf489b229b1a4cbb"
+  integrity sha512-w2CEVeRcUhCGYMHnNNwb8q+9w42scL7RcNzJm85gZVzNBE3AF0sLq5YP/IdaTBJIFBphIKG3bGbwRH+zsgH/ig==
 
 escalade@^3.1.0:
   version "3.1.0"
@@ -6297,6 +6328,61 @@ svg-tags@^1.0.0:
   resolved "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
   integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=
 
+svg.draggable.js@^2.2.2:
+  version "2.2.2"
+  resolved "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz#c514a2f1405efb6f0263e7958f5b68fce50603ba"
+  integrity sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==
+  dependencies:
+    svg.js "^2.0.1"
+
+svg.easing.js@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz#8aa9946b0a8e27857a5c40a10eba4091e5691f12"
+  integrity sha1-iqmUawqOJ4V6XEChDrpAkeVpHxI=
+  dependencies:
+    svg.js ">=2.3.x"
+
+svg.filter.js@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz#91008e151389dd9230779fcbe6e2c9a362d1c203"
+  integrity sha1-kQCOFROJ3ZIwd5/L5uLJo2LRwgM=
+  dependencies:
+    svg.js "^2.2.5"
+
+svg.js@>=2.3.x, svg.js@^2.0.1, svg.js@^2.2.5, svg.js@^2.4.0, svg.js@^2.6.5:
+  version "2.7.1"
+  resolved "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz#eb977ed4737001eab859949b4a398ee1bb79948d"
+  integrity sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==
+
+svg.pathmorphing.js@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz#c25718a1cc7c36e852ecabc380e758ac09bb2b65"
+  integrity sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==
+  dependencies:
+    svg.js "^2.4.0"
+
+svg.resize.js@^1.4.3:
+  version "1.4.3"
+  resolved "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz#885abd248e0cd205b36b973c4b578b9a36f23332"
+  integrity sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==
+  dependencies:
+    svg.js "^2.6.5"
+    svg.select.js "^2.1.2"
+
+svg.select.js@^2.1.2:
+  version "2.1.2"
+  resolved "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz#e41ce13b1acff43a7441f9f8be87a2319c87be73"
+  integrity sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==
+  dependencies:
+    svg.js "^2.2.5"
+
+svg.select.js@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz#a4198e359f3825739226415f82176a90ea5cc917"
+  integrity sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==
+  dependencies:
+    svg.js "^2.6.5"
+
 table@^5.2.3:
   version "5.4.6"
   resolved "https://registry.npmjs.org/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
@@ -7024,6 +7110,11 @@ yn@3.1.1:
   resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
   integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
 
+zrender@4.3.2:
+  version "4.3.2"
+  resolved "https://registry.npmjs.org/zrender/-/zrender-4.3.2.tgz#ec7432f9415c82c73584b6b7b8c47e1b016209c6"
+  integrity sha512-bIusJLS8c4DkIcdiK+s13HiQ/zjQQVgpNohtd8d94Y2DnJqgM1yjh/jpDb8DoL6hd7r8Awagw8e3qK/oLaWr3g==
+
 zxcvbn@^4.4.2:
   version "4.4.2"
   resolved "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30"