lvkun 2 роки тому
батько
коміт
aa880660df

+ 3 - 1
.eslintrc.js

@@ -22,6 +22,8 @@ module.exports = {
     '@typescript-eslint/ban-types': 'off',
     'vue/on-parsing-error': 'off',
     'vue/no-unused-vars': 'off',
-    'no-useless-escape': 'off'
+    'no-useless-escape': 'off',
+    'no-use-before-define': 'off',
+    'vue/no-setup-props-destructure': 'off'
   }
 }

+ 133 - 126
package-lock.json

@@ -16279,9 +16279,9 @@
       }
     },
     "@babel/parser": {
-      "version": "7.21.3",
-      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.21.3.tgz",
-      "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ=="
+      "version": "7.23.0",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
+      "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw=="
     },
     "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
       "version": "7.22.5",
@@ -18679,49 +18679,6 @@
         "webpack-merge": "^5.7.3",
         "webpack-virtual-modules": "^0.4.2",
         "whatwg-fetch": "^3.6.2"
-      },
-      "dependencies": {
-        "@vue/vue-loader-v15": {
-          "version": "npm:vue-loader@15.10.2",
-          "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.2.tgz",
-          "integrity": "sha512-ndeSe/8KQc/nlA7TJ+OBhv2qalmj1s+uBs7yHDRFaAXscFTApBzY9F1jES3bautmgWjDlDct0fw8rPuySDLwxw==",
-          "dev": true,
-          "requires": {
-            "@vue/component-compiler-utils": "^3.1.0",
-            "hash-sum": "^1.0.2",
-            "loader-utils": "^1.1.0",
-            "vue-hot-reload-api": "^2.3.0",
-            "vue-style-loader": "^4.1.0"
-          },
-          "dependencies": {
-            "hash-sum": {
-              "version": "1.0.2",
-              "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
-              "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
-              "dev": true
-            }
-          }
-        },
-        "json5": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
-          "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.0"
-          }
-        },
-        "loader-utils": {
-          "version": "1.4.2",
-          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
-          "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
-          "dev": true,
-          "requires": {
-            "big.js": "^5.2.2",
-            "emojis-list": "^3.0.0",
-            "json5": "^1.0.1"
-          }
-        }
       }
     },
     "@vue/cli-shared-utils": {
@@ -18796,49 +18753,49 @@
       }
     },
     "@vue/compiler-core": {
-      "version": "3.2.47",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
-      "integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz",
+      "integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==",
       "requires": {
-        "@babel/parser": "^7.16.4",
-        "@vue/shared": "3.2.47",
+        "@babel/parser": "^7.21.3",
+        "@vue/shared": "3.3.4",
         "estree-walker": "^2.0.2",
-        "source-map": "^0.6.1"
+        "source-map-js": "^1.0.2"
       }
     },
     "@vue/compiler-dom": {
-      "version": "3.2.47",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
-      "integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz",
+      "integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==",
       "requires": {
-        "@vue/compiler-core": "3.2.47",
-        "@vue/shared": "3.2.47"
+        "@vue/compiler-core": "3.3.4",
+        "@vue/shared": "3.3.4"
       }
     },
     "@vue/compiler-sfc": {
-      "version": "3.2.47",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
-      "integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
-      "requires": {
-        "@babel/parser": "^7.16.4",
-        "@vue/compiler-core": "3.2.47",
-        "@vue/compiler-dom": "3.2.47",
-        "@vue/compiler-ssr": "3.2.47",
-        "@vue/reactivity-transform": "3.2.47",
-        "@vue/shared": "3.2.47",
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz",
+      "integrity": "sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==",
+      "requires": {
+        "@babel/parser": "^7.20.15",
+        "@vue/compiler-core": "3.3.4",
+        "@vue/compiler-dom": "3.3.4",
+        "@vue/compiler-ssr": "3.3.4",
+        "@vue/reactivity-transform": "3.3.4",
+        "@vue/shared": "3.3.4",
         "estree-walker": "^2.0.2",
-        "magic-string": "^0.25.7",
+        "magic-string": "^0.30.0",
         "postcss": "^8.1.10",
-        "source-map": "^0.6.1"
+        "source-map-js": "^1.0.2"
       }
     },
     "@vue/compiler-ssr": {
-      "version": "3.2.47",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
-      "integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz",
+      "integrity": "sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==",
       "requires": {
-        "@vue/compiler-dom": "3.2.47",
-        "@vue/shared": "3.2.47"
+        "@vue/compiler-dom": "3.3.4",
+        "@vue/shared": "3.3.4"
       }
     },
     "@vue/component-compiler-utils": {
@@ -18924,57 +18881,98 @@
       }
     },
     "@vue/reactivity": {
-      "version": "3.2.47",
-      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.47.tgz",
-      "integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz",
+      "integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==",
       "requires": {
-        "@vue/shared": "3.2.47"
+        "@vue/shared": "3.3.4"
       }
     },
     "@vue/reactivity-transform": {
-      "version": "3.2.47",
-      "resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
-      "integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz",
+      "integrity": "sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==",
       "requires": {
-        "@babel/parser": "^7.16.4",
-        "@vue/compiler-core": "3.2.47",
-        "@vue/shared": "3.2.47",
+        "@babel/parser": "^7.20.15",
+        "@vue/compiler-core": "3.3.4",
+        "@vue/shared": "3.3.4",
         "estree-walker": "^2.0.2",
-        "magic-string": "^0.25.7"
+        "magic-string": "^0.30.0"
       }
     },
     "@vue/runtime-core": {
-      "version": "3.2.47",
-      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
-      "integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.4.tgz",
+      "integrity": "sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==",
       "requires": {
-        "@vue/reactivity": "3.2.47",
-        "@vue/shared": "3.2.47"
+        "@vue/reactivity": "3.3.4",
+        "@vue/shared": "3.3.4"
       }
     },
     "@vue/runtime-dom": {
-      "version": "3.2.47",
-      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
-      "integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.4.tgz",
+      "integrity": "sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==",
       "requires": {
-        "@vue/runtime-core": "3.2.47",
-        "@vue/shared": "3.2.47",
-        "csstype": "^2.6.8"
+        "@vue/runtime-core": "3.3.4",
+        "@vue/shared": "3.3.4",
+        "csstype": "^3.1.1"
       }
     },
     "@vue/server-renderer": {
-      "version": "3.2.47",
-      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
-      "integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.4.tgz",
+      "integrity": "sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==",
       "requires": {
-        "@vue/compiler-ssr": "3.2.47",
-        "@vue/shared": "3.2.47"
+        "@vue/compiler-ssr": "3.3.4",
+        "@vue/shared": "3.3.4"
       }
     },
     "@vue/shared": {
-      "version": "3.2.47",
-      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.47.tgz",
-      "integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ=="
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
+      "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ=="
+    },
+    "@vue/vue-loader-v15": {
+      "version": "npm:vue-loader@15.10.2",
+      "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.2.tgz",
+      "integrity": "sha512-ndeSe/8KQc/nlA7TJ+OBhv2qalmj1s+uBs7yHDRFaAXscFTApBzY9F1jES3bautmgWjDlDct0fw8rPuySDLwxw==",
+      "dev": true,
+      "requires": {
+        "@vue/component-compiler-utils": "^3.1.0",
+        "hash-sum": "^1.0.2",
+        "loader-utils": "^1.1.0",
+        "vue-hot-reload-api": "^2.3.0",
+        "vue-style-loader": "^4.1.0"
+      },
+      "dependencies": {
+        "hash-sum": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
+          "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
+          "dev": true
+        },
+        "json5": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+          "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.0"
+          }
+        },
+        "loader-utils": {
+          "version": "1.4.2",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
+          "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
+          "dev": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^1.0.1"
+          }
+        }
+      }
     },
     "@vue/web-component-wrapper": {
       "version": "1.3.0",
@@ -20275,6 +20273,11 @@
         "yaml": "^1.7.2"
       }
     },
+    "countup.js": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.8.0.tgz",
+      "integrity": "sha512-f7xEhX0awl4NOElHulrl4XRfKoNH3rB+qfNSZZyjSZhaAoUk6elvhH+MNxMmlmuUJ2/QNTWPSA7U4mNtIAKljQ=="
+    },
     "crelt": {
       "version": "1.0.6",
       "resolved": "https://registry.npmmirror.com/crelt/-/crelt-1.0.6.tgz",
@@ -20487,9 +20490,9 @@
       }
     },
     "csstype": {
-      "version": "2.6.21",
-      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz",
-      "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
+      "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
     },
     "d3": {
       "version": "7.8.5",
@@ -21915,7 +21918,7 @@
     },
     "estree-walker": {
       "version": "2.0.2",
-      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
       "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
     },
     "esutils": {
@@ -23938,11 +23941,11 @@
       }
     },
     "magic-string": {
-      "version": "0.25.9",
-      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz",
-      "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+      "version": "0.30.5",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
+      "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
       "requires": {
-        "sourcemap-codec": "^1.4.8"
+        "@jridgewell/sourcemap-codec": "^1.4.15"
       }
     },
     "make-dir": {
@@ -25960,7 +25963,8 @@
     "source-map": {
       "version": "0.6.1",
       "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true
     },
     "source-map-js": {
       "version": "1.0.2",
@@ -25976,11 +25980,6 @@
         "source-map": "^0.6.0"
       }
     },
-    "sourcemap-codec": {
-      "version": "1.4.8",
-      "resolved": "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
-      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
-    },
     "spdx-correct": {
       "version": "3.2.0",
       "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz",
@@ -26742,15 +26741,15 @@
       "dev": true
     },
     "vue": {
-      "version": "3.2.47",
-      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.2.47.tgz",
-      "integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz",
+      "integrity": "sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==",
       "requires": {
-        "@vue/compiler-dom": "3.2.47",
-        "@vue/compiler-sfc": "3.2.47",
-        "@vue/runtime-dom": "3.2.47",
-        "@vue/server-renderer": "3.2.47",
-        "@vue/shared": "3.2.47"
+        "@vue/compiler-dom": "3.3.4",
+        "@vue/compiler-sfc": "3.3.4",
+        "@vue/runtime-dom": "3.3.4",
+        "@vue/server-renderer": "3.3.4",
+        "@vue/shared": "3.3.4"
       }
     },
     "vue-class-component": {
@@ -26759,6 +26758,14 @@
       "integrity": "sha512-w1nMzsT/UdbDAXKqhwTmSoyuJzUXKrxLE77PCFVuC6syr8acdFDAq116xgvZh9UCuV0h+rlCtxXolr3Hi3HyPQ==",
       "requires": {}
     },
+    "vue-countup-v3": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/vue-countup-v3/-/vue-countup-v3-1.4.0.tgz",
+      "integrity": "sha512-Tt5c28V/9J8Y34uJeHfyFqbsQfUcMoHgwG3OVJ8Sj1PKMpk0i0zqy0gmHCqvZJE/zp26IUNdEmrS08cKGENHrA==",
+      "requires": {
+        "countup.js": "^2.6.2"
+      }
+    },
     "vue-demi": {
       "version": "0.14.5",
       "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.5.tgz",

+ 2 - 1
package.json

@@ -36,8 +36,9 @@
     "path-browserify": "^1.0.1",
     "pinia": "^2.0.33",
     "style-resources-loader": "^1.5.0",
-    "vue": "^3.2.13",
+    "vue": "^3.3.4",
     "vue-class-component": "^8.0.0-0",
+    "vue-countup-v3": "^1.4.0",
     "vue-hooks-plus": "^1.6.2",
     "vue-router": "^4.0.3",
     "w3c-keyname": "^2.2.8"

+ 13 - 3
src/components/StatisticsTemplate/index.vue

@@ -7,7 +7,9 @@
         :xs="12"  :sm="12"  :md="6"  :lg="8"  :xl="4"
       >
         <div class="statistic" >
-          <span class="count" :style="{color: item.color}" >{{item.value}}</span>
+          <span class="count" :style="{color: item.color}" >
+            <CountUp style="height: 64px" :end-val="item.value" :duration="duration(item.value)" />
+          </span>
           <span>{{item.unit}}</span>
         </div>
         <div class="label" >
@@ -19,13 +21,20 @@
 </a-card>
 </template>
 <script lang='ts' setup >
-import vueCountTo from 'vue-count-to'
-
+import CountUp from 'vue-countup-v3'
 interface IProps {
   title: string,
   list: {value: string | number, key?: string, label: string, color: string, unit: string }[]
 }
 
+const duration = (duration: number | string) => {
+  const count = Number(duration)
+  if (count < 10) return 0
+  else if (count < 100) return 2
+  else if (count < 1000) return 3
+  else if (count > 1000) return 5
+}
+
 const props = defineProps<IProps>()
 
 </script>
@@ -33,6 +42,7 @@ const props = defineProps<IProps>()
 @import '~@/styles/theme.less';
 .statistic {
   color: @sublabel-color;
+  height: 70px;
   .count {
     font-size: 48px;
     margin-right: 10px;

+ 274 - 55
src/components/TableProV2/index.tsx

@@ -1,59 +1,278 @@
-import { defineComponent, ref } from 'vue'
-
-// interface Methods {
-//   get: string
-//   post: string
-//   put: string
-//   del: string
-// }
-
-export default defineComponent({
-  name: 'table-pro-v2',
-  props: {
-    methods: {
-      type: Object,
-      required: true,
-      default: () => ({
-        get: '',
-        post: '',
-        put: '',
-        del: ''
-      }),
-      validator: function (keys: 'get' | 'post' | 'put' | 'del') {
-        return keys === 'get'
-      }
-    },
-    columns: {
-      type: Array,
-      default: () => [],
-      required: true
-    },
-    pagination: {
-      type: Object || false,
-      required: true,
-      default: false
-      // validator: function (keys: 'page' | 'pageSize' | 'total' | 'delete') {
-      //   return keys === 'page'
-      // }
-    }
-  },
-  setup (props, ctx) {
-    const loading = ref<boolean>(false)
-    // const { get, post, put, del } = props.methods
-    return () => (
+import {
+  TableColumnProps, Table, Row, Col, Space, Tooltip, Dropdown, Menu,
+  Button, DropdownButton, PaginationProps
+} from 'ant-design-vue'
+import { DownOutlined } from '@ant-design/icons-vue'
+import { PropType, computed, defineComponent, reactive, ref } from 'vue'
+
+/**
+ * @description Table Pro 超级table 将各种业务与操作融合起来
+ */
+
+interface GenericListProps<T> {
+  dataSource: T;
+}
+
+// export default /* #__PURE__ */ defineComponent<T>({
+//   name: 'table-pro-v2',
+//   props: {
+//     columns: {
+//       type: Array as PropType<TableColumnProps[]>,
+//       default: () => [],
+//       required: true
+//     },
+//     pagination: {
+//       type: Object as PropType<PaginationProps> || false,
+//       required: true,
+//       default: () => {
+//         return {
+//           page: 1,
+//           pageSize: 10,
+//           total: 0
+//         }
+//       }
+//     },
+//     request: {
+//       type: Object as PropType<{get: Function, params: Object}>,
+//       default: () => {
+//         return {
+//           params: {},
+//           get: () => {}
+//         }
+//       }
+//     }
+//   },
+//   emits: ['reload', 'add'],
+//   setup (props, ctx) {
+//     const { request, columns } = props
+
+//     const columnsPro = ref<TableColumnProps & {hidden: boolean} []>(columns.map(column => ({ ...column, hidden: false })))
+
+//     const rowCustomized = computed(() => props.columns.map(item => ({ title: item.title, key: item.key })))
+
+//     const loading = ref<Boolean>(false)
+
+//     const dataSource = ref([])
+
+//     const pagination = reactive<PaginationProps>(Object.assign({}, {
+//       current: 1,
+//       pageSize: 10,
+//       total: 0,
+//       onChange: (page: number, pageSize: number) => onChangePage(page, pageSize)
+//     }, { ...props.pagination }))
+
+//     const opraMeun = (
+//       <Menu>
+//         <Menu.item key={0} onClick={ctx.emit('add')} > 新增 </Menu.item>
+//         <Menu.item key={2} onClick={() => exportExcel()}> 导出 </Menu.item>
+//         <Menu.item key={3} onClick={() => pure()}> 纯净 </Menu.item>
+//       </Menu>
+//     )
+
+//     const exportExcel = () => {}
+
+//     const pure = () => {}
+
+//     const onChangePage = (page: number, pageSize: number) => {
+//       pagination.current = page
+//       pagination.pageSize = pageSize
+//       dispatchRequest()
+//     }
+
+//     const dispatchRequest = async () => {
+//       loading.value = true
+//       const { data, sum } = await request.get({
+//         ...request.params,
+//         page: pagination.current,
+//         pageSize: pagination.pageSize
+//       })
+//       loading.value = false
+//       pagination.total = sum
+//       dataSource.value = data
+//     }
+
+//     const init = () => {
+
+//     }
+
+//     init()
+
+//     return () => (
+//       <>
+//         <Row gutter={[8, 8]}>
+//           <Col>
+//             <solt name="search" ></solt>
+//           </Col>
+//           <Col>
+//             <Space>
+//               <Tooltip title='列表定制' >
+//                  {/*  @click.prevent */}
+//                  {/*  <a class="ant-dropdown-link"> <menu-outlined /> </a> */}
+//                 <Dropdown
+//                   trigger={['click']}
+//                   overlay={
+//                   <Menu>
+//                     {
+//                       rowCustomized.value.map(item => (
+//                         <Menu.item key={item.key}>
+//                           {item.title}
+//                         </Menu.item>
+//                       ))
+//                     }
+//                   </Menu>}
+//                 >
+//                 </Dropdown>
+//               </Tooltip>
+//                  {/*  @click.prevent */}
+//                  {/*  <a class="ant-dropdown-link"> <menu-outlined /> </a> */}
+//                 <DropdownButton
+//                   trigger={['click']}
+//                   overlay={opraMeun}
+//                   icon={<DownOutlined />}
+//                   onClick={dispatchRequest}
+//                 >
+//                   <Button>操作</Button>
+//                 </DropdownButton>
+//             </Space>
+//           </Col>
+//         </Row>
+//         <Table
+//           columns={columns}
+//           loading={loading}
+//           pagination={pagination}
+//           dataSource={dataSource}
+//         >
+//           <slot name='action'></slot>
+//         </Table>
+//       </>
+//     )
+//   }
+// })
+
+// export default
+
+const com = defineComponent((props: {
+  columns: TableColumnProps[],
+  pagination: PaginationProps | false
+  request: {
+    get: (record: any) => {data, sum},
+    params: any
+  }
+}) => {
+  const { columns, request } = props
+
+  const emits = defineEmits(['add'])
+
+  const loading = ref<boolean>(false)
+
+  const columnsPro = ref<(TableColumnProps & {hidden: boolean}) []>(columns.map(column => ({ ...column, hidden: false })))
+
+  const columnsFilter = computed(() => columnsPro.value.filter(column => !column.hidden))
+
+  const rowCustomized = computed(() => columns.map(item => ({ title: item.title, key: item.key })))
+
+  const pagination = reactive<PaginationProps>(Object.assign({}, {
+    current: 1,
+    pageSize: 10,
+    total: 0,
+    onChange: (page: number, pageSize: number) => onChangePage(page, pageSize)
+  }, typeof props.pagination === 'boolean' ? {} : { ...props.pagination }))
+
+  const dataSource = ref()
+
+  const opraMeun = (
+    <Menu>
+      <Menu.item key={0} onClick={emits('add')} > 新增 </Menu.item>
+      <Menu.item key={2} onClick={() => exportExcel()}> 导出 </Menu.item>
+      <Menu.item key={3} onClick={() => pure()}> 纯净 </Menu.item>
+    </Menu>
+  )
+
+  const exportExcel = () => {}
+
+  const pure = () => {}
+
+  const reload = () => dispatchRequest()
+
+  const onChangePage = (page: number, pageSize: number) => {
+    pagination.current = page
+    pagination.pageSize = pageSize
+    dispatchRequest()
+  }
+
+  const dispatchRequest = async () => {
+    loading.value = true
+    const { data, sum } = await request.get({
+      ...request.params,
+      page: pagination.current,
+      pageSize: pagination.pageSize
+    })
+    loading.value = false
+    pagination.total = sum
+    dataSource.value = data
+  }
+
+  /**
+   * 展示或者隐藏对应的column
+   */
+  const swicthColumn = (index: number) => {
+    columnsPro.value[index].hidden = !columnsPro.value[index].hidden
+  }
+
+  return () => (
       <>
-        <a-row>
-          <a-col></a-col>
-          <a-col></a-col>
-        </a-row>
-        {/* <a-table
-          columns={props.columns}
-          loading={loading}
-          pagination={props.pagination}
-        >
+        <Row gutter={[8, 8]}>
+          <Col>
+            <solt name="search" ></solt>
+          </Col>
+          <Col>
+            <Space>
+              <Tooltip title='列表定制' >
+                 {/*  @click.prevent */}
+                 {/*  <a class="ant-dropdown-link"> <menu-outlined /> </a> */}
+                <Dropdown
+                  trigger={['click']}
+                  overlay={
+                  <Menu>
+                    {
+                      rowCustomized.value.map((item, index) => (
+                        <Menu.item key={item.key} onClick={() => swicthColumn(index)} >
+                          {item.title}
+                        </Menu.item>
+                      ))
+                    }
+                  </Menu>}
+                >
+                </Dropdown>
+              </Tooltip>
+                 {/*  @click.prevent */}
+                 {/*  <a class="ant-dropdown-link"> <menu-outlined /> </a> */}
+                <DropdownButton
+                  trigger={['click']}
+                  overlay={opraMeun}
+                  icon={<DownOutlined />}
+                  onClick={reload}
+                >
+                  <Button>刷新</Button>
+                </DropdownButton>
+            </Space>
+          </Col>
+        </Row>
+        {/*       dataSource={dataSource} */}
+        <Table
+          columns={columnsFilter.value}
+          loading={loading.value}
+          pagination={typeof props.pagination === 'boolean' ? false : pagination}
 
-        </a-table> */}
+        >
+          <slot></slot>
+          <slot name='action'></slot>
+        </Table>
       </>
-    )
-  }
+  )
 })
+
+export {
+  com
+  // 这里直接导出ref有没有可能优雅一些
+}

+ 0 - 3
src/components/index.tsx

@@ -1,3 +0,0 @@
-export const AppTsx = () => <a-button type="primary" >vue3.0</a-button>
-
-// export const

+ 1 - 3
src/layout/components/Sidebar/index.vue

@@ -7,7 +7,6 @@
   >
     <right-outlined />
   </a-button>
-
   <a-drawer
       placement="left"
       :closable="false"
@@ -41,7 +40,6 @@
 
   <a-layout-sider
     v-if="!isMobile"
-    :trigger="null"
     v-model:collapsed="collapsed"
     collapsible
     :style="{backgroundColor: bgColor, overflow: 'hidden', overflowY: 'scroll'}"
@@ -89,7 +87,7 @@ const route = useRoute()
 
 const router = useRouter()
 
-const collapsed = ref<boolean>(false)
+const collapsed = ref<boolean>(true)
 
 const sidebarRoute = ref<any>()
 

+ 1 - 0
src/layout/user.vue

@@ -37,5 +37,6 @@ console.log('userStore.userInfo:', userStore)
 .user-name {
   font: 16px 'Italiana', sans-serif;
   text-transform: lowercase;
+  cursor: pointer;
 }
 </style>

+ 26 - 17
src/pages/Iot/device/topology.vue

@@ -52,27 +52,28 @@
               class="rich-media-node"
             >
               <a-col :span="24"  >
-                <a-space>
-                  📦
-                    <div>
-                    {{node.value}}
-                    {{node.modelLabel}}
-                    {{node.deviceLabel}}
-                  </div>
-                </a-space>
+                <!--    📦 -->
+                <div class="label" >
+                  <div style="width: 100%; text-align: center;" >{{node.value}}</div>
+                  <div style="width: 100%; text-align: center;" v-if="node.modelLabel" > 模型名: {{node.modelLabel}}</div>
+                  <div style="width: 100%; text-align: center;" v-if="node.deviceLabel" >设备名:  {{node.deviceLabel}}</div>
+                </div>
               </a-col>
-              <a-col>
-                <div>
-                  {{node.transportType}}
-                  {{node.payloadType}}
+              <a-col :span="24" >
+                <div >
+                  <div v-if="node.transportType" >协议:{{node.transportType}}</div>
+                  <div v-if="node.payloadType" >格式:{{node.payloadType}}</div>
                 </div>
                 <div v-if="node.deviceStatus" class="deviceStatus" >
+                  状态:
                   <a-tag :color="DeviceContriller.deviceStatusMap.get(node.deviceStatus)?.color" >
                     {{ DeviceContriller.deviceStatusMap.get(node.deviceStatus)?.name}}
                   </a-tag>
                 </div>
-                <div v-if="node.deviceStatus && node.lastConnectTs">
-                  {{ dayjs(node.lastConnectTs).format('YYYY-MM-DD HH:mm:ss') }} ~ {{ dayjs(node.lastActivityTs).format('YYYY-MM-DD HH:mm:ss')}}
+                <div v-if="node.deviceStatus && node.lastConnectTs"  class="ts">
+                  <div>时间:{{ dayjs(node.lastConnectTs).format('YYYY-MM-DD HH:mm:ss') }}</div>
+                  <div style="width: 100%;text-align: center;" >~</div>
+                  <div style="width: 100%;text-align: right;" >{{ dayjs(node.lastActivityTs).format('YYYY-MM-DD HH:mm:ss')}}</div>
                 </div>
               </a-col>
               <!-- {{node.deviceLabel}} -->
@@ -123,12 +124,12 @@ const treeData = reactive({
     name: '产品模型',
     value: '产品模型'
   },
-  treeConfig: { nodeWidth: 120, nodeHeight: 80, levelHeight: 200 }
+  treeConfig: { nodeWidth: 190, nodeHeight: 80, levelHeight: 200 }
 }
 )
 
 const zoomIn = () => {
-  zoomCount.value += 10
+  zoomCount.value += 5
 
   try {
     vueTreeRef.value.zoomIn()
@@ -138,7 +139,7 @@ const zoomIn = () => {
 }
 
 const zoomOut = () => {
-  zoomCount.value -= 10
+  zoomCount.value -= 5
   try {
     vueTreeRef.value.zoomOut()
   } catch (error) {
@@ -231,6 +232,7 @@ onMounted(() => {
 }
 
 .rich-media-node {
+  width: 300px;
   padding: 8px;
   color: white;
   background-color: #1890ff;
@@ -239,11 +241,18 @@ onMounted(() => {
     display: flex;
     justify-content: center;
     align-items: center;
+    .label {
+    }
     .deviceStatus {
       display: flex;
       justify-content: center;
       align-items: center;
     }
+    .ts {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
   }
 }
 

+ 2 - 2
src/pages/Iot/model/index.vue

@@ -122,12 +122,12 @@
 import { CommonController, ModelController } from '@/controller/index'
 import { onMounted, reactive, watch } from 'vue'
 import { computed } from '@vue/reactivity'
-import { Form } from 'ant-design-vue'
+import { Form, TableColumnProps } from 'ant-design-vue'
 import { useRouter } from 'vue-router'
 import StatisticsTemplate from '@/components/StatisticsTemplate/index.vue'
 import { QuestionCircleOutlined } from '@ant-design/icons-vue'
 // question-circle-outlined
-const columns = [
+const columns: TableColumnProps[] = [
   {
     title: '产品模型名称',
     dataIndex: 'modelLabel',

+ 27 - 0
src/store/modules/designStore/systemStore.ts

@@ -0,0 +1,27 @@
+import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+
+/**
+ * 系统级别的控制
+ * 1. 页面布局控制 竖屏控制与横屏控制
+ * 2. 开启色盲模式
+ * 3.
+ */
+export const useSystemStore = defineStore('systemStore', () => {
+
+  // const
+
+})
+
+// /* 灰度色盲模式 */
+// body {
+//   filter: grayscale(100%);
+// }
+// /* 红-绿色盲模式 */
+// body {
+// filter: url(#protanopia);
+// }
+// /* 蓝-黄色盲模式 */
+// body {
+// filter: url(#tritanopia);
+// }

Різницю між файлами не показано, бо вона завелика
+ 316 - 316
yarn.lock


Деякі файли не було показано, через те що забагато файлів було змінено