mo 1 year ago
parent
commit
1978fd2679

+ 448 - 0
package-lock.json

@@ -14,6 +14,7 @@
         "clean-deep": "^3.4.0",
         "dayjs": "^1.11.7",
         "echarts": "^5.4.2",
+        "moveable": "^0.49.0",
         "numeral": "^2.0.6",
         "pinia": "^2.1.4",
         "plyr": "^3.7.8",
@@ -1706,6 +1707,14 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@cfcs/core": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmmirror.com/@cfcs/core/-/core-0.0.6.tgz",
+      "integrity": "sha512-FxfJMwoLB8MEMConeXUCqtMGqxdtePQxRBOiGip9ULcYYam3WfCgoY6xdnMaSkYvRvmosp5iuG+TiPofm65+Pw==",
+      "dependencies": {
+        "@egjs/component": "^3.0.2"
+      }
+    },
     "node_modules/@css-render/plugin-bem": {
       "version": "0.15.12",
       "resolved": "https://registry.npmmirror.com/@css-render/plugin-bem/-/plugin-bem-0.15.12.tgz",
@@ -1724,6 +1733,34 @@
         "vue": "^3.0.11"
       }
     },
+    "node_modules/@daybrush/utils": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmmirror.com/@daybrush/utils/-/utils-1.13.0.tgz",
+      "integrity": "sha512-ALK12C6SQNNHw1enXK+UO8bdyQ+jaWNQ1Af7Z3FNxeAwjYhQT7do+TRE4RASAJ3ObaS2+TJ7TXR3oz2Gzbw0PQ=="
+    },
+    "node_modules/@egjs/agent": {
+      "version": "2.4.3",
+      "resolved": "https://registry.npmmirror.com/@egjs/agent/-/agent-2.4.3.tgz",
+      "integrity": "sha512-XvksSENe8wPeFlEVouvrOhKdx8HMniJ3by7sro2uPF3M6QqWwjzVcmvwoPtdjiX8O1lfRoLhQMp1a7NGlVTdIA=="
+    },
+    "node_modules/@egjs/children-differ": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/@egjs/children-differ/-/children-differ-1.0.1.tgz",
+      "integrity": "sha512-DRvyqMf+CPCOzAopQKHtW+X8iN6Hy6SFol+/7zCUiE5y4P/OB8JP8FtU4NxtZwtafvSL4faD5KoQYPj3JHzPFQ==",
+      "dependencies": {
+        "@egjs/list-differ": "^1.0.0"
+      }
+    },
+    "node_modules/@egjs/component": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/@egjs/component/-/component-3.0.4.tgz",
+      "integrity": "sha512-sXA7bGbIeLF2OAw/vpka66c6QBBUPcA4UUhR4WGJfnp2XWdiI8QrnJGJMr/UxpE/xnevX9tN3jvNPlW8WkHl3g=="
+    },
+    "node_modules/@egjs/list-differ": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/@egjs/list-differ/-/list-differ-1.0.1.tgz",
+      "integrity": "sha512-OTFTDQcWS+1ZREOdCWuk5hCBgYO4OsD30lXcOCyVOAjXMhgL5rBRDnt/otb6Nz8CzU0L/igdcaQBDLWc4t9gvg=="
+    },
     "node_modules/@emotion/hash": {
       "version": "0.8.0",
       "resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz",
@@ -2492,6 +2529,31 @@
         }
       }
     },
+    "node_modules/@scena/dragscroll": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/@scena/dragscroll/-/dragscroll-1.4.0.tgz",
+      "integrity": "sha512-3O8daaZD9VXA9CP3dra6xcgt/qrm0mg0xJCwiX6druCteQ9FFsXffkF8PrqxY4Z4VJ58fFKEa0RlKqbsi/XnRA==",
+      "dependencies": {
+        "@daybrush/utils": "^1.6.0",
+        "@scena/event-emitter": "^1.0.2"
+      }
+    },
+    "node_modules/@scena/event-emitter": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/@scena/event-emitter/-/event-emitter-1.0.5.tgz",
+      "integrity": "sha512-AzY4OTb0+7ynefmWFQ6hxDdk0CySAq/D4efljfhtRHCOP7MBF9zUfhKG3TJiroVjASqVgkRJFdenS8ArZo6Olg==",
+      "dependencies": {
+        "@daybrush/utils": "^1.1.1"
+      }
+    },
+    "node_modules/@scena/matrix": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/@scena/matrix/-/matrix-1.1.1.tgz",
+      "integrity": "sha512-JVKBhN0tm2Srl+Yt+Ywqu0oLgLcdemDQlD1OxmN9jaCTwaFPZ7tY8n6dhVgMEaR9qcR7r+kAlMXnSfNyYdE+Vg==",
+      "dependencies": {
+        "@daybrush/utils": "^1.4.0"
+      }
+    },
     "node_modules/@types/crypto-js": {
       "version": "4.1.1",
       "resolved": "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.1.1.tgz",
@@ -3784,6 +3846,49 @@
         "browserslist": "^4.21.5"
       }
     },
+    "node_modules/croact": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/croact/-/croact-1.0.4.tgz",
+      "integrity": "sha512-9GhvyzTY/IVUrMQ2iz/mzgZ8+NcjczmIo/t4FkC1CU0CEcau6v6VsEih4jkTa4ZmRgYTF0qXEZLObCzdDFplpw==",
+      "dependencies": {
+        "@daybrush/utils": "^1.13.0",
+        "@egjs/list-differ": "^1.0.0"
+      }
+    },
+    "node_modules/croact-css-styled": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmmirror.com/croact-css-styled/-/croact-css-styled-1.1.9.tgz",
+      "integrity": "sha512-G7yvRiVJ3Eoj0ov2h2xR4312hpOzATay2dGS9clK8yJQothjH1sBXIyvOeRP5wBKD9mPcKcoUXPCPsl0tQog4w==",
+      "dependencies": {
+        "@daybrush/utils": "^1.13.0",
+        "css-styled": "~1.0.8",
+        "framework-utils": "^1.1.0"
+      }
+    },
+    "node_modules/croact-moveable": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmmirror.com/croact-moveable/-/croact-moveable-0.5.0.tgz",
+      "integrity": "sha512-C4FDdrChSD2/YGW2W9G+OiTGQeg1ONb6ZQrJKDSPji7+ujceoMMka8C7E1kOMxZKmXtiNb1vKadyQNx1cRW0GQ==",
+      "dependencies": {
+        "@daybrush/utils": "^1.13.0",
+        "@egjs/agent": "^2.2.1",
+        "@egjs/children-differ": "^1.0.1",
+        "@egjs/list-differ": "^1.0.0",
+        "@scena/dragscroll": "^1.4.0",
+        "@scena/event-emitter": "^1.0.5",
+        "@scena/matrix": "^1.1.1",
+        "croact-css-styled": "^1.1.9",
+        "css-to-mat": "^1.1.1",
+        "framework-utils": "^1.1.0",
+        "gesto": "^1.19.0",
+        "overlap-area": "^1.1.0",
+        "react-css-styled": "^1.1.9",
+        "react-moveable": "~0.52.0"
+      },
+      "peerDependencies": {
+        "croact": "^1.0.4"
+      }
+    },
     "node_modules/cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -3820,6 +3925,23 @@
       "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==",
       "dev": true
     },
+    "node_modules/css-styled": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/css-styled/-/css-styled-1.0.8.tgz",
+      "integrity": "sha512-tCpP7kLRI8dI95rCh3Syl7I+v7PP+2JYOzWkl0bUEoSbJM+u8ITbutjlQVf0NC2/g4ULROJPi16sfwDIO8/84g==",
+      "dependencies": {
+        "@daybrush/utils": "^1.13.0"
+      }
+    },
+    "node_modules/css-to-mat": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/css-to-mat/-/css-to-mat-1.1.1.tgz",
+      "integrity": "sha512-kvpxFYZb27jRd2vium35G7q5XZ2WJ9rWjDUMNT36M3Hc41qCrLXFM5iEKMGXcrPsKfXEN+8l/riB4QzwwwiEyQ==",
+      "dependencies": {
+        "@daybrush/utils": "^1.13.0",
+        "@scena/matrix": "^1.0.0"
+      }
+    },
     "node_modules/cssesc": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
@@ -4774,6 +4896,11 @@
         "node": "*"
       }
     },
+    "node_modules/framework-utils": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/framework-utils/-/framework-utils-1.1.0.tgz",
+      "integrity": "sha512-KAfqli5PwpFJ8o3psRNs8svpMGyCSAe8nmGcjQ0zZBWN2H6dZDnq+ABp3N3hdUmFeMrLtjOCTXD4yplUJIWceg=="
+    },
     "node_modules/fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -4808,6 +4935,15 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/gesto": {
+      "version": "1.19.1",
+      "resolved": "https://registry.npmmirror.com/gesto/-/gesto-1.19.1.tgz",
+      "integrity": "sha512-ofWVEdqmnpFm3AFf7aoclhoayseb3OkwSiXbXusKYu/99iN5HgeWP+SWqdghQ5TFlOgP5Zlz+6SY8mP2V0kFaQ==",
+      "dependencies": {
+        "@daybrush/utils": "^1.13.0",
+        "@scena/event-emitter": "^1.0.2"
+      }
+    },
     "node_modules/get-intrinsic": {
       "version": "1.2.0",
       "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
@@ -5628,6 +5764,22 @@
         "node": ">=6"
       }
     },
+    "node_modules/keycode": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmmirror.com/keycode/-/keycode-2.2.1.tgz",
+      "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg=="
+    },
+    "node_modules/keycon": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/keycon/-/keycon-1.4.0.tgz",
+      "integrity": "sha512-p1NAIxiRMH3jYfTeXRs2uWbVJ1WpEjpi8ktzUyBJsX7/wn2qu2VRXktneBLNtKNxJmlUYxRi9gOJt1DuthXR7A==",
+      "dependencies": {
+        "@cfcs/core": "^0.0.6",
+        "@daybrush/utils": "^1.7.1",
+        "@scena/event-emitter": "^1.0.2",
+        "keycode": "^2.2.0"
+      }
+    },
     "node_modules/kind-of": {
       "version": "6.0.3",
       "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz",
@@ -6227,6 +6379,18 @@
         "node": ">=10"
       }
     },
