소스 검색

refactor: refactor CountTo component

Vben 3 년 전
부모
커밋
40008bc235

+ 7 - 1
CHANGELOG.zh_CN.md

@@ -1,5 +1,11 @@
 ## Wip
 
+### ✨ Refactor
+
+- `CountTo`组件重构
+
+### ✨ Features
+
 - `radioButtonGroup` 支持`boolean`值
 - `useModalInner` 新增 `redoModalHeight`用于在 Modal 内部重设`Modal`高度
 - `useECharts` 新增`getInstance`用于获取`echart`实例
@@ -12,7 +18,7 @@
 - `BasicTable`新增`updateTableDataRecord`方法用于更新指定行数据
 - `useModal`新增`closeModal`方法用于关闭`Modal`
 
-## Bug Fixes
+### 🐛 Bug Fixes
 
 - 修复`redoModalHeight`不能减小高度的问题
 - 修复 `BasicForm`设置 schemas 数据不生效的问题

+ 6 - 6
package.json

@@ -33,16 +33,16 @@
   },
   "dependencies": {
     "@iconify/iconify": "^2.0.1",
-    "@logicflow/core": "^0.4.11",
-    "@logicflow/extension": "^0.4.12",
-    "@vueuse/core": "^5.0.1",
+    "@logicflow/core": "^0.4.13",
+    "@logicflow/extension": "^0.4.13",
+    "@vueuse/core": "^5.0.2",
     "@zxcvbn-ts/core": "^0.3.0",
     "ant-design-vue": "2.1.2",
     "axios": "^0.21.1",
     "codemirror": "^5.61.1",
     "cropperjs": "^1.5.11",
     "crypto-js": "^4.0.0",
-    "echarts": "^5.1.1",
+    "echarts": "^5.1.2",
     "lodash-es": "^4.17.21",
     "mockjs": "^1.1.0",
     "nprogress": "^0.2.0",
@@ -115,13 +115,13 @@
     "vite-plugin-compression": "^0.2.5",
     "vite-plugin-html": "^2.0.7",
     "vite-plugin-imagemin": "^0.3.2",
-    "vite-plugin-mock": "^2.7.0",
+    "vite-plugin-mock": "^2.7.1",
     "vite-plugin-purge-icons": "^0.7.0",
     "vite-plugin-pwa": "^0.7.3",
     "vite-plugin-style-import": "^0.10.1",
     "vite-plugin-svg-icons": "^0.7.0",
     "vite-plugin-theme": "^0.8.1",
-    "vite-plugin-windicss": "^1.0.2",
+    "vite-plugin-windicss": "^1.0.3",
     "vue-eslint-parser": "^7.6.0",
     "vue-tsc": "^0.1.7"
   },

+ 5 - 2
src/components/ContextMenu/src/ContextMenu.vue

@@ -71,7 +71,8 @@
       });
 
       onUnmounted(() => {
-        unref(wrapRef) && document.body.removeChild(el);
+        const el = unref(wrapRef);
+        el && document.body.removeChild(el);
       });
 
       function handleAction(item: ContextMenuItem, e: MouseEvent) {
@@ -118,8 +119,10 @@
         });
       }
       return () => {
+        if (!unref(showRef)) {
+          return null;
+        }
         const { items } = props;
-        if (!unref(showRef)) return null;
         return (
           <Menu
             inlineIndent={12}

+ 3 - 3
src/components/CountTo/index.ts

@@ -1,4 +1,4 @@
-// Transform vue-count-to to support vue3 version
+import { withInstall } from '/@/utils';
+import countTo from './src/CountTo.vue';
 
-import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
-export const CountTo = createAsyncComponent(() => import('./src/CountTo.vue'));
+export const CountTo = withInstall(countTo);

+ 65 - 117
src/components/CountTo/src/CountTo.vue

@@ -1,50 +1,56 @@
 <template>
-  <span :style="{ color: color }">
-    {{ displayValue }}
+  <span :style="{ color }">
+    {{ value }}
   </span>
 </template>
 <script lang="ts">
-  import { defineComponent, reactive, computed, watch, onMounted, unref, toRef } from 'vue';
-  import { countToProps } from './props';
+  import { defineComponent, ref, computed, watchEffect, unref, onMounted, watch } from 'vue';
+  import { useTransition, TransitionPresets } from '@vueuse/core';
   import { isNumber } from '/@/utils/is';
+
+  const props = {
+    startVal: { type: Number, default: 0 },
+    endVal: { type: Number, default: 2021 },
+    duration: { type: Number, default: 1500 },
+    autoplay: { type: Boolean, default: true },
+    decimals: {
+      type: Number,
+      default: 0,
+      validator(value: number) {
+        return value >= 0;
+      },
+    },
+    prefix: { type: String, default: '' },
+    suffix: { type: String, default: '' },
+    separator: { type: String, default: ',' },
+    decimal: { type: String, default: '.' },
+    /**
+     * font color
+     */
+    color: { type: String },
+    /**
+     * Turn on digital animation
+     */
+    useEasing: { type: Boolean, default: true },
+    /**
+     * Digital animation
+     */
+    transition: { type: String, default: 'linear' },
+  };
+
   export default defineComponent({
     name: 'CountTo',
-    props: countToProps,
-    emits: ['mounted', 'callback'],
+    props,
+    emits: ['onStarted', 'onFinished'],
     setup(props, { emit }) {
-      const state = reactive<{
-        localStartVal: number;
-        printVal: number | null;
-        displayValue: string;
-        paused: boolean;
-        localDuration: number | null;
-        startTime: number | null;
-        timestamp: number | null;
-        rAF: any;
-        remaining: number | null;
-        color: any;
-      }>({
-        localStartVal: props.startVal,
-        displayValue: formatNumber(props.startVal),
-        printVal: null,
-        paused: false,
-        localDuration: props.duration,
-        startTime: null,
-        timestamp: null,
-        remaining: null,
-        rAF: null,
-        color: null,
-      });
+      const source = ref(props.startVal);
+      const disabled = ref(false);
+      let outputValue = useTransition(source);
 
-      onMounted(() => {
-        if (props.autoplay) {
-          start();
-        }
-        emit('mounted');
-      });
+      const value = computed(() => formatNumber(unref(outputValue)));
 
-      const getCountDown = computed(() => {
-        return props.startVal > props.endVal;
+      watchEffect(() => {
+        source.value = props.startVal;
       });
 
       watch([() => props.startVal, () => props.endVal], () => {
@@ -53,93 +59,42 @@
         }
       });
 
-      function start() {
-        const { startVal, duration, color } = props;
-        state.localStartVal = startVal;
-        state.startTime = null;
-        state.localDuration = duration;
-        state.color = color;
-        state.paused = false;
-        state.rAF = requestAnimationFrame(count);
-      }
-
-      function pauseResume() {
-        if (state.paused) {
-          resume();
-          state.paused = false;
-        } else {
-          pause();
-          state.paused = true;
-        }
-      }
-
-      function pause() {
-        cancelAnimationFrame(state.rAF);
-      }
+      onMounted(() => {
+        props.autoplay && start();
+      });
 
-      function resume() {
-        state.startTime = null;
-        state.localDuration = +(state.remaining as number);
-        state.localStartVal = +(state.printVal as number);
-        requestAnimationFrame(count);
+      function start() {
+        run();
+        source.value = props.endVal;
       }
 
       function reset() {
-        state.startTime = null;
-        cancelAnimationFrame(state.rAF);
-        state.displayValue = formatNumber(props.startVal);
+        source.value = props.startVal;
+        run();
       }
 
-      function count(timestamp: number) {
-        const { useEasing, easingFn, endVal } = props;
-        if (!state.startTime) state.startTime = timestamp;
-        state.timestamp = timestamp;
-        const progress = timestamp - state.startTime;
-        state.remaining = (state.localDuration as number) - progress;
-        if (useEasing) {
-          if (unref(getCountDown)) {
-            state.printVal =
-              state.localStartVal -
-              easingFn(progress, 0, state.localStartVal - endVal, state.localDuration as number);
-          } else {
-            state.printVal = easingFn(
-              progress,
-              state.localStartVal,
-              endVal - state.localStartVal,
-              state.localDuration as number
-            );
-          }
-        } else {
-          if (unref(getCountDown)) {
-            state.printVal =
-              state.localStartVal -
-              (state.localStartVal - endVal) * (progress / (state.localDuration as number));
-          } else {
-            state.printVal =
-              state.localStartVal +
-              (endVal - state.localStartVal) * (progress / (state.localDuration as number));
-          }
-        }
-        if (unref(getCountDown)) {
-          state.printVal = state.printVal < endVal ? endVal : state.printVal;
-        } else {
-          state.printVal = state.printVal > endVal ? endVal : state.printVal;
-        }
-        state.displayValue = formatNumber(state.printVal);
-        if (progress < (state.localDuration as number)) {
-          state.rAF = requestAnimationFrame(count);
-        } else {
-          emit('callback');
-        }
+      function run() {
+        outputValue = useTransition(source, {
+          disabled,
+          duration: props.duration,
+          onFinished: () => emit('onFinished'),
+          onStarted: () => emit('onStarted'),
+          ...(props.useEasing ? { transition: TransitionPresets[props.transition] } : {}),
+        });
       }
 
       function formatNumber(num: number | string) {
+        if (!num) {
+          return '';
+        }
         const { decimals, decimal, separator, suffix, prefix } = props;
         num = Number(num).toFixed(decimals);
         num += '';
+
         const x = num.split('.');
         let x1 = x[0];
         const x2 = x.length > 1 ? decimal + x[1] : '';
+
         const rgx = /(\d+)(\d{3})/;
         if (separator && !isNumber(separator)) {
           while (rgx.test(x1)) {
@@ -149,14 +104,7 @@
         return prefix + x1 + x2 + suffix;
       }
 
-      return {
-        count,
-        reset,
-        resume,
-        start,
-        pauseResume,
-        displayValue: toRef(state, 'displayValue'),
-      };
+      return { value, start, reset };
     },
   });
 </script>

+ 0 - 31
src/components/CountTo/src/props.ts

@@ -1,31 +0,0 @@
-import { PropType } from 'vue';
-import { propTypes } from '/@/utils/propTypes';
-export const countToProps = {
-  startVal: propTypes.number.def(0),
-  endVal: propTypes.number.def(2020),
-  duration: propTypes.number.def(1300),
-  autoplay: propTypes.bool.def(true),
-  decimals: {
-    type: Number as PropType<number>,
-    required: false,
-    default: 0,
-    validator(value: number) {
-      return value >= 0;
-    },
-  },
-  color: {
-    type: String as PropType<string>,
-    require: false,
-  },
-  decimal: propTypes.string.def('.'),
-  separator: propTypes.string.def(','),
-  prefix: propTypes.string.def(''),
-  suffix: propTypes.string.def(''),
-  useEasing: propTypes.bool.def(true),
-  easingFn: {
-    type: Function as PropType<(t: number, b: number, c: number, d: number) => number>,
-    default(t: number, b: number, c: number, d: number) {
-      return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b;
-    },
-  },
-};

+ 4 - 3
src/components/Cropper/src/Cropper.vue

@@ -20,7 +20,7 @@
 
   type Options = Cropper.Options;
 
-  const defaultOptions: Cropper.Options = {
+  const defaultOptions: Options = {
     aspectRatio: 16 / 9,
     zoomable: true,
     zoomOnTouch: true,
@@ -42,6 +42,7 @@
     movable: true,
     rotatable: true,
   };
+
   export default defineComponent({
     name: 'CropperImage',
     props: {
@@ -108,9 +109,9 @@
         let imgInfo = cropper.value.getData();
         cropper.value.getCroppedCanvas().toBlob((blob) => {
           let fileReader: FileReader = new FileReader();
-          fileReader.onloadend = (e: any) => {
+          fileReader.onloadend = (e) => {
             ctx.emit('cropperedInfo', {
-              imgBase64: e.target.result,
+              imgBase64: e.target?.result ?? '',
               imgInfo,
             });
           };

+ 40 - 40
yarn.lock

@@ -1252,21 +1252,21 @@
     "@intlify/runtime" "9.1.6"
     "@intlify/shared" "9.1.6"
 
-"@logicflow/core@^0.4.11":
-  version "0.4.11"
-  resolved "https://registry.yarnpkg.com/@logicflow/core/-/core-0.4.11.tgz#3c617e5cddb47e7052d62fee56ba77ab45b1cd25"
-  integrity sha512-FlErJRyKw+XzyT0/0hha8Dwsiok9Cri2ZS2/SDmqLdUK6I3rD6LpmVabj8LjYH4IWb0fOYSfgGhY4oWQAKqa9g==
+"@logicflow/core@^0.4.13":
+  version "0.4.13"
+  resolved "https://registry.npmjs.org/@logicflow/core/-/core-0.4.13.tgz#69d1e7a30b5e545ada3a2e980059f3e6f6923f15"
+  integrity sha512-xOLz8RO6ldAT5H9Q2Ewt9d7z146B54TUom5EXzJ/jx/s1Phy24vP8tAGu6LuoTEitgNHTRQq7WxvH9NqnpxCpg==
   dependencies:
     "@types/mousetrap" "^1.6.4"
     mousetrap "^1.6.5"
     preact "^10.4.8"
 
-"@logicflow/extension@^0.4.12":
-  version "0.4.12"
-  resolved "https://registry.yarnpkg.com/@logicflow/extension/-/extension-0.4.12.tgz#be69e8ebbcffee6bb0f07778f2126ad98f93f64a"
-  integrity sha512-fD0bXxYIEo1d047A3PXkAVMH6vM5y8AAIfLxnXxdMJGOVLH44iWCO6eNW8bvnoab7aSmhj2MWMgY3op5XVZh1Q==
+"@logicflow/extension@^0.4.13":
+  version "0.4.13"
+  resolved "https://registry.npmjs.org/@logicflow/extension/-/extension-0.4.13.tgz#b1c87b5458345414cc6c5fb813511e62db4acfa7"
+  integrity sha512-DAfgO9A5VrJ4oXruTYGgmDGvZAIaT2kLAlTE+luyg4eld6YsgwWXqXazJWCd9ObdAJkLYCf7lU27hZsDNqqbrg==
   dependencies:
-    "@logicflow/core" "^0.4.11"
+    "@logicflow/core" "^0.4.13"
     ids "^1.0.0"
     preact "^10.4.8"
 
@@ -2033,25 +2033,25 @@
   resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.0.11.tgz#20d22dd0da7d358bb21c17f9bde8628152642c77"
   integrity sha512-b+zB8A2so8eCE0JsxjL24J7vdGl8rzPQ09hZNhystm+KqSbKcAej1A+Hbva1rCMmTTqA+hFnUSDc5kouEo0JzA==
 
-"@vueuse/core@^5.0.1":
-  version "5.0.1"
-  resolved "https://registry.npmjs.org/@vueuse/core/-/core-5.0.1.tgz#94bbb6c71d95b79efbdb24111915775e61723f1b"
-  integrity sha512-hzcyYNvW1p9ZEwm+oBaWrHgGx6S93pJBiXLZUj2pgCNiJZjaedoePT9xzesi1SBxeKcYxwToaTISLeKdE4VKeg==
+"@vueuse/core@^5.0.2":
+  version "5.0.2"
+  resolved "https://registry.npmjs.org/@vueuse/core/-/core-5.0.2.tgz#302389f620c0d4b51fdf157012d9b5b522b605e7"
+  integrity sha512-Sp9+7AL4Cg3Tx6I55WoH7zICGRlp6ZUF9NW3EU8SZTkryHm0afAjFfASMwlfV030JFeh45BdqafDOrenVmM9Cw==
   dependencies:
-    "@vueuse/shared" "5.0.1"
+    "@vueuse/shared" "5.0.2"
     vue-demi "*"
 
-"@vueuse/shared@5.0.1":
-  version "5.0.1"
-  resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-5.0.1.tgz#3b6607ffc9e19b322c39be8a2f6b584d203a7c5e"
-  integrity sha512-/+kRII9chn45PhFfRuPVbSQApJHhhqXFhPrWjnYKckMfQE9ZOuNMb1bmQnDTqzuNkoS/ENeHBMq0rnV/cfz/3Q==
+"@vueuse/shared@5.0.2":
+  version "5.0.2"
+  resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-5.0.2.tgz#274c2bf163d25eb7fd2fc51f23088a2b7f060594"
+  integrity sha512-S1hRRmEdipjTD4DbXgPdw4ZZYebU/nDi75vNP3Ibpa1irW3NUNUKOT/TWnwRHLQvXquUtdvalhI8D9Db+czZJg==
   dependencies:
     vue-demi "*"
 
-"@windicss/plugin-utils@1.0.2":
-  version "1.0.2"
-  resolved "https://registry.npmjs.org/@windicss/plugin-utils/-/plugin-utils-1.0.2.tgz#c34d6498058d5f4291805027d2ef6e34638a572a"
-  integrity sha512-W9fZoPNsD3NMVyqzt9eNb1DNp9p4oy7EscCfGVIg1KBxAC8S+AnXtkaR/rad09y+aqzbILKNfzDKdimDR2FA9g==
+"@windicss/plugin-utils@1.0.3":
+  version "1.0.3"
+  resolved "https://registry.npmjs.org/@windicss/plugin-utils/-/plugin-utils-1.0.3.tgz#04d039ef56b58180079df3f9b3bd8a21a57368d3"
+  integrity sha512-SBYjmWBO+dOqxJgyyOAETOuMdcugvVgZYQc3rb7KtcTW5u9UkFXtiuGdoq8cWyFpSkn46gmjCb4WNbY3kEIVnQ==
   dependencies:
     "@antfu/utils" "^0.1.6"
     debug "^4.3.2"
@@ -4050,13 +4050,13 @@ duplexer3@^0.1.4:
   resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
   integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
 
-echarts@^5.1.1:
-  version "5.1.1"
-  resolved "https://registry.npmjs.org/echarts/-/echarts-5.1.1.tgz#b186f162f017c555cfd67b12ede6762bdc3ddfda"
-  integrity sha512-b3nP8M9XwZM2jISuA+fP0EuJv8lcfgWrinel185Npy8bE/UhXTDIPJcqgQOCWdvk0c5CeT6Dsm1xBjmJXAGlxQ==
+echarts@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.npmjs.org/echarts/-/echarts-5.1.2.tgz#aa1ab0cef5b74fa2f7c620261a5f286893d30fd1"
+  integrity sha512-okUhO4sw22vwZp+rTPNjd/bvTdpug4K4sHNHyrV8NdAncIX9/AarlolFqtJCAYKGFYhUBNjIWu1EznFrSWTFxg==
   dependencies:
     tslib "2.0.3"
-    zrender "5.1.0"
+    zrender "5.1.1"
 
 ecstatic@^3.3.2:
   version "3.3.2"
@@ -10573,10 +10573,10 @@ vite-plugin-imagemin@^0.3.2:
     imagemin-svgo "^8.0.0"
     imagemin-webp "^6.0.0"
 
-vite-plugin-mock@^2.7.0:
-  version "2.7.0"
-  resolved "https://registry.yarnpkg.com/vite-plugin-mock/-/vite-plugin-mock-2.7.0.tgz#21aec0397e29d013c87d765c56d177728d4288b4"
-  integrity sha512-hB3MbnQlrmqGOigbPB+UsUQ/ZjTisj75FprJ7IDw8pDYQjWmHC7AtmDOHdzpGYPKEEX1mz7UhGJ93LLarPqJNg==
+vite-plugin-mock@^2.7.1:
+  version "2.7.1"
+  resolved "https://registry.npmjs.org/vite-plugin-mock/-/vite-plugin-mock-2.7.1.tgz#c7d25277f7b88158cd3927c1abee7c7721ed92d3"
+  integrity sha512-UnYcb4UZrpe5fHBNFNEJQetnR32+XxrduTYhyDQTtXmaJ9Yy+JuBfIT6Mueyf7MGPe8T6hNgIEYaMLSUD5+nWA==
   dependencies:
     "@rollup/plugin-node-resolve" "^11.2.1"
     "@types/mockjs" "^1.0.3"
@@ -10647,12 +10647,12 @@ vite-plugin-theme@^0.8.1:
     esbuild-plugin-alias "^0.1.2"
     tinycolor2 "^1.4.2"
 
-vite-plugin-windicss@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-1.0.2.tgz#0d0fd1ff36dc81d348be755e59a8ee471941095c"
-  integrity sha512-iTmkxm8Yp+ZCFWLOs//9q3d4hYaBVDlkRGLzNBUNvRW9AQFVea57ZPhglMm9xOt1nW/O68n5Rkg4/In8rrEjHQ==
+vite-plugin-windicss@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-1.0.3.tgz#bd45cfee13777e7b57c37a257ebcb7e73fee94ab"
+  integrity sha512-y9pudcMajdI88PTs49qGftlfAvsLUUhK2Eig+xn5sgxPCbAc3Rj5phXJkRzGDqfmEzGwbpF6JwjmiGmZkm8V+g==
   dependencies:
-    "@windicss/plugin-utils" "1.0.2"
+    "@windicss/plugin-utils" "1.0.3"
     chalk "^4.1.1"
     debug "^4.3.2"
     windicss "^3.1.3"
@@ -11321,10 +11321,10 @@ yocto-queue@^0.1.0:
   resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
   integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
 
-zrender@5.1.0:
-  version "5.1.0"
-  resolved "https://registry.npmjs.org/zrender/-/zrender-5.1.0.tgz#b6a84c3aa7ccc6642ee0519901ca4c0835c4d85e"
-  integrity sha512-c+8VRx52ycbmqwHeHLlo/BAfIHBl/JZNLM6cfDQFgzIH05yb+f5J9F/fbRsP+zGc8dW9XHuhdt8/iqukgMZSeg==
+zrender@5.1.1:
+  version "5.1.1"
+  resolved "https://registry.npmjs.org/zrender/-/zrender-5.1.1.tgz#0515f4f8cc0f4742f02a6b8819550a6d13d64c5c"
+  integrity sha512-oeWlmUZPQdS9f5hK4pV21tHPqA3wgQ7CkKkw7l0CCBgWlJ/FP+lRgLFtUBW6yam4JX8y9CdHJo1o587VVrbcoQ==
   dependencies:
     tslib "2.0.3"