+    "node_modules/moveable": {
+      "version": "0.49.0",
+      "resolved": "https://registry.npmmirror.com/moveable/-/moveable-0.49.0.tgz",
+      "integrity": "sha512-69bBN9mIrtRjgx90CHM1lyo9bNuTM4rixj3FZTZuKVyMnTgtWt1fF/g4yY78pqqz9EUIo73cWvfrHhL5LGIPOA==",
+      "dependencies": {
+        "@daybrush/utils": "^1.13.0",
+        "@scena/event-emitter": "^1.0.5",
+        "croact": "^1.0.4",
+        "croact-moveable": "~0.5.0",
+        "react-moveable": "~0.52.0"
+      }
+    },
     "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
@@ -6703,6 +6867,14 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/overlap-area": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/overlap-area/-/overlap-area-1.1.0.tgz",
+      "integrity": "sha512-3dlJgJCaVeXH0/eZjYVJvQiLVVrPO4U1ZGqlATtx6QGO3b5eNM6+JgUKa7oStBTdYuGTk7gVoABCW6Tp+dhRdw==",
+      "dependencies": {
+        "@daybrush/utils": "^1.7.1"
+      }
+    },
     "node_modules/p-finally": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/p-finally/-/p-finally-1.0.0.tgz",
@@ -7124,6 +7296,43 @@
       "resolved": "https://registry.npmmirror.com/rangetouch/-/rangetouch-2.0.1.tgz",
       "integrity": "sha512-sln+pNSc8NGaHoLzwNBssFSf/rSYkqeBXzX1AtJlkJiUaVSJSbRAWJk+4omsXkN+EJalzkZhWQ3th1m0FpR5xA=="
     },
+    "node_modules/react-css-styled": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmmirror.com/react-css-styled/-/react-css-styled-1.1.9.tgz",
+      "integrity": "sha512-M7fJZ3IWFaIHcZEkoFOnkjdiUFmwd8d+gTh2bpqMOcnxy/0Gsykw4dsL4QBiKsxcGow6tETUa4NAUcmJF+/nfw==",
+      "dependencies": {
+        "css-styled": "~1.0.8",
+        "framework-utils": "^1.1.0"
+      }
+    },
+    "node_modules/react-moveable": {
+      "version": "0.52.0",
+      "resolved": "https://registry.npmmirror.com/react-moveable/-/react-moveable-0.52.0.tgz",
+      "integrity": "sha512-JI544MMnObw3TermDZvJwv9zetlXOm2Z+u4hG6VGM/5ZbH3D2IYOD+9Jg4PWMoXhQZl1JiDDOBzmF+suAOZSaw==",
+      "dependencies": {
+        "@daybrush/utils": "^1.13.0",
+        "@egjs/agent": "^2.2.1",
+        "@egjs/children-differ": "^1.0.1",
+        "@egjs/list-differ": "^1.0.0",
+        "@scena/dragscroll": "^1.4.0",
+        "@scena/event-emitter": "^1.0.5",
+        "@scena/matrix": "^1.1.1",
+        "css-to-mat": "^1.1.1",
+        "framework-utils": "^1.1.0",
+        "gesto": "^1.19.0",
+        "overlap-area": "^1.1.0",
+        "react-css-styled": "^1.1.9",
+        "react-selecto": "^1.25.0"
+      }
+    },
+    "node_modules/react-selecto": {
+      "version": "1.25.1",
+      "resolved": "https://registry.npmmirror.com/react-selecto/-/react-selecto-1.25.1.tgz",
+      "integrity": "sha512-0TKJA72BHxsZ2jpMK5iAnzRNfeyqfu8r4FF69sFVc3NfSEVTgZMd8B5g0OHw8jJor9x6q+iOJIa3zjot0wSBeg==",
+      "dependencies": {
+        "selecto": "~1.25.0"
+      }
+    },
     "node_modules/readable-stream": {
       "version": "3.6.2",
       "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -7398,6 +7607,23 @@
       "integrity": "sha512-lEV5VB8BUKTo/AfktXJcy+JeXns26ylbMkIUco8CYREsQijuz4mrXres2Q+vMLdwkuLxJdIPQ8IlCIxLYm71Yw==",
       "dev": true
     },
+    "node_modules/selecto": {
+      "version": "1.25.0",
+      "resolved": "https://registry.npmmirror.com/selecto/-/selecto-1.25.0.tgz",
+      "integrity": "sha512-fw43SV6PUSxyrJcm1+mz0Les2QABnP9jXqXfFGXojtrMBoVdqNJoq9gUWl7ysH73o70u8YgBbjeXE6L6SfZ+Jg==",
+      "dependencies": {
+        "@daybrush/utils": "^1.13.0",
+        "@egjs/children-differ": "^1.0.1",
+        "@scena/dragscroll": "^1.4.0",
+        "@scena/event-emitter": "^1.0.5",
+        "css-styled": "^1.0.8",
+        "css-to-mat": "^1.1.1",
+        "framework-utils": "^1.1.0",
+        "gesto": "^1.19.1",
+        "keycon": "^1.2.0",
+        "overlap-area": "^1.1.0"
+      }
+    },
     "node_modules/semver": {
       "version": "6.3.0",
       "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz",
@@ -9832,6 +10058,14 @@
         "to-fast-properties": "^2.0.0"
       }
     },
+    "@cfcs/core": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmmirror.com/@cfcs/core/-/core-0.0.6.tgz",
+      "integrity": "sha512-FxfJMwoLB8MEMConeXUCqtMGqxdtePQxRBOiGip9ULcYYam3WfCgoY6xdnMaSkYvRvmosp5iuG+TiPofm65+Pw==",
+      "requires": {
+        "@egjs/component": "^3.0.2"
+      }
+    },
     "@css-render/plugin-bem": {
       "version": "0.15.12",
       "resolved": "https://registry.npmmirror.com/@css-render/plugin-bem/-/plugin-bem-0.15.12.tgz",
@@ -9844,6 +10078,34 @@
       "integrity": "sha512-AQLGhhaE0F+rwybRCkKUdzBdTEM/5PZBYy+fSYe1T9z9+yxMuV/k7ZRqa4M69X+EI1W8pa4kc9Iq2VjQkZx4rg==",
       "dev": true
     },
+    "@daybrush/utils": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmmirror.com/@daybrush/utils/-/utils-1.13.0.tgz",
+      "integrity": "sha512-ALK12C6SQNNHw1enXK+UO8bdyQ+jaWNQ1Af7Z3FNxeAwjYhQT7do+TRE4RASAJ3ObaS2+TJ7TXR3oz2Gzbw0PQ=="
+    },
+    "@egjs/agent": {
+      "version": "2.4.3",
+      "resolved": "https://registry.npmmirror.com/@egjs/agent/-/agent-2.4.3.tgz",
+      "integrity": "sha512-XvksSENe8wPeFlEVouvrOhKdx8HMniJ3by7sro2uPF3M6QqWwjzVcmvwoPtdjiX8O1lfRoLhQMp1a7NGlVTdIA=="
+    },
+    "@egjs/children-differ": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/@egjs/children-differ/-/children-differ-1.0.1.tgz",
+      "integrity": "sha512-DRvyqMf+CPCOzAopQKHtW+X8iN6Hy6SFol+/7zCUiE5y4P/OB8JP8FtU4NxtZwtafvSL4faD5KoQYPj3JHzPFQ==",
+      "requires": {
+        "@egjs/list-differ": "^1.0.0"
+      }
+    },
+    "@egjs/component": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/@egjs/component/-/component-3.0.4.tgz",
+      "integrity": "sha512-sXA7bGbIeLF2OAw/vpka66c6QBBUPcA4UUhR4WGJfnp2XWdiI8QrnJGJMr/UxpE/xnevX9tN3jvNPlW8WkHl3g=="
+    },
+    "@egjs/list-differ": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/@egjs/list-differ/-/list-differ-1.0.1.tgz",
+      "integrity": "sha512-OTFTDQcWS+1ZREOdCWuk5hCBgYO4OsD30lXcOCyVOAjXMhgL5rBRDnt/otb6Nz8CzU0L/igdcaQBDLWc4t9gvg=="
+    },
     "@emotion/hash": {
       "version": "0.8.0",
       "resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz",
@@ -10319,6 +10581,31 @@
         "picomatch": "^2.3.1"
       }
     },
+    "@scena/dragscroll": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/@scena/dragscroll/-/dragscroll-1.4.0.tgz",
+      "integrity": "sha512-3O8daaZD9VXA9CP3dra6xcgt/qrm0mg0xJCwiX6druCteQ9FFsXffkF8PrqxY4Z4VJ58fFKEa0RlKqbsi/XnRA==",
+      "requires": {
+        "@daybrush/utils": "^1.6.0",
+        "@scena/event-emitter": "^1.0.2"
+      }
+    },
+    "@scena/event-emitter": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/@scena/event-emitter/-/event-emitter-1.0.5.tgz",
+      "integrity": "sha512-AzY4OTb0+7ynefmWFQ6hxDdk0CySAq/D4efljfhtRHCOP7MBF9zUfhKG3TJiroVjASqVgkRJFdenS8ArZo6Olg==",
+      "requires": {
+        "@daybrush/utils": "^1.1.1"
+      }
+    },
+    "@scena/matrix": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/@scena/matrix/-/matrix-1.1.1.tgz",
+      "integrity": "sha512-JVKBhN0tm2Srl+Yt+Ywqu0oLgLcdemDQlD1OxmN9jaCTwaFPZ7tY8n6dhVgMEaR9qcR7r+kAlMXnSfNyYdE+Vg==",
+      "requires": {
+        "@daybrush/utils": "^1.4.0"
+      }
+    },
     "@types/crypto-js": {
       "version": "4.1.1",
       "resolved": "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.1.1.tgz",
@@ -11380,6 +11667,46 @@
         "browserslist": "^4.21.5"
       }
     },
+    "croact": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/croact/-/croact-1.0.4.tgz",
+      "integrity": "sha512-9GhvyzTY/IVUrMQ2iz/mzgZ8+NcjczmIo/t4FkC1CU0CEcau6v6VsEih4jkTa4ZmRgYTF0qXEZLObCzdDFplpw==",
+      "requires": {
+        "@daybrush/utils": "^1.13.0",
+        "@egjs/list-differ": "^1.0.0"
+      }
+    },
+    "croact-css-styled": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmmirror.com/croact-css-styled/-/croact-css-styled-1.1.9.tgz",
+      "integrity": "sha512-G7yvRiVJ3Eoj0ov2h2xR4312hpOzATay2dGS9clK8yJQothjH1sBXIyvOeRP5wBKD9mPcKcoUXPCPsl0tQog4w==",
+      "requires": {
+        "@daybrush/utils": "^1.13.0",
+        "css-styled": "~1.0.8",
+        "framework-utils": "^1.1.0"
+      }
+    },
+    "croact-moveable": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmmirror.com/croact-moveable/-/croact-moveable-0.5.0.tgz",
+      "integrity": "sha512-C4FDdrChSD2/YGW2W9G+OiTGQeg1ONb6ZQrJKDSPji7+ujceoMMka8C7E1kOMxZKmXtiNb1vKadyQNx1cRW0GQ==",
+      "requires": {
+        "@daybrush/utils": "^1.13.0",
+        "@egjs/agent": "^2.2.1",
+        "@egjs/children-differ": "^1.0.1",
+        "@egjs/list-differ": "^1.0.0",
+        "@scena/dragscroll": "^1.4.0",
+        "@scena/event-emitter": "^1.0.5",
+        "@scena/matrix": "^1.1.1",
+        "croact-css-styled": "^1.1.9",
+        "css-to-mat": "^1.1.1",
+        "framework-utils": "^1.1.0",
+        "gesto": "^1.19.0",
+        "overlap-area": "^1.1.0",
+        "react-css-styled": "^1.1.9",
+        "react-moveable": "~0.52.0"
+      }
+    },
     "cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -11415,6 +11742,23 @@
         }
       }
     },
+    "css-styled": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/css-styled/-/css-styled-1.0.8.tgz",
+      "integrity": "sha512-tCpP7kLRI8dI95rCh3Syl7I+v7PP+2JYOzWkl0bUEoSbJM+u8ITbutjlQVf0NC2/g4ULROJPi16sfwDIO8/84g==",
+      "requires": {
+        "@daybrush/utils": "^1.13.0"
+      }
+    },
+    "css-to-mat": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/css-to-mat/-/css-to-mat-1.1.1.tgz",
+      "integrity": "sha512-kvpxFYZb27jRd2vium35G7q5XZ2WJ9rWjDUMNT36M3Hc41qCrLXFM5iEKMGXcrPsKfXEN+8l/riB4QzwwwiEyQ==",
+      "requires": {
+        "@daybrush/utils": "^1.13.0",
+        "@scena/matrix": "^1.0.0"
+      }
+    },
     "cssesc": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
@@ -12165,6 +12509,11 @@
       "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
       "dev": true
     },
+    "framework-utils": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/framework-utils/-/framework-utils-1.1.0.tgz",
+      "integrity": "sha512-KAfqli5PwpFJ8o3psRNs8svpMGyCSAe8nmGcjQ0zZBWN2H6dZDnq+ABp3N3hdUmFeMrLtjOCTXD4yplUJIWceg=="
+    },
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -12189,6 +12538,15 @@
       "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
       "dev": true
     },
+    "gesto": {
+      "version": "1.19.1",
+      "resolved": "https://registry.npmmirror.com/gesto/-/gesto-1.19.1.tgz",
+      "integrity": "sha512-ofWVEdqmnpFm3AFf7aoclhoayseb3OkwSiXbXusKYu/99iN5HgeWP+SWqdghQ5TFlOgP5Zlz+6SY8mP2V0kFaQ==",
+      "requires": {
+        "@daybrush/utils": "^1.13.0",
+        "@scena/event-emitter": "^1.0.2"
+      }
+    },
     "get-intrinsic": {
       "version": "1.2.0",
       "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
@@ -12822,6 +13180,22 @@
       "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
       "dev": true
     },
+    "keycode": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmmirror.com/keycode/-/keycode-2.2.1.tgz",
+      "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg=="
+    },
+    "keycon": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/keycon/-/keycon-1.4.0.tgz",
+      "integrity": "sha512-p1NAIxiRMH3jYfTeXRs2uWbVJ1WpEjpi8ktzUyBJsX7/wn2qu2VRXktneBLNtKNxJmlUYxRi9gOJt1DuthXR7A==",
+      "requires": {
+        "@cfcs/core": "^0.0.6",
+        "@daybrush/utils": "^1.7.1",
+        "@scena/event-emitter": "^1.0.2",
+        "keycode": "^2.2.0"
+      }
+    },
     "kind-of": {
       "version": "6.0.3",
       "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz",
@@ -13295,6 +13669,18 @@
       "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
       "dev": true
     },
+    "moveable": {
+      "version": "0.49.0",
+      "resolved": "https://registry.npmmirror.com/moveable/-/moveable-0.49.0.tgz",
+      "integrity": "sha512-69bBN9mIrtRjgx90CHM1lyo9bNuTM4rixj3FZTZuKVyMnTgtWt1fF/g4yY78pqqz9EUIo73cWvfrHhL5LGIPOA==",
+      "requires": {
+        "@daybrush/utils": "^1.13.0",
+        "@scena/event-emitter": "^1.0.5",
+        "croact": "^1.0.4",
+        "croact-moveable": "~0.5.0",
+        "react-moveable": "~0.52.0"
+      }
+    },
     "ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
@@ -13685,6 +14071,14 @@
       "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
       "dev": true
     },
+    "overlap-area": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/overlap-area/-/overlap-area-1.1.0.tgz",
+      "integrity": "sha512-3dlJgJCaVeXH0/eZjYVJvQiLVVrPO4U1ZGqlATtx6QGO3b5eNM6+JgUKa7oStBTdYuGTk7gVoABCW6Tp+dhRdw==",
+      "requires": {
+        "@daybrush/utils": "^1.7.1"
+      }
+    },
     "p-finally": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/p-finally/-/p-finally-1.0.0.tgz",
@@ -14005,6 +14399,43 @@
       "resolved": "https://registry.npmmirror.com/rangetouch/-/rangetouch-2.0.1.tgz",
       "integrity": "sha512-sln+pNSc8NGaHoLzwNBssFSf/rSYkqeBXzX1AtJlkJiUaVSJSbRAWJk+4omsXkN+EJalzkZhWQ3th1m0FpR5xA=="
     },
+    "react-css-styled": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmmirror.com/react-css-styled/-/react-css-styled-1.1.9.tgz",
+      "integrity": "sha512-M7fJZ3IWFaIHcZEkoFOnkjdiUFmwd8d+gTh2bpqMOcnxy/0Gsykw4dsL4QBiKsxcGow6tETUa4NAUcmJF+/nfw==",
+      "requires": {
+        "css-styled": "~1.0.8",
+        "framework-utils": "^1.1.0"
+      }
+    },
+    "react-moveable": {
+      "version": "0.52.0",
+      "resolved": "https://registry.npmmirror.com/react-moveable/-/react-moveable-0.52.0.tgz",
+      "integrity": "sha512-JI544MMnObw3TermDZvJwv9zetlXOm2Z+u4hG6VGM/5ZbH3D2IYOD+9Jg4PWMoXhQZl1JiDDOBzmF+suAOZSaw==",
+      "requires": {
+        "@daybrush/utils": "^1.13.0",
+        "@egjs/agent": "^2.2.1",
+        "@egjs/children-differ": "^1.0.1",
+        "@egjs/list-differ": "^1.0.0",
+        "@scena/dragscroll": "^1.4.0",
+        "@scena/event-emitter": "^1.0.5",
+        "@scena/matrix": "^1.1.1",
+        "css-to-mat": "^1.1.1",
+        "framework-utils": "^1.1.0",
+        "gesto": "^1.19.0",
+        "overlap-area": "^1.1.0",
+        "react-css-styled": "^1.1.9",
+        "react-selecto": "^1.25.0"
+      }
+    },
+    "react-selecto": {
+      "version": "1.25.1",
+      "resolved": "https://registry.npmmirror.com/react-selecto/-/react-selecto-1.25.1.tgz",
+      "integrity": "sha512-0TKJA72BHxsZ2jpMK5iAnzRNfeyqfu8r4FF69sFVc3NfSEVTgZMd8B5g0OHw8jJor9x6q+iOJIa3zjot0wSBeg==",
+      "requires": {
+        "selecto": "~1.25.0"
+      }
+    },
     "readable-stream": {
       "version": "3.6.2",
       "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -14227,6 +14658,23 @@
       "integrity": "sha512-lEV5VB8BUKTo/AfktXJcy+JeXns26ylbMkIUco8CYREsQijuz4mrXres2Q+vMLdwkuLxJdIPQ8IlCIxLYm71Yw==",
       "dev": true
     },
+    "selecto": {
+      "version": "1.25.0",
+      "resolved": "https://registry.npmmirror.com/selecto/-/selecto-1.25.0.tgz",
+      "integrity": "sha512-fw43SV6PUSxyrJcm1+mz0Les2QABnP9jXqXfFGXojtrMBoVdqNJoq9gUWl7ysH73o70u8YgBbjeXE6L6SfZ+Jg==",
+      "requires": {
+        "@daybrush/utils": "^1.13.0",
+        "@egjs/children-differ": "^1.0.1",
+        "@scena/dragscroll": "^1.4.0",
+        "@scena/event-emitter": "^1.0.5",
+        "css-styled": "^1.0.8",
+        "css-to-mat": "^1.1.1",
+        "framework-utils": "^1.1.0",
+        "gesto": "^1.19.1",
+        "keycon": "^1.2.0",
+        "overlap-area": "^1.1.0"
+      }
+    },
     "semver": {
       "version": "6.3.0",
       "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz",

+ 1 - 0
package.json

@@ -27,6 +27,7 @@
     "clean-deep": "^3.4.0",
     "dayjs": "^1.11.7",
     "echarts": "^5.4.2",
+    "moveable": "^0.49.0",
     "numeral": "^2.0.6",
     "pinia": "^2.1.4",
     "plyr": "^3.7.8",

+ 2 - 2
src/components/CSelect/index.module.less

@@ -6,8 +6,8 @@
 .CSelectWrap {
   :global {
     .n-select {
-      width: 180px;
-
+      width: 100%;
+      min-width: 180px;
       .n-base-selection-label {
         height: 43px;
         line-height: 43px;

BIN
src/components/layout/images/beatIcon.png


BIN
src/components/layout/images/setTimeIcon.png


BIN
src/components/layout/images/toneIcon.png


BIN
src/components/layout/images/toolbox.png


+ 45 - 2
src/components/layout/index.module.less

@@ -250,7 +250,6 @@
 }
 
 :global {
-
   .fade-slide-leave-active,
   .fade-slide-enter-active {
     transition: all 0.3s;
@@ -265,4 +264,48 @@
     opacity: 0;
     transform: translateX(30px);
   }
-}
+}
+
+.toolboxImg {
+  width: 60px;
+  height: 68px;
+  position: absolute;
+  right: 32px;
+  bottom: 84px;
+  cursor: pointer;
+  z-index: 1000;
+}
+:global {
+  .moveable-control-box {
+    --moveable-color: transparent !important;
+  }
+  .n-popover {
+    background-color: transparent !important;
+  }
+}
+.booxToolWrap {
+  width: 286px;
+  height: 95px;
+  background: #ffffff;
+  box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1);
+  border-radius: 20px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 8px 23px 10px;
+  justify-content: space-between;
+  .booxToolItem {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    font-size: 12px;
+    &:hover {
+      opacity: 0.8;
+    }
+    img {
+      width: 56px;
+      height: 56px;
+      margin-bottom: 4px;
+    }
+  }
+}

+ 162 - 2
src/components/layout/index.tsx

@@ -1,12 +1,132 @@
-import { Transition, defineComponent } from 'vue';
+import { Transition, defineComponent, onMounted, ref } from 'vue';
 import LayoutSilder from './layoutSilder';
 import LayoutTop from './layoutTop';
 import styles from './index.module.less';
+import { NModal, NPopover } from 'naive-ui';
+import Moveable from 'moveable';
+import toolbox from './images/toolbox.png';
+import setTimeIcon from './images/setTimeIcon.png';
+import beatIcon from './images/beatIcon.png';
+import toneIcon from './images/toneIcon.png';
 export default defineComponent({
   name: 'layoutView',
   setup() {
+    const directionType = ref('left');
+    const showModalBeat = ref(false);
+    const showModalTone = ref(false);
+    const showModalTime = ref(false);
+    const initMoveable = async () => {
+      console.log(document.querySelector('.wrap'));
+      if (document.querySelector('.wrap')) {
+        const moveable = new Moveable(document.querySelector('.wrap') as any, {
+          target: document.querySelector('#moveNPopover') as any,
+          // If the container is null, the position is fixed. (default: parentElement(document.body))
+          container: document.querySelector('.wrap') as any,
+          // snappable: true,
+          // bounds: {"left":100,"top":100,"right":100,"bottom":100},
+          draggable: true,
+          resizable: false,
+          scalable: false,
+          rotatable: false,
+          warpable: false,
+          pinchable: false, // ["resizable", "scalable", "rotatable"]
+          origin: false,
+          keepRatio: false,
+          // Resize, Scale Events at edges.
+          edge: false,
+          throttleDrag: 0,
+          throttleResize: 0,
+          throttleScale: 0,
+          throttleRotate: 0
+        });
+        moveable
+          // .on('dragStart', ({ target, clientX, clientY }) => {
+          //   console.log('dragStart');
+          // })
+          .on(
+            'drag',
+            ({
+              target,
+              transform,
+              left,
+              top,
+              right,
+              bottom,
+              beforeDelta,
+              beforeDist,
+              delta,
+              dist,
+              clientX,
+              clientY
+            }) => {
+              const subdEl = document.getElementById(
+                `moveNPopover`
+              ) as HTMLDivElement;
+              // console.log(subdEl, "subdEl", "drag");
+              const subdElStyle = getComputedStyle(subdEl, null);
+              const RectInfo = {
+                left: Number(subdElStyle.left.replace('px', '')),
+                top: Number(subdElStyle.top.replace('px', '')),
+                width: Number(subdElStyle.width.replace('px', '')),
+                height: Number(subdElStyle.height.replace('px', ''))
+              };
+
+              const mainWidth =
+                parseInt(
+                  window.getComputedStyle(
+                    document.querySelector('.wrap') as Element
+                  ).width
+                ) - RectInfo.width;
+
+              const mainHeight =
+                parseInt(
+                  window.getComputedStyle(
+                    document.querySelector('.wrap') as Element
+                  ).height
+                ) - RectInfo.height;
+
+              if (left < 0) {
+                left = 0;
+              }
+              if (top < 0) {
+                top = 0;
+              }
+              if (right < 0) {
+                right = 0;
+              }
+              if (bottom < 0) {
+                bottom = 0;
+              }
+              if (left > mainWidth) {
+                left = mainWidth;
+              }
+              if (top > mainHeight) {
+                top = mainHeight;
+              }
+
+              target!.style.left = `${left}px`;
+              target!.style.top = `${top}px`;
+            }
+          )
+          .on('dragEnd', async ({ target, isDrag, clientX, clientY }) => {
+            if (document.body.clientWidth / 2 - clientX > 0) {
+              // 往左出
+              directionType.value = 'right';
+            } else {
+              // 往又出
+              directionType.value = 'left';
+            }
+          });
+      }
+    };
+    onMounted(() => {
+      initMoveable();
+    });
+    const startShowModal = (val: 'setTimeIcon' | 'beatIcon' | 'toneIcon') => {
+      console.log(val);
+    };
     return () => (
-      <div class={styles.wrap}>
+      <div class={[styles.wrap, 'wrap']}>
         <div>
           <LayoutSilder></LayoutSilder>
         </div>
@@ -24,6 +144,46 @@ export default defineComponent({
             {/* </div> */}
           </div>
         </div>
+        <NPopover
+          raw
+          trigger="click"
+          show-arrow={false}
+          placement={directionType.value as 'left' | 'right'}
+          v-slots={{
+            trigger: () => (
+              <img
+                src={toolbox}
+                id="moveNPopover"
+                class={[styles.toolboxImg, 'moveNPopover']}
+                alt=""
+              />
+            )
+          }}>
+          <div class={styles.booxToolWrap}>
+            <div
+              class={styles.booxToolItem}
+              onClick={() => startShowModal('beatIcon')}>
+              <img src={beatIcon} alt="" />
+              节拍器
+            </div>
+            <div
+              class={styles.booxToolItem}
+              onClick={() => startShowModal('toneIcon')}>
+              <img src={toneIcon} alt="" />
+              调音器
+            </div>
+            <div
+              class={styles.booxToolItem}
+              onClick={() => startShowModal('setTimeIcon')}>
+              <img src={setTimeIcon} alt="" />
+              计时器
+            </div>
+          </div>
+        </NPopover>
+
+        <NModal v-model:show={showModalBeat.value}></NModal>
+        <NModal v-model:show={showModalTone.value}></NModal>
+        <NModal v-model:show={showModalTime.value}></NModal>
       </div>
     );
   }

+ 17 - 11
src/styles/index.less

@@ -1,4 +1,4 @@
-:root{
+:root {
   --product-color: #3044ca;
 }
 * {
@@ -56,24 +56,24 @@ body {
 }
 
 ::-webkit-scrollbar {
-  width: 8Px;
-  height: 12Px;
+  width: 8px;
+  height: 12px;
   background-color: #fff;
 }
 
 ::-webkit-scrollbar-thumb {
   display: block;
-  min-height: 12Px;
-  min-width: 8Px;
-  border-radius: 6Px;
+  min-height: 12px;
+  min-width: 8px;
+  border-radius: 6px;
   background-color: rgb(217, 217, 217);
 }
 
 ::-webkit-scrollbar-thumb:hover {
   display: block;
-  min-height: 12Px;
-  min-width: 8Px;
-  border-radius: 6Px;
+  min-height: 12px;
+  min-width: 8px;
+  border-radius: 6px;
   background-color: rgb(159, 159, 159);
 }
 
@@ -111,7 +111,13 @@ body {
   background-color: #f7f7f8 !important;
   color: rgba(0, 0, 0, 0.88) !important;
 }
-
+.n-data-table-tr {
+  .n-data-table-td {
+    &:nth-child(1) {
+      padding-left: 20px;
+    }
+  }
+}
 .n-data-table-th__title-wrapper {
   &::after {
     content: '';
@@ -143,7 +149,7 @@ body {
 
   &.background {
     .n-card-header {
-      background: #F5F6FA;
+      background: #f5f6fa;
     }
   }
 

+ 20 - 19
src/views/attend-class/index.module.less

@@ -15,7 +15,7 @@
   width: 187px;
   height: 60px;
   cursor: pointer;
-  transition: all .5s;
+  transition: all 0.5s;
 
   img {
     width: 100%;
@@ -27,7 +27,7 @@
   opacity: 0;
   pointer-events: none;
   transform: translateY(-100%);
-  transition: all .5s;
+  transition: all 0.5s;
 }
 
 .coursewarePlay {
@@ -159,7 +159,7 @@
   }
 
   &.acitveAnimation {
-    transition-duration: .8s;
+    transition-duration: 0.8s;
   }
 
   &.show {
@@ -202,18 +202,16 @@
   }
 
   &:active {
-    opacity: .8;
+    opacity: 0.8;
   }
 
   &.btnsDisabled {
-    opacity: .3;
+    opacity: 0.3;
     pointer-events: none;
   }
 }
 
-
 :global {
-
   .top-enter-active,
   .top-leave-active {
     transition: transform 0.5s;
@@ -266,7 +264,7 @@
       padding: 0px !important;
       text-align: center;
 
-      &>div {
+      & > div {
         margin-bottom: 24px;
       }
     }
@@ -294,7 +292,11 @@
           width: 100%;
           display: inline-block;
           height: 10px;
-          background: linear-gradient(90deg, #77BBFF 0%, rgba(163, 231, 255, 0.22) 100%);
+          background: linear-gradient(
+            90deg,
+            #77bbff 0%,
+            rgba(163, 231, 255, 0.22) 100%
+          );
         }
       }
 
@@ -312,7 +314,7 @@
   right: 38px;
   display: flex;
   z-index: 199;
-  transition: all .3s ease-in-out;
+  transition: all 0.3s ease-in-out;
 
   .switchBtn {
     width: 64px;
@@ -320,18 +322,18 @@
     cursor: pointer;
 
     img {
-      transition: all .2s ease;
+      transition: all 0.2s ease;
       transform: scale(1);
       width: inherit;
       height: inherit;
 
       &:active {
         transform: scale(0.9);
-        transition: all .2s ease;
+        transition: all 0.2s ease;
       }
     }
 
-    &+.switchBtn {
+    & + .switchBtn {
       margin-left: 36px;
     }
   }
@@ -342,7 +344,7 @@
   left: 40px;
   bottom: 40px;
   z-index: 199;
-  transition: all .5s;
+  transition: all 0.5s;
 
   .displayBtn {
     width: 70px;
@@ -354,7 +356,7 @@
       height: inherit;
     }
 
-    &+.switchBtn {
+    & + .switchBtn {
       margin-left: 40px;
     }
   }
@@ -364,7 +366,7 @@
   opacity: 0;
   pointer-events: none;
   transform: translateY(100%);
-  transition: all .5s;
+  transition: all 0.5s;
 }
 
 .attendClassModal {
@@ -413,7 +415,6 @@
       .n-button {
         height: 48px !important;
         min-width: 156px;
-
       }
     }
   }
@@ -424,7 +425,7 @@
 
   :global {
     .n-card-header {
-      background: #F5F6FA;
+      background: #f5f6fa;
     }
   }
-}
+}

+ 2 - 0
src/views/classList/index.module.less

@@ -1,6 +1,8 @@
 .listWrap {
   padding: 32px;
   background-color: #fff;
+  min-height: 100%;
+  border-radius: 20px;
 }
 .addBtnIcon {
   width: 16px;

+ 63 - 27
src/views/classList/index.tsx

@@ -24,9 +24,40 @@ export default defineComponent({
       pagination: {
         page: 1,
         rows: 10,
-        pageTotal: 0
+        pageTotal: 6
       },
-      tableList: [] as any
+      tableList: [
+        {
+          className: '三年级1班',
+          studentNum: '36',
+          lastStudy: '人教版二年级上册 | 第一单元 |【歌表演】如愿'
+        },
+        {
+          className: '三年级2班',
+          studentNum: '43',
+          lastStudy: '人教版二年级上册 | 第一单元 |【歌表演】欢乐颂'
+        },
+        {
+          className: '三年级3班',
+          studentNum: '56',
+          lastStudy: '人教版二年级上册 | 第一单元 |【歌表演】我和我的祖国'
+        },
+        {
+          className: '三年级4班',
+          studentNum: '35',
+          lastStudy: '人教版二年级上册 | 第一单元 |【歌表演】孤勇者'
+        },
+        {
+          className: '三年级5班',
+          studentNum: '42',
+          lastStudy: '人教版二年级上册 | 第一单元 |【歌表演】大雁'
+        },
+        {
+          className: '三年级1班',
+          studentNum: '38',
+          lastStudy: '人教版二年级上册 | 第一单元 |【歌表演】暗香'
+        }
+      ] as any
     });
     const search = () => {
       console.log('search', state);
@@ -43,19 +74,42 @@ export default defineComponent({
       return [
         {
           title: '班级名称',
-          key: 'id'
+          key: 'className'
         },
         {
           title: '学生人数',
-          key: 'id'
+          key: 'studentNum'
         },
         {
           title: '上次学习',
-          key: 'id'
+          key: 'lastStudy'
         },
         {
           title: '操作',
-          key: 'id'
+          key: 'id',
+          render(row: any) {
+            return (
+              <div>
+                <NSpace>
+                  <NButton type="primary" text>
+                    详情
+                  </NButton>
+                  <NButton type="primary" text>
+                    修改
+                  </NButton>
+                  <NButton type="primary" text>
+                    学生调整
+                  </NButton>
+                  <NButton type="primary" text>
+                    开始上课
+                  </NButton>
+                  <NButton type="primary" text textColor="#EA4132">
+                    删除
+                  </NButton>
+                </NSpace>
+              </div>
+            );
+          }
         }
       ];
     };
@@ -86,7 +140,7 @@ export default defineComponent({
                       value: 'song1'
                     }
                   ],
-                  placeholder: '学生声部',
+                  placeholder: '全部年级',
                   clearable: true
                 } as any)}
                 v-model:value={state.orchestraType}></CSelect>
@@ -105,30 +159,12 @@ export default defineComponent({
                       value: 'song1'
                     }
                   ],
-                  placeholder: '年级班级',
-                  clearable: true
-                } as any)}
-                v-model:value={state.orchestraType}></CSelect>
-            </NFormItem>
-            <NFormItem>
-              <CSelect
-                {...({
-                  options: [
-                    {
-                      label:
-                        "Everybody's Got Something to Hide Except Me and My Monkey",
-                      value: 'song0'
-                    },
-                    {
-                      label: 'Drive My Car',
-                      value: 'song1'
-                    }
-                  ],
-                  placeholder: '学生类型',
+                  placeholder: '全部班级',
                   clearable: true
                 } as any)}
                 v-model:value={state.orchestraType}></CSelect>
             </NFormItem>
+
             <NFormItem>
               <NSpace justify="end">
                 <NButton type="primary" ghost class="resetBtn" onClick={search}>

+ 1 - 1
src/views/home/components/trainData.tsx

@@ -24,7 +24,7 @@ export default defineComponent({
         '2022-10-15',
         '2022-10-16'
       ],
-      studentList: [22, 23, 25, 26, 27, 6, 7],
+      studentList: [50, 100, 80, 180, 70, 99, 300],
       payInfoList: [100, 200, 300, 450, 330, 200, 10]
     });
 

+ 75 - 0
src/views/home/index.module.less

@@ -488,3 +488,78 @@
     }
   }
 }
+.indDot,
+.endDot {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+.indDot {
+  span {
+    border-radius: 50%;
+    width: 8px;
+    height: 8px;
+    background: #198cfe;
+    margin-right: 8px;
+  }
+}
+.endDot {
+  opacity: 0.7;
+  color: #aaaaaa;
+  span {
+    border-radius: 50%;
+    width: 8px;
+    height: 8px;
+    background: #aaaaaa;
+    margin-right: 8px;
+  }
+}
+.chioseModel {
+  width: 413px;
+  :global {
+    .n-select {
+      width: 100%;
+      min-width: 180px;
+      .n-base-selection-label {
+        height: 43px;
+        line-height: 43px;
+      }
+      .n-base-selection__border {
+        border-radius: 8px;
+        overflow: hidden;
+      }
+      .n-base-selection__state-border {
+        border-radius: 8px;
+        overflow: hidden;
+      }
+    }
+    .n-card-header {
+      position: relative;
+      padding: 20px 18px;
+      text-align: center;
+      // background: #F5F6FA;
+      font-size: 22px;
+      font-weight: 600;
+      color: #131415;
+      line-height: 30px;
+    }
+    .n-card-header__close {
+      position: absolute;
+      right: 18px;
+    }
+    .n-card__content {
+      padding-bottom: 30px;
+    }
+  }
+  .updateBtnGroup {
+    padding: 0;
+    justify-content: center !important;
+
+    :global {
+      .n-button {
+        height: 48px !important;
+        min-width: 156px;
+      }
+    }
+  }
+}

+ 91 - 14
src/views/home/index.tsx

@@ -1,7 +1,7 @@
 import { defineComponent, reactive } from 'vue';
 import styles from './index.module.less';
 import bannerPerson from './images/bannerPerson.png';
-import { NIcon, NImage, NDataTable } from 'naive-ui';
+import { NIcon, NImage, NDataTable, NButton, NModal } from 'naive-ui';
 import leftDot from './images/leftDot.png';
 import rightDot from './images/rightDot.png';
 import lineIcon from './images/lineIcon.png';
@@ -14,6 +14,7 @@ import cloundIcon from './images/cloundIcon.png';
 import goClass from './images/goClass.png';
 import boxIcon from './images/boxIcon.png';
 import TeachList from './components/teachList';
+import ChioseModal from './modals/chioseModal';
 export default defineComponent({
   name: 'home-page',
   setup() {
@@ -22,51 +23,116 @@ export default defineComponent({
       pagination: {
         page: 1,
         rows: 10,
-        pageTotal: 0
+        pageTotal: 4
       },
-      tableList: [] as any
+      tableList: [
+        {
+          teacherName: '孙忆枫',
+          createTime: '2023-06-27',
+          endTime: '2023-06-30',
+          status: 'ing',
+          studentNum: 100,
+          submitNum: 100,
+          quantityNum: 60,
+          submitRate: 100,
+          quantityRate: 60
+        },
+        {
+          teacherName: '孙忆枫',
+          createTime: '2023-06-27',
+          endTime: '2023-06-30',
+          status: 'ing',
+          studentNum: 100,
+          submitNum: 100,
+          quantityNum: 60,
+          submitRate: 100,
+          quantityRate: 60
+        },
+        {
+          teacherName: '孙忆枫',
+          createTime: '2023-06-27',
+          endTime: '2023-06-30',
+          status: 'ing',
+          studentNum: 100,
+          submitNum: 100,
+          quantityNum: 60,
+          submitRate: 100,
+          quantityRate: 60
+        },
+        {
+          teacherName: '孙忆枫',
+          createTime: '2023-06-25',
+          endTime: '2023-06-26',
+          status: 'end',
+          studentNum: 100,
+          submitNum: 100,
+          quantityNum: 60,
+          submitRate: 100,
+          quantityRate: 60
+        }
+      ] as any,
+      goCourseVisiable: false
     });
     const columns = () => {
       return [
         {
           title: '布置老师',
-          key: 'id'
+          key: 'teacherName'
         },
         {
           title: '布置时间',
-          key: 'id'
+          key: 'createTime'
         },
         {
           title: '截止时间',
-          key: 'id'
+          key: 'endTime'
         },
         {
           title: '训练状态',
-          key: 'id'
+          key: 'status',
+          render(row: any) {
+            return row.status == 'ing' ? (
+              <div class={styles.indDot}>
+                {' '}
+                <span></span> 进行中
+              </div>
+            ) : (
+              <div class={styles.endDot}>
+                <span></span>已结束
+              </div>
+            );
+          }
         },
         {
           title: '布置人数',
-          key: 'id'
+          key: 'studentNum'
         },
         {
           title: '提交人数',
-          key: 'id'
+          key: 'submitNum'
         },
         {
           title: '合格人数',
-          key: 'id'
+          key: 'quantityNum'
         },
         {
           title: '提交率',
-          key: 'id'
+          key: 'submitRate'
         },
         {
           title: '合格率',
-          key: 'id'
+          key: 'quantityRate'
         },
         {
           title: '操作',
-          key: 'id'
+          key: 'id',
+          render(row: any) {
+            return (
+              <NButton text type="primary">
+                详情
+              </NButton>
+            );
+          }
         }
       ];
     };
@@ -142,7 +208,9 @@ export default defineComponent({
                 <div class={styles.titleDot}></div>快捷入口
               </h3>
               <div class={styles.quickList}>
-                <div class={styles.quickItem}>
+                <div
+                  class={styles.quickItem}
+                  onClick={() => (state.goCourseVisiable = true)}>
                   <NImage
                     previewDisabled
                     class={styles.quickItemImg}
@@ -173,6 +241,15 @@ export default defineComponent({
             <TeachList></TeachList>
           </div>
         </div>
+
+        <NModal
+          v-model:show={state.goCourseVisiable}
+          preset="card"
+          class={styles.chioseModel}
+          title={'选择课件'}>
+          <ChioseModal
+            onClose={() => (state.goCourseVisiable = false)}></ChioseModal>
+        </NModal>
       </div>
     );
   }

+ 114 - 0
src/views/home/modals/chioseModal.tsx

@@ -0,0 +1,114 @@
+import { defineComponent, reactive } from 'vue';
+import styles from '../index.module.less';
+import { NButton, NForm, NFormItem, NSelect, NSpace } from 'naive-ui';
+import { useRouter } from 'vue-router';
+export default defineComponent({
+  name: 'train-update',
+  emits: ['close'],
+  setup(props, { emit }) {
+    // 'practice' | 'evaluation'
+    const forms = reactive({
+      version: null,
+      classId: null,
+      category: null,
+      chapter: null
+    });
+    const router = useRouter();
+    const gotoClassPage = () => {
+      const { href } = router.resolve({
+        path: '/attend-class'
+      });
+      window.open(href, +new Date() + '');
+    };
+    return () => (
+      <div class={styles.trainUpdate}>
+        <NForm labelAlign="left" labelPlacement="left">
+          <NFormItem path="classId">
+            <NSelect
+              {...({
+                options: [
+                  {
+                    label: '三年级2班',
+                    value: 'song0'
+                  },
+                  {
+                    label: '二年级1班',
+                    value: 'song1'
+                  }
+                ],
+                placeholder: '选择班级',
+                clearable: true
+              } as any)}
+              v-model:value={forms.classId}></NSelect>
+          </NFormItem>
+          <NFormItem path="version">
+            <NSelect
+              {...({
+                options: [
+                  {
+                    label: '人教版',
+                    value: 'song0'
+                  },
+                  {
+                    label: '人音版',
+                    value: 'song1'
+                  }
+                ],
+                placeholder: '选择教材版本',
+                clearable: true
+              } as any)}
+              v-model:value={forms.version}></NSelect>
+          </NFormItem>
+          <NFormItem path="category">
+            <NSelect
+              {...({
+                options: [
+                  {
+                    label: '一年级上册',
+                    value: 'song0'
+                  },
+                  {
+                    label: '一年级下册',
+                    value: 'song1'
+                  }
+                ],
+                placeholder: '选择册别',
+                clearable: true
+              } as any)}
+              v-model:value={forms.category}></NSelect>
+          </NFormItem>
+          <NFormItem path="chapter">
+            <NSelect
+              {...({
+                options: [
+                  {
+                    label: '第一单元|第一章',
+                    value: 'song0'
+                  },
+                  {
+                    label: '第一单元|第二章',
+                    value: 'song1'
+                  }
+                ],
+                placeholder: '选择章节',
+                clearable: true
+              } as any)}
+              v-model:value={forms.chapter}></NSelect>
+          </NFormItem>
+          <NSpace class={styles.updateBtnGroup}>
+            <NButton strong type="default" round onClick={() => emit('close')}>
+              取消
+            </NButton>
+            <NButton
+              strong
+              type="primary"
+              round
+              onClick={() => gotoClassPage()}>
+              确认
+            </NButton>
+          </NSpace>
+        </NForm>
+      </div>
+    );
+  }
+});

+ 171 - 0
src/views/login/components/codeLogin.tsx

@@ -0,0 +1,171 @@
+import { defineComponent, reactive, ref } from 'vue';
+import styles from '../index.module.less';
+import lockIcon from '../images/pwdIcon.png';
+import useIcon from '../images/phoneIcon.png';
+import {
+  useMessage,
+  NForm,
+  NFormItem,
+  NInput,
+  NButton,
+  NInputGroup
+} from 'naive-ui';
+import { useRoute, useRouter } from 'vue-router';
+import { PageEnum } from '/src/enums/pageEnum';
+import { storage } from '@/utils/storage';
+import { useUserStore } from '/src/store/modules/users';
+interface FormState {
+  username: string;
+  password: string;
+  grant_type: string;
+  loginType: string;
+  client_id: string;
+  client_secret: string;
+}
+
+export default defineComponent({
+  name: 'codeLogin',
+  setup(props, { emit }) {
+    const router = useRouter();
+    const route = useRoute();
+    const formRef = ref();
+    const message = useMessage();
+    const loading = ref(false);
+    const autoLogin = ref(true);
+    const LOGIN_NAME = PageEnum.BASE_LOGIN_NAME;
+    const showPwd = ref(false);
+    const userStore = useUserStore();
+    const formInline = reactive({
+      username: 'admin',
+      password: '123456',
+      isCaptcha: true
+    });
+
+    const handleSubmit = async () => {
+      formRef.value.validate(async (errors: any) => {
+        if (!errors) {
+          const { username, password } = formInline;
+          message.loading('登录中...');
+          loading.value = true;
+
+          const params: FormState = {
+            username,
+            password,
+            loginType: 'password',
+            grant_type: 'password',
+            client_id: 'jmedu-backend',
+            client_secret: 'jmedu-backend'
+          };
+
+          try {
+            await userStore.login(params);
+            // return;
+            message.destroyAll();
+            // if (some.code == ResultEnum.SUCCESS) {
+            //  判断是否勾选自动登录
+            if (autoLogin.value) {
+              storage.set('userInfo', JSON.stringify(formInline));
+            } else {
+              storage.remove('userInfo');
+            }
+
+            // route.query?.redirect ||
+            const toPath = decodeURIComponent('/' as string);
+
+            message.success('登录成功,即将进入系统');
+            if (route.name === LOGIN_NAME) {
+              router.replace('/');
+            } else router.replace(toPath);
+            // } else {
+            //   // message.info(some.msg || "登录失败");
+            // }
+          } catch (e: any) {
+            message.destroyAll();
+            loading.value = false;
+            message.error(e.msg);
+            console.log(e);
+          } finally {
+            loading.value = false;
+          }
+        }
+      });
+    };
+    return () => (
+      <div class={styles['view-account-form-wrap']}>
+        {/* <div class={styles.formTitle}>
+      <div class={styles.dot}></div>
+      酷乐秀课堂乐器
+    </div> */}
+        <NForm
+          ref={formRef}
+          label-placement="left"
+          size="large"
+          model={formInline}>
+          <NFormItem
+            path="username"
+            rule={[
+              { required: true, message: '请输入用户名', trigger: 'blur' }
+            ]}>
+            <NInput
+              v-model:value={formInline.username}
+              placeholder="请输入用户名">
+              {{
+                prefix: () => (
+                  <img src={useIcon} class={styles.prefixIcon} alt="" />
+                )
+              }}
+            </NInput>
+          </NFormItem>
+          <NFormItem
+            path="password"
+            rule={[{ required: true, message: '请输入密码', trigger: 'blur' }]}>
+            <NInputGroup>
+              <NInput
+                v-model:value={formInline.password}
+                type="text"
+                showPasswordOn="click"
+                placeholder="请输入密码"
+                inputProps={{ autocomplete: 'off' }}
+                class={styles.sendInput}
+                onKeydown={(e: KeyboardEvent) => {
+                  if (e.code === 'Enter') {
+                    handleSubmit();
+                  }
+                }}>
+                {{
+                  prefix: () => (
+                    <img src={lockIcon} class={styles.prefixIcon} alt="" />
+                  ),
+                  suffix: () => (
+                    <NButton class={styles.sendMsg}>发送短信</NButton>
+                  )
+                }}
+              </NInput>
+            </NInputGroup>
+          </NFormItem>
+
+          <NFormItem class={styles['default-color']}>
+            <div class={[styles['flex'], styles['justify-between']]}>
+              <div class={styles['flex-initial']}>
+                {/* <NCheckbox v-model:checked={autoLogin.value}>
+                  记住密码
+                </NCheckbox> */}
+              </div>
+            </div>
+          </NFormItem>
+          <NFormItem>
+            <NButton
+              class={styles.submitBtm}
+              type="primary"
+              onClick={handleSubmit}
+              size="large"
+              disabled={loading.value}
+              block>
+              立即登录
+            </NButton>
+          </NFormItem>
+        </NForm>
+      </div>
+    );
+  }
+});

+ 188 - 0
src/views/login/components/pwdLogin.tsx

@@ -0,0 +1,188 @@
+import { defineComponent, reactive, ref } from 'vue';
+import styles from '../index.module.less';
+import lockIcon from '../images/lock-icon.png';
+import useIcon from '../images/user-icon.png';
+import openEye from '../images/openEye.png';
+import closeEye from '../images/closeEye.png';
+import {
+  useMessage,
+  NForm,
+  NFormItem,
+  NInput,
+  NButton,
+  NCheckbox
+} from 'naive-ui';
+import { useRoute, useRouter } from 'vue-router';
+import { PageEnum } from '/src/enums/pageEnum';
+import { storage } from '@/utils/storage';
+import { useUserStore } from '/src/store/modules/users';
+
+interface FormState {
+  username: string;
+  password: string;
+  grant_type: string;
+  loginType: string;
+  client_id: string;
+  client_secret: string;
+}
+
+export default defineComponent({
+  name: 'codeLogin',
+  setup(props, { emit }) {
+    const router = useRouter();
+    const route = useRoute();
+    const formRef = ref();
+    const message = useMessage();
+    const loading = ref(false);
+    const autoLogin = ref(true);
+    const LOGIN_NAME = PageEnum.BASE_LOGIN_NAME;
+    const showPwd = ref(false);
+    const userStore = useUserStore();
+    const formInline = reactive({
+      username: 'admin',
+      password: '123456',
+      isCaptcha: true
+    });
+
+    const handleSubmit = async () => {
+      formRef.value.validate(async (errors: any) => {
+        if (!errors) {
+          const { username, password } = formInline;
+          message.loading('登录中...');
+          loading.value = true;
+
+          const params: FormState = {
+            username,
+            password,
+            loginType: 'password',
+            grant_type: 'password',
+            client_id: 'jmedu-backend',
+            client_secret: 'jmedu-backend'
+          };
+
+          try {
+            await userStore.login(params);
+            // return;
+            message.destroyAll();
+            // if (some.code == ResultEnum.SUCCESS) {
+            //  判断是否勾选自动登录
+            if (autoLogin.value) {
+              storage.set('userInfo', JSON.stringify(formInline));
+            } else {
+              storage.remove('userInfo');
+            }
+
+            // route.query?.redirect ||
+            const toPath = decodeURIComponent('/' as string);
+
+            message.success('登录成功,即将进入系统');
+            if (route.name === LOGIN_NAME) {
+              router.replace('/');
+            } else router.replace(toPath);
+            // } else {
+            //   // message.info(some.msg || "登录失败");
+            // }
+          } catch (e: any) {
+            message.destroyAll();
+            loading.value = false;
+            message.error(e.msg);
+            console.log(e);
+          } finally {
+            loading.value = false;
+          }
+        }
+      });
+    };
+    return () => (
+      <div class={styles['view-account-form-wrap']}>
+        {/* <div class={styles.formTitle}>
+      <div class={styles.dot}></div>
+      酷乐秀课堂乐器
+    </div> */}
+        <NForm
+          ref={formRef}
+          label-placement="left"
+          size="large"
+          model={formInline}>
+          <NFormItem
+            path="username"
+            rule={[
+              { required: true, message: '请输入用户名', trigger: 'blur' }
+            ]}>
+            <NInput
+              v-model:value={formInline.username}
+              placeholder="请输入用户名">
+              {{
+                prefix: () => (
+                  <img src={useIcon} class={styles.prefixIcon} alt="" />
+                )
+              }}
+            </NInput>
+          </NFormItem>
+          <NFormItem
+            path="password"
+            rule={[{ required: true, message: '请输入密码', trigger: 'blur' }]}>
+            <NInput
+              v-model:value={formInline.password}
+              type="text"
+              showPasswordOn="click"
+              placeholder="请输入密码"
+              inputProps={{ autocomplete: 'off' }}
+              class={[showPwd.value ? '' : styles['no-pwd']]}
+              onKeydown={(e: KeyboardEvent) => {
+                if (e.code === 'Enter') {
+                  handleSubmit();
+                }
+              }}>
+              {{
+                prefix: () => (
+                  <img src={lockIcon} class={styles.prefixIcon} alt="" />
+                ),
+                suffix: () => (
+                  <img
+                    src={showPwd.value ? closeEye : openEye}
+                    class={styles.pwdIcon}
+                    alt=""
+                    onClick={() => {
+                      showPwd.value = !showPwd.value;
+                    }}
+                  />
+                )
+              }}
+            </NInput>
+          </NFormItem>
+          <NFormItem class={styles['default-color']}>
+            <div class={[styles['flex'], styles['justify-between']]}>
+              <div class={styles['flex-initial']}>
+                <NCheckbox v-model:checked={autoLogin.value}>
+                  记住密码
+                </NCheckbox>
+              </div>
+            </div>
+          </NFormItem>
+          <NFormItem>
+            <NButton
+              class={styles.submitBtm}
+              type="primary"
+              onClick={handleSubmit}
+              size="large"
+              disabled={loading.value}
+              block>
+              立即登录
+            </NButton>
+          </NFormItem>
+          <NFormItem>
+            <NButton
+              text
+              class={styles.forgetBtm}
+              onClick={handleSubmit}
+              size="large"
+              block>
+              忘记密码
+            </NButton>
+          </NFormItem>
+        </NForm>
+      </div>
+    );
+  }
+});

BIN
src/views/login/images/colLogo.png


BIN
src/views/login/images/login-left.png


BIN
src/views/login/images/login_bg.png


BIN
src/views/login/images/login_styles.png


BIN
src/views/login/images/loginright.png


BIN
src/views/login/images/phoneIcon.png


BIN
src/views/login/images/pwdIcon.png


+ 135 - 49
src/views/login/index.module.less

@@ -1,5 +1,5 @@
 .no-pwd {
-  font-family: "dotfont";
+  font-family: 'dotfont';
 
   ::v-deep .n-input__input-el {
     -webkit-text-security: disc !important;
@@ -8,45 +8,67 @@
 }
 
 .view-account {
-  background: url("./images/login_bg.png") no-repeat 100% 100%;
-  background-size: 100%;
+  width: 100%;
+  height: 100vh;
   display: flex;
-  flex-direction: column;
-  min-height: 100vh;
-  width: 100vw;
-  background-color: #e9f7ff;
+  flex-direction: row;
+  align-items: center;
+  background-color: #ddefff;
   overflow: auto;
 
   &-container {
-    margin-top: 10%;
+    position: relative;
+    width: 1180px;
     display: flex;
-    flex-direction: row;
+    flex-direction: column;
     justify-content: center;
     align-items: center;
-
+    height: 100%;
     .stylesWrap {
-      width: 40%;
-      margin-right: 190px;
-
+      margin-top: 174px;
+      width: 955px;
       img {
-        width: 100%;
+        width: 955px;
         // height: 100%;
       }
     }
+    .loginLeft {
+      left: 0;
+      top: 0;
+      width: 139px;
+      height: 139px;
+      position: absolute;
+    }
+    .loginRight {
+      right: 0;
+      bottom: 0;
+      width: 194px;
+      height: 186px;
+      position: absolute;
+    }
   }
 
   &-form {
-    width: 30%;
-
+    width: 740px;
+    min-height: 100%;
+    background-color: #fff;
+    padding: 60px;
+    .colLogo {
+      width: 232px;
+      height: 79px;
+      margin-bottom: 110px;
+    }
     .view-account-form-wrap {
+      margin-top: 100px;
       position: relative;
-      padding: 12% 15% 12%;
       background: #ffffff;
       border-radius: 16px;
-
+      display: flex;
+      flex-direction: column;
+      align-items: center;
       .prefixIcon {
-        width: 18px;
-        height: 18px;
+        width: 22px;
+        height: 22px;
         margin-right: 5px;
       }
 
@@ -76,39 +98,103 @@
         }
       }
     }
-  }
-
-  .submitBtm {
-    margin-top: 20px;
-    background: #3594fa;
-    border-radius: 35px;
-    font-size: 22px;
-    font-family: PingFangSC-Semibold, PingFang SC;
-    font-weight: 600;
-    color: #ffffff;
-    height: 50px;
-    line-height: 50px;
-  }
+    .submitBtm {
+      width: 480px;
+      margin-top: 20px;
+      background: #198cfe;
+      border-radius: 12px;
+      font-size: 22px;
+      font-weight: 600 !important;
+      color: #ffffff;
+      height: 62px;
+      line-height: 62px;
+      text-align: center;
+    }
+    .forgetBtm {
+      height: 28px;
+      font-size: 20px;
+      font-weight: 600 !important;
+      color: #aaaaaa;
+      line-height: 28px;
+    }
+    .sendInput {
+      :global {
+        .n-input-wrapper {
+          padding-right: 0;
+        }
+      }
+      .sendMsg {
+        width: 128px;
+        height: 64px;
+        background: #198cfe;
+        border-radius: 0px 8px 8px 0px;
+        font-size: 20px;
+        font-family: PingFangSC-Medium, PingFang SC;
+        font-weight: 500;
+        color: #ffffff;
+        line-height: 62px;
+      }
+    }
 
-  &-top {
-    padding: 32px 0;
-    text-align: center;
+    &-top {
+      padding: 32px 0;
+      text-align: center;
 
-    &-desc {
-      font-size: 14px;
-      color: #808695;
+      &-desc {
+        font-size: 14px;
+        color: #808695;
+      }
     }
-  }
-
-  &-other {
-    width: 100%;
-  }
 
-  .default-color {
-    color: #515a6e;
+    &-other {
+      width: 100%;
+    }
 
-    .ant-checkbox-wrapper {
-      color: #515a6e;
+    .default-color {
+      color: #131415;
+      font-size: 18px;
+      .ant-checkbox-wrapper {
+        color: #515a6e;
+      }
+    }
+    .loginTabs {
+      :global {
+        .n-tabs-tab-pad {
+          width: 80px;
+        }
+        .n-tabs-tab__label {
+          font-size: 24px;
+          position: relative;
+          z-index: 100;
+          color: #8b8d98;
+        }
+        .n-tabs-tab--active {
+          .n-tabs-tab__label {
+            color: #131415;
+            font-weight: 600;
+          }
+        }
+        .n-tabs-bar {
+          width: 96px;
+          height: 10px;
+          bottom: 8px;
+          z-index: 0;
+          background: linear-gradient(
+            90deg,
+            #77bbff 0%,
+            rgba(163, 231, 255, 0.22) 100%
+          );
+        }
+        .n-input {
+          border-radius: 8px;
+          width: 480px;
+          font-size: 20px;
+        }
+        .n-input__input-el {
+          height: 64px;
+          line-height: 64px;
+        }
+      }
     }
   }
-}
+}

+ 22 - 173
src/views/login/index.tsx

@@ -1,189 +1,38 @@
 import { defineComponent, reactive, ref } from 'vue';
 import loginStyles from './images/login_styles.png';
-import lockIcon from './images/lock-icon.png';
-import useIcon from './images/user-icon.png';
-import openEye from './images/openEye.png';
-import closeEye from './images/closeEye.png';
-import { PageEnum } from '@/enums/pageEnum';
-import {
-  useMessage,
-  NForm,
-  NFormItem,
-  NInput,
-  NButton,
-  NCheckbox
-} from 'naive-ui';
-import { useUserStore } from '@/store/modules/users';
+import loginLeft from './images/login-left.png';
+import loginRight from './images/loginright.png';
+import colLogo from './images/colLogo.png';
+import CodeLogin from './components/codeLogin';
+import PwdLogin from './components/pwdLogin';
+import { NTabs, NTabPane } from 'naive-ui';
 import styles from './index.module.less';
-import { ResultEnum } from '@/enums/httpEnum';
-import { useRoute, useRouter } from 'vue-router';
-import { storage } from '@/utils/storage';
-
-interface FormState {
-  username: string;
-  password: string;
-  grant_type: string;
-  loginType: string;
-  client_id: string;
-  client_secret: string;
-}
 
 export default defineComponent({
   name: 'login-page',
   setup() {
-    const router = useRouter();
-    const route = useRoute();
-    const formRef = ref();
-    const message = useMessage();
-    const loading = ref(false);
-    const autoLogin = ref(true);
-    const LOGIN_NAME = PageEnum.BASE_LOGIN_NAME;
-    const showPwd = ref(false);
-    const formInline = reactive({
-      username: 'admin',
-      password: '123456',
-      isCaptcha: true
-    });
-    const userStore = useUserStore();
-
-    const handleSubmit = async () => {
-      formRef.value.validate(async (errors: any) => {
-        if (!errors) {
-          const { username, password } = formInline;
-          message.loading('登录中...');
-          loading.value = true;
-
-          const params: FormState = {
-            username,
-            password,
-            loginType: 'password',
-            grant_type: 'password',
-            client_id: 'jmedu-backend',
-            client_secret: 'jmedu-backend'
-          };
-
-          try {
-            await userStore.login(params);
-            // return;
-            message.destroyAll();
-            // if (some.code == ResultEnum.SUCCESS) {
-            //  判断是否勾选自动登录
-            if (autoLogin.value) {
-              storage.set('userInfo', JSON.stringify(formInline));
-            } else {
-              storage.remove('userInfo');
-            }
-
-            // route.query?.redirect ||
-            const toPath = decodeURIComponent('/' as string);
-
-            message.success('登录成功,即将进入系统');
-            if (route.name === LOGIN_NAME) {
-              router.replace('/');
-            } else router.replace(toPath);
-            // } else {
-            //   // message.info(some.msg || "登录失败");
-            // }
-          } catch (e: any) {
-            message.destroyAll();
-            loading.value = false;
-            message.error(e.msg);
-            console.log(e);
-          } finally {
-            loading.value = false;
-          }
-        }
-      });
-    };
     return () => (
       <div class={styles['view-account']}>
         <div class={styles['view-account-container']}>
+          <img src={loginLeft} class={styles.loginLeft} alt="" />
+          <img src={loginRight} class={styles.loginRight} alt="" />
           <div class={styles['stylesWrap']}>
             <img src={loginStyles} alt="" />
           </div>
-          <div class={styles['view-account-form']}>
-            <div class={styles['view-account-form-wrap']}>
-              <div class={styles.formTitle}>
-                <div class={styles.dot}></div>
-                酷乐秀课堂乐器
-              </div>
-              <NForm
-                ref={formRef}
-                label-placement="left"
-                size="large"
-                model={formInline}>
-                <NFormItem
-                  path="username"
-                  rule={[
-                    { required: true, message: '请输入用户名', trigger: 'blur' }
-                  ]}>
-                  <NInput
-                    v-model:value={formInline.username}
-                    placeholder="请输入用户名">
-                    {{
-                      prefix: () => (
-                        <img src={useIcon} class={styles.prefixIcon} alt="" />
-                      )
-                    }}
-                  </NInput>
-                </NFormItem>
-                <NFormItem
-                  path="password"
-                  rule={[
-                    { required: true, message: '请输入密码', trigger: 'blur' }
-                  ]}>
-                  <NInput
-                    v-model:value={formInline.password}
-                    type="text"
-                    showPasswordOn="click"
-                    placeholder="请输入密码"
-                    inputProps={{ autocomplete: 'off' }}
-                    class={[showPwd.value ? '' : styles['no-pwd']]}
-                    onKeydown={(e: KeyboardEvent) => {
-                      if (e.code === 'Enter') {
-                        handleSubmit();
-                      }
-                    }}>
-                    {{
-                      prefix: () => (
-                        <img src={lockIcon} class={styles.prefixIcon} alt="" />
-                      ),
-                      suffix: () => (
-                        <img
-                          src={showPwd.value ? closeEye : openEye}
-                          class={styles.pwdIcon}
-                          alt=""
-                          onClick={() => {
-                            showPwd.value = !showPwd.value;
-                          }}
-                        />
-                      )
-                    }}
-                  </NInput>
-                </NFormItem>
-                <NFormItem class={styles['default-color']}>
-                  <div class={[styles['flex'], styles['justify-between']]}>
-                    <div class={styles['flex-initial']}>
-                      <NCheckbox v-model:checked={autoLogin.value}>
-                        记住密码
-                      </NCheckbox>
-                    </div>
-                  </div>
-                </NFormItem>
-                <NFormItem>
-                  <NButton
-                    class={styles.submitBtm}
-                    type="primary"
-                    onClick={handleSubmit}
-                    size="large"
-                    loading={loading.value}
-                    block>
-                    登录
-                  </NButton>
-                </NFormItem>
-              </NForm>
-            </div>
-          </div>
+        </div>
+        <div class={styles['view-account-form']}>
+          <img class={styles.colLogo} src={colLogo}></img>
+          <NTabs
+            default-value="pwdLogin"
+            class={styles.loginTabs}
+            justify-content="center">
+            <NTabPane name="pwdLogin" tab="密码登录">
+              <PwdLogin></PwdLogin>
+            </NTabPane>
+            <NTabPane name="codeLogin" tab="短信验证">
+              <CodeLogin></CodeLogin>
+            </NTabPane>
+          </NTabs>
         </div>
       </div>
     );

+ 1 - 0
src/views/setting/components/personInfo.tsx

@@ -70,6 +70,7 @@ export default defineComponent({
             </NGrid>
           </NForm>
         </div>
+        <div class={styles.btnList}></div>
       </div>
     );
   }

+ 2 - 0
src/views/studentList/index.module.less

@@ -1,6 +1,8 @@
 .listWrap {
   padding: 32px;
   background-color: #fff;
+  border-radius: 20px;
+  min-height: 100%;
 }
 .addBtnIcon {
   width: 16px;

+ 70 - 12
src/views/studentList/index.tsx

@@ -24,9 +24,42 @@ export default defineComponent({
       pagination: {
         page: 1,
         rows: 10,
-        pageTotal: 0
+        pageTotal: 4
       },
-      tableList: [] as any
+      tableList: [
+        {
+          studentName: '胡小小',
+          phone: '17625367893',
+          sex: '0',
+          className: '一年级3班',
+          classType: 'normal',
+          studentType: 'member'
+        },
+        {
+          studentName: '丁曼蓉',
+          phone: '14677789334',
+          sex: '1',
+          className: '一年级3班',
+          classType: 'normal',
+          studentType: ''
+        },
+        {
+          studentName: '李书意',
+          phone: '13467857893',
+          sex: '1',
+          className: '一年级3班',
+          classType: 'graduate',
+          studentType: 'member'
+        },
+        {
+          studentName: '夏小满',
+          phone: '13925367893',
+          sex: '0',
+          className: '一年级3班',
+          classType: 'none',
+          studentType: ''
+        }
+      ] as any
     });
     const search = () => {
       console.log('search', state);
@@ -43,31 +76,56 @@ export default defineComponent({
       return [
         {
           title: '姓名',
-          key: 'id'
+          key: 'studentName'
         },
         {
           title: '手机号',
-          key: 'id'
+          key: 'phone'
         },
         {
           title: '性别',
-          key: 'id'
-        },
-        {
-          title: '乐器',
-          key: 'id'
+          key: 'sex',
+          render(row: any) {
+            return <>{row.sex == '0' ? '女' : '男'}</>;
+          }
         },
+
         {
           title: '班级',
-          key: 'id'
+          key: 'className',
+          render(row: any) {
+            return (
+              <>
+                <div>
+                  {row.classType == 'none' ? (
+                    <p style={{ color: '#EA4132' }}>{'未在班级'}</p>
+                  ) : null}
+                  {row.classType == 'graduate' ? (
+                    <p style={{ color: '#AAAAAA' }}>{'毕业'}</p>
+                  ) : null}
+                  {row.classType == 'normal' ? <p>{row.className}</p> : null}
+                </div>
+              </>
+            );
+          }
         },
         {
           title: '学生类型',
-          key: 'id'
+          key: 'studentType',
+          render(row: any) {
+            return <>{row.studentType == 'member' ? '会员' : '普通'}</>;
+          }
         },
         {
           title: '操作',
-          key: 'id'
+          key: 'id',
+          render(row: any) {
+            return (
+              <NButton text type="primary">
+                详情
+              </NButton>
+            );
+          }
         }
       ];
     };