develoop
ww 5 months ago
parent 1f7a44466c
commit f88b793800
  1. 4
      package.json
  2. 438
      pnpm-lock.yaml
  3. 14
      src/App.vue
  4. 2
      src/api/user/index.ts
  5. 48
      src/api/user/user.js
  6. 4
      src/layout/main/index.vue
  7. 2
      src/layout/menu/index.vue
  8. 2
      src/main.ts
  9. 115
      src/permission.ts
  10. 3
      src/store/index.ts
  11. 1
      src/store/modules/type/types.ts
  12. 158
      src/store/modules/user.ts
  13. 25
      src/utils/img.js
  14. 1
      src/utils/request.ts
  15. 13
      src/views/MyCourseStudy/Courselikes.vue
  16. 13
      src/views/MyCourseStudy/courseCollections.vue
  17. 13
      src/views/MyCourseStudy/learningProcess.vue
  18. 13
      src/views/course/CourseObjectives.vue
  19. 13
      src/views/course/curriculumMap.vue
  20. 180
      src/views/group/index.vue
  21. 86
      src/views/home/components/Class.vue
  22. 24
      src/views/home/components/ConHeader.vue
  23. 24
      src/views/home/components/Echarts.vue
  24. 159
      src/views/home/components/Info.vue
  25. 146
      src/views/home/components/Status.vue
  26. 77
      src/views/home/components/Student.vue
  27. 11
      src/views/home/components/Welcome.vue
  28. 27
      src/views/home/index.vue
  29. 506
      src/views/login/index.vue
  30. 108
      src/views/message/index.vue
  31. 175
      src/views/portal/courseHomepage.vue
  32. 533
      src/views/student/index.vue
  33. 68
      src/views/student/login.vue

@ -19,9 +19,12 @@
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@vueuse/core": "^10.9.0",
"ali-oss": "^6.20.0",
"axios": "^1.6.8",
"echarts": "^5.5.0",
"element-plus": "^2.6.0",
"jwt-decode": "^4.0.0",
"nanoid": "^5.0.7",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"vue": "^3.4.19",
@ -39,6 +42,7 @@
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.22.0",
"mockjs": "^1.1.0",
"pinia-plugin-persistedstate": "^3.2.1",
"postcss": "^8.4.35",
"postcss-html": "^1.6.0",
"postcss-scss": "^4.0.9",

@ -11,6 +11,9 @@ dependencies:
'@vueuse/core':
specifier: ^10.9.0
version: 10.10.0(vue@3.4.27)
ali-oss:
specifier: ^6.20.0
version: 6.20.0
axios:
specifier: ^1.6.8
version: 1.7.2
@ -20,6 +23,12 @@ dependencies:
element-plus:
specifier: ^2.6.0
version: 2.7.4(vue@3.4.27)
jwt-decode:
specifier: ^4.0.0
version: 4.0.0
nanoid:
specifier: ^5.0.7
version: 5.0.7
nprogress:
specifier: ^0.2.0
version: 0.2.0
@ -67,6 +76,9 @@ devDependencies:
mockjs:
specifier: ^1.1.0
version: 1.1.0
pinia-plugin-persistedstate:
specifier: ^3.2.1
version: 3.2.1(pinia@2.1.7)
postcss:
specifier: ^8.4.35
version: 8.4.38
@ -1318,7 +1330,6 @@ packages:
/@vue/devtools-api@6.6.2:
resolution: {integrity: sha512-134clD8u7cBBXdmBbXI282gHGF7T/eAbD/G7mAK2llQF62IbI4ny28IVamZVMoJSvfImC2Xxnj732hXkJvUj6g==}
dev: false
/@vue/language-core@1.8.27(typescript@5.4.5):
resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==}
@ -1434,6 +1445,18 @@ packages:
hasBin: true
dev: true
/address@1.2.2:
resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==}
engines: {node: '>= 10.0.0'}
dev: false
/agentkeepalive@3.5.3:
resolution: {integrity: sha512-yqXL+k5rr8+ZRpOAntkaaRgWgE5o8ESAj5DyRmVTCSoZxXmqemb9Dd7T4i5UzwuERdLAJUy6XzR9zFVuf0kzkw==}
engines: {node: '>= 4.0.0'}
dependencies:
humanize-ms: 1.2.1
dev: false
/ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
dependencies:
@ -1452,6 +1475,40 @@ packages:
uri-js: 4.4.1
dev: true
/ali-oss@6.20.0:
resolution: {integrity: sha512-TzFXgGlw81sy2JvcCveSYsa2b2+6kv+HA6WTc+cXg6bu8nUAmVPfncRGbn3x2getSOniOFA+TyGy3V4l3Fks+Q==}
engines: {node: '>=8'}
dependencies:
address: 1.2.2
agentkeepalive: 3.5.3
bowser: 1.9.4
copy-to: 2.0.1
dateformat: 2.2.0
debug: 4.3.5
destroy: 1.2.0
end-or-error: 1.0.1
get-ready: 1.0.0
humanize-ms: 1.2.1
is-type-of: 1.4.0
js-base64: 2.6.4
jstoxml: 2.2.9
lodash: 4.17.21
merge-descriptors: 1.0.3
mime: 2.6.0
platform: 1.3.6
pump: 3.0.0
qs: 6.12.1
sdk-base: 2.0.1
stream-http: 2.8.2
stream-wormhole: 1.1.0
urllib: 2.41.0
utility: 1.18.0
xml2js: 0.6.2
transitivePeerDependencies:
- proxy-agent
- supports-color
dev: false
/ansi-regex@2.1.1:
resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==}
engines: {node: '>=0.10.0'}
@ -1486,6 +1543,10 @@ packages:
color-convert: 2.0.1
dev: true
/any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
dev: false
/anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
@ -1668,6 +1729,10 @@ packages:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
dev: true
/bowser@1.9.4:
resolution: {integrity: sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==}
dev: false
/brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
@ -1717,6 +1782,10 @@ packages:
update-browserslist-db: 1.0.16(browserslist@4.23.0)
dev: true
/builtin-status-codes@3.0.0:
resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==}
dev: false
/bundle-require@4.1.0(esbuild@0.21.4):
resolution: {integrity: sha512-FeArRFM+ziGkRViKRnSTbHZc35dgmR9yNog05Kn0+ItI59pOAISGvnnIwW1WgFZQW59IxD9QpJnUPkdIPfZuXg==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@ -1751,7 +1820,6 @@ packages:
function-bind: 1.1.2
get-intrinsic: 1.2.4
set-function-length: 1.2.2
dev: true
/callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
@ -1894,6 +1962,11 @@ packages:
- supports-color
dev: true
/content-type@1.0.5:
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
engines: {node: '>= 0.6'}
dev: false
/convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
dev: true
@ -1903,6 +1976,14 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/copy-to@2.0.1:
resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==}
dev: false
/core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
dev: false
/cors@2.8.5:
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
engines: {node: '>= 0.10'}
@ -2015,6 +2096,10 @@ packages:
is-data-view: 1.0.1
dev: true
/dateformat@2.2.0:
resolution: {integrity: sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw==}
dev: false
/dayjs@1.11.11:
resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==}
dev: false
@ -2032,7 +2117,6 @@ packages:
optional: true
dependencies:
ms: 2.0.0
dev: true
/debug@3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
@ -2055,7 +2139,6 @@ packages:
optional: true
dependencies:
ms: 2.1.2
dev: true
/decode-uri-component@0.2.2:
resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
@ -2066,6 +2149,13 @@ packages:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
dev: true
/default-user-agent@1.0.0:
resolution: {integrity: sha512-bDF7bg6OSNcSwFWPu4zYKpVkJZQYVrAANMYB8bc9Szem1D0yKdm4sa/rOCs2aC9+2GMqQ7KnwtZRvDhmLF0dXw==}
engines: {node: '>= 0.10.0'}
dependencies:
os-name: 1.0.3
dev: false
/define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
@ -2073,7 +2163,6 @@ packages:
es-define-property: 1.0.0
es-errors: 1.3.0
gopd: 1.0.1
dev: true
/define-properties@1.2.1:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
@ -2111,6 +2200,16 @@ packages:
engines: {node: '>=0.4.0'}
dev: false
/destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
dev: false
/digest-header@1.1.0:
resolution: {integrity: sha512-glXVh42vz40yZb9Cq2oMOt70FIoWiv+vxNvdKdU8CwjLad25qHM3trLxhl9bVjdr6WaslIXhWpn0NO8T/67Qjg==}
engines: {node: '>= 8.0.0'}
dev: false
/dir-glob@3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'}
@ -2215,7 +2314,6 @@ packages:
/ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
dev: true
/electron-to-chromium@1.4.787:
resolution: {integrity: sha512-d0EFmtLPjctczO3LogReyM2pbBiiZbnsKnGF+cdZhsYzHm/A0GV7W94kqzLD8SN4O3f3iHlgLUChqghgyznvCQ==}
@ -2260,6 +2358,17 @@ packages:
engines: {node: '>= 0.8'}
dev: true
/end-of-stream@1.4.4:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
dependencies:
once: 1.4.0
dev: false
/end-or-error@1.0.1:
resolution: {integrity: sha512-OclLMSug+k2A0JKuf494im25ANRBVW8qsjmwbgX7lQ8P82H21PQ1PWkoYwb9y5yMBS69BPlwtzdIFClo3+7kOQ==}
engines: {node: '>= 0.11.14'}
dev: false
/entities@1.1.2:
resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==}
dev: true
@ -2340,12 +2449,10 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
get-intrinsic: 1.2.4
dev: true
/es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
dev: true
/es-object-atoms@1.0.0:
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
@ -2758,7 +2865,6 @@ packages:
engines: {node: '>=0.10.0'}
dependencies:
is-extendable: 0.1.1
dev: true
/extend-shallow@3.0.2:
resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==}
@ -2927,6 +3033,15 @@ packages:
mime-types: 2.1.35
dev: false
/formstream@1.5.1:
resolution: {integrity: sha512-q7ORzFqotpwn3Y/GBK2lK7PjtZZwJHz9QE9Phv8zb5IrL9ftGLyi2zjGURON3voK8TaZ+mqJKERYN4lrHYTkUQ==}
dependencies:
destroy: 1.2.0
mime: 2.6.0
node-hex: 1.0.1
pause-stream: 0.0.11
dev: false
/fragment-cache@0.2.1:
resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==}
engines: {node: '>=0.10.0'}
@ -2957,7 +3072,6 @@ packages:
/function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
dev: true
/function.prototype.name@1.1.6:
resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
@ -2987,7 +3101,10 @@ packages:
has-proto: 1.0.3
has-symbols: 1.0.3
hasown: 2.0.2
dev: true
/get-ready@1.0.0:
resolution: {integrity: sha512-mFXCZPJIlcYcth+N8267+mghfYN9h3EhsDa6JSnbA3Wrhh/XFpuowviFcsDeYZtKspQyWyJqfs4O6P8CHeTwzw==}
dev: false
/get-symbol-description@1.0.2:
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
@ -3085,7 +3202,6 @@ packages:
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
dependencies:
get-intrinsic: 1.2.4
dev: true
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
@ -3125,17 +3241,14 @@ packages:
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
dependencies:
es-define-property: 1.0.0
dev: true
/has-proto@1.0.3:
resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
engines: {node: '>= 0.4'}
dev: true
/has-symbols@1.0.3:
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
engines: {node: '>= 0.4'}
dev: true
/has-tostringtag@1.0.2:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
@ -3180,7 +3293,6 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
function-bind: 1.1.2
dev: true
/he@1.2.0:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
@ -3212,6 +3324,19 @@ packages:
entities: 4.5.0
dev: true
/humanize-ms@1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
dependencies:
ms: 2.1.3
dev: false
/iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
dependencies:
safer-buffer: 2.1.2
dev: false
/ignore@5.3.1:
resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
engines: {node: '>= 4'}
@ -3250,7 +3375,6 @@ packages:
/inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
dev: true
/ini@1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
@ -3265,6 +3389,10 @@ packages:
side-channel: 1.0.6
dev: true
/ip@1.1.9:
resolution: {integrity: sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==}
dev: false
/is-accessor-descriptor@1.0.1:
resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==}
engines: {node: '>= 0.10'}
@ -3314,6 +3442,10 @@ packages:
engines: {node: '>= 0.4'}
dev: true
/is-class-hotfix@0.0.6:
resolution: {integrity: sha512-0n+pzCC6ICtVr/WXnN2f03TK/3BfXY7me4cjCAqT8TYXEl0+JBRoqBo94JJHXcyDSLUeWbNX8Fvy5g5RJdAstQ==}
dev: false
/is-core-module@2.13.1:
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
dependencies:
@ -3360,7 +3492,6 @@ packages:
/is-extendable@0.1.1:
resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
engines: {node: '>=0.10.0'}
dev: true
/is-extendable@1.0.1:
resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==}
@ -3461,6 +3592,14 @@ packages:
has-symbols: 1.0.3
dev: true
/is-type-of@1.4.0:
resolution: {integrity: sha512-EddYllaovi5ysMLMEN7yzHEKh8A850cZ7pykrY1aNRQGn/CDjRDE9qEWbIdt7xGEVJmjBXzU/fNnC4ABTm8tEQ==}
dependencies:
core-util-is: 1.0.3
is-class-hotfix: 0.0.6
isstream: 0.1.2
dev: false
/is-typed-array@1.1.13:
resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
engines: {node: '>= 0.4'}
@ -3481,7 +3620,6 @@ packages:
/isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
dev: true
/isarray@2.0.5:
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
@ -3503,9 +3641,12 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/isstream@0.1.2:
resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
dev: false
/js-base64@2.6.4:
resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
dev: true
/js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@ -3569,6 +3710,15 @@ packages:
graceful-fs: 4.2.11
dev: true
/jstoxml@2.2.9:
resolution: {integrity: sha512-OYWlK0j+roh+eyaMROlNbS5cd5R25Y+IUpdl7cNdB8HNrkgwQzIS7L9MegxOiWNBj9dQhA/yAxiMwCC5mwNoBw==}
dev: false
/jwt-decode@4.0.0:
resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==}
engines: {node: '>=18'}
dev: false
/keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
dependencies:
@ -3711,6 +3861,10 @@ packages:
engines: {node: '>=18'}
dev: true
/merge-descriptors@1.0.3:
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
dev: false
/merge-options@1.0.1:
resolution: {integrity: sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==}
engines: {node: '>=4'}
@ -3764,6 +3918,12 @@ packages:
mime-db: 1.52.0
dev: false
/mime@2.6.0:
resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
engines: {node: '>=4.0.0'}
hasBin: true
dev: false
/minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
dependencies:
@ -3779,7 +3939,6 @@ packages:
/minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
dev: true
/mixin-deep@1.3.2:
resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==}
@ -3789,6 +3948,13 @@ packages:
is-extendable: 1.0.1
dev: true
/mkdirp@0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true
dependencies:
minimist: 1.2.8
dev: false
/mockjs@1.1.0:
resolution: {integrity: sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==}
hasBin: true
@ -3798,25 +3964,36 @@ packages:
/ms@2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
dev: true
/ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
/ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
dev: true
/muggle-string@0.3.1:
resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==}
dev: true
/mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
dependencies:
any-promise: 1.3.0
object-assign: 4.1.1
thenify-all: 1.6.0
dev: false
/nanoid@3.3.7:
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
/nanoid@5.0.7:
resolution: {integrity: sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==}
engines: {node: ^18 || >=20}
hasBin: true
dev: false
/nanomatch@1.2.13:
resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==}
engines: {node: '>=0.10.0'}
@ -3844,6 +4021,11 @@ packages:
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
dev: true
/node-hex@1.0.1:
resolution: {integrity: sha512-iwpZdvW6Umz12ICmu9IYPRxg0tOLGmU3Tq2tKetejCj3oZd7b2nUXwP3a7QA5M9glWy8wlPS1G3RwM/CdsUbdQ==}
engines: {node: '>=8.0.0'}
dev: false
/node-releases@2.0.14:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
dev: true
@ -3870,7 +4052,6 @@ packages:
/object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
dev: true
/object-copy@0.1.0:
resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==}
@ -3883,7 +4064,6 @@ packages:
/object-inspect@1.13.1:
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
dev: true
/object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
@ -3953,7 +4133,6 @@ packages:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
dependencies:
wrappy: 1.0.2
dev: true
/optionator@0.9.4:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
@ -3967,6 +4146,23 @@ packages:
word-wrap: 1.2.5
dev: true
/os-name@1.0.3:
resolution: {integrity: sha512-f5estLO2KN8vgtTRaILIgEGBoBrMnZ3JQ7W9TMZCnOIGwHe8TRGSpcagnWDo+Dfhd/z08k9Xe75hvciJJ8Qaew==}
engines: {node: '>=0.10.0'}
hasBin: true
dependencies:
osx-release: 1.1.0
win-release: 1.1.1
dev: false
/osx-release@1.1.0:
resolution: {integrity: sha512-ixCMMwnVxyHFQLQnINhmIpWqXIfS2YOXchwQrk+OFzmo6nDjQ0E4KXAyyUh0T0MZgV4bUhkRrAbVqlE4yLVq4A==}
engines: {node: '>=0.10.0'}
hasBin: true
dependencies:
minimist: 1.2.8
dev: false
/p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
@ -4044,6 +4240,12 @@ packages:
resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==}
dev: true
/pause-stream@0.0.11:
resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==}
dependencies:
through: 2.3.8
dev: false
/picocolors@1.0.1:
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
@ -4052,6 +4254,14 @@ packages:
engines: {node: '>=8.6'}
dev: true
/pinia-plugin-persistedstate@3.2.1(pinia@2.1.7):
resolution: {integrity: sha512-MK++8LRUsGF7r45PjBFES82ISnPzyO6IZx3CH5vyPseFLZCk1g2kgx6l/nW8pEBKxxd4do0P6bJw+mUSZIEZUQ==}
peerDependencies:
pinia: ^2.0.0
dependencies:
pinia: 2.1.7(typescript@5.4.5)(vue@3.4.27)
dev: true
/pinia@2.1.7(typescript@5.4.5)(vue@3.4.27):
resolution: {integrity: sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==}
peerDependencies:
@ -4068,6 +4278,9 @@ packages:
typescript: 5.4.5
vue: 3.4.27(typescript@5.4.5)
vue-demi: 0.14.8(vue@3.4.27)
/platform@1.3.6:
resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==}
dev: false
/posix-character-classes@0.1.1:
@ -4224,15 +4437,33 @@ packages:
hasBin: true
dev: true
/process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
dev: false
/proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
dev: false
/pump@3.0.0:
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
dependencies:
end-of-stream: 1.4.4
once: 1.4.0
dev: false
/punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
dev: true
/qs@6.12.1:
resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==}
engines: {node: '>=0.6'}
dependencies:
side-channel: 1.0.6
dev: false
/query-string@4.3.4:
resolution: {integrity: sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==}
engines: {node: '>=0.10.0'}
@ -4245,6 +4476,18 @@ packages:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true
/readable-stream@2.3.8:
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
dependencies:
core-util-is: 1.0.3
inherits: 2.0.4
isarray: 1.0.0
process-nextick-args: 2.0.1
safe-buffer: 5.1.2
string_decoder: 1.1.1
util-deprecate: 1.0.2
dev: false
/readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
@ -4383,6 +4626,10 @@ packages:
isarray: 2.0.5
dev: true
/safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
dev: false
/safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
dev: true
@ -4402,6 +4649,10 @@ packages:
ret: 0.1.15
dev: true
/safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
dev: false
/sass-loader@14.2.1(sass@1.77.4):
resolution: {integrity: sha512-G0VcnMYU18a4N7VoNDegg2OuMjYtxnqzQWARVWCIVSZwJeiL9kg8QMsuIZOplsJgTzZLF6jGxI3AClj8I9nRdQ==}
engines: {node: '>= 18.12.0'}
@ -4437,6 +4688,21 @@ packages:
source-map-js: 1.2.0
dev: true
/sax@1.4.1:
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
dev: false
/sdk-base@2.0.1:
resolution: {integrity: sha512-eeG26wRwhtwYuKGCDM3LixCaxY27Pa/5lK4rLKhQa7HBjJ3U3Y+f81MMZQRsDw/8SC2Dao/83yJTXJ8aULuN8Q==}
dependencies:
get-ready: 1.0.0
dev: false
/semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true
dev: false
/semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
@ -4458,7 +4724,6 @@ packages:
get-intrinsic: 1.2.4
gopd: 1.0.1
has-property-descriptors: 1.0.2
dev: true
/set-function-name@2.0.2:
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
@ -4500,7 +4765,6 @@ packages:
es-errors: 1.3.0
get-intrinsic: 1.2.4
object-inspect: 1.13.1
dev: true
/signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
@ -4606,7 +4870,21 @@ packages:
/statuses@1.5.0:
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
engines: {node: '>= 0.6'}
dev: true
/stream-http@2.8.2:
resolution: {integrity: sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==}
dependencies:
builtin-status-codes: 3.0.0
inherits: 2.0.4
readable-stream: 2.3.8
to-arraybuffer: 1.0.1
xtend: 4.0.2
dev: false
/stream-wormhole@1.1.0:
resolution: {integrity: sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==}
engines: {node: '>=4.0.0'}
dev: false
/strict-uri-encode@1.1.0:
resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==}
@ -4649,6 +4927,12 @@ packages:
es-object-atoms: 1.0.0
dev: true
/string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
dependencies:
safe-buffer: 5.1.2
dev: false
/string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
dependencies:
@ -4969,6 +5253,27 @@ packages:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
dev: true
/thenify-all@1.6.0:
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
engines: {node: '>=0.8'}
dependencies:
thenify: 3.3.1
dev: false
/thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
dependencies:
any-promise: 1.3.0
dev: false
/through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
dev: false
/to-arraybuffer@1.0.1:
resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==}
dev: false
/to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
@ -5126,6 +5431,13 @@ packages:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
dev: true
/unescape@1.0.1:
resolution: {integrity: sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==}
engines: {node: '>=0.10.0'}
dependencies:
extend-shallow: 2.0.1
dev: false
/union-value@1.0.1:
resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==}
engines: {node: '>=0.10.0'}
@ -5176,6 +5488,33 @@ packages:
deprecated: Please see https://github.com/lydell/urix#deprecated
dev: true
/urllib@2.41.0:
resolution: {integrity: sha512-pNXdxEv52L67jahLT+/7QE+Fup1y2Gc6EdmrAhQ6OpQIC2rl14oWwv9hvk1GXOZqEnJNwRXHABuwgPOs1CtL7g==}
engines: {node: '>= 0.10.0'}
peerDependencies:
proxy-agent: ^5.0.0
peerDependenciesMeta:
proxy-agent:
optional: true
dependencies:
any-promise: 1.3.0
content-type: 1.0.5
debug: 2.6.9
default-user-agent: 1.0.0
digest-header: 1.1.0
ee-first: 1.1.1
formstream: 1.5.1
humanize-ms: 1.2.1
iconv-lite: 0.4.24
ip: 1.1.9
pump: 3.0.0
qs: 6.12.1
statuses: 1.5.0
utility: 1.18.0
transitivePeerDependencies:
- supports-color
dev: false
/use@3.1.1:
resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==}
engines: {node: '>=0.10.0'}
@ -5183,7 +5522,17 @@ packages:
/util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: true
/utility@1.18.0:
resolution: {integrity: sha512-PYxZDA+6QtvRvm//++aGdmKG/cI07jNwbROz0Ql+VzFV1+Z0Dy55NI4zZ7RHc9KKpBePNFwoErqIuqQv/cjiTA==}
engines: {node: '>= 0.12.0'}
dependencies:
copy-to: 2.0.1
escape-html: 1.0.3
mkdirp: 0.5.6
mz: 2.7.0
unescape: 1.0.1
dev: false
/utils-merge@1.0.1:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
@ -5284,7 +5633,6 @@ packages:
optional: true
dependencies:
vue: 3.4.27(typescript@5.4.5)
dev: false
/vue-eslint-parser@9.4.3(eslint@8.57.0):
resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==}
@ -5383,6 +5731,13 @@ packages:
isexe: 2.0.0
dev: true
/win-release@1.1.1:
resolution: {integrity: sha512-iCRnKVvGxOQdsKhcQId2PXV1vV3J/sDPXKA4Oe9+Eti2nb2ESEsYHRYls/UjoUW3bIc5ZDO8dTH50A/5iVN+bw==}
engines: {node: '>=0.10.0'}
dependencies:
semver: 5.7.2
dev: false
/word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
@ -5390,7 +5745,6 @@ packages:
/wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
dev: true
/write-file-atomic@5.0.1:
resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}
@ -5405,6 +5759,24 @@ packages:
engines: {node: '>=12'}
dev: true
/xml2js@0.6.2:
resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
engines: {node: '>=4.0.0'}
dependencies:
sax: 1.4.1
xmlbuilder: 11.0.1
dev: false
/xmlbuilder@11.0.1:
resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
engines: {node: '>=4.0'}
dev: false
/xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
dev: false
/yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
dev: true

@ -3,7 +3,19 @@
</template>
<script lang="ts" setup>
import {} from 'vue'
// import { useRouter, useRoute } from 'vue-router'
// vue3 compositionAPI
// 1. router useRouter
// const router = userRouter()
// 2. route useRoute()
// const route = useRooute()
// const router = useRouter()
// const goList = () => {
// router.push('/')
// }
// import {} from 'vue'
// import { useUserStore } from '@/stores/user.js'
// const userStore = useUserStore()
</script>
<style lang="scss" scoped></style>

@ -4,9 +4,7 @@ import { loginType, loginResponseType, userInfoType } from './types'
enum API {
LOGIN_URL = '/user/login',
USERINFO_URL = '/user/info',
LOGOUT_URL = '/user/logout',
}
// 用户登录

@ -0,0 +1,48 @@
import request from '@/utils/request'
//账号登录接口
export const userLoginService = (loginData) => {
return request.post('/user/login', loginData, {
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
}
//手机号登录接口
export const userPhoneLoginService = (phones) => {
return request.post('/user/phone/login?phone=' + phones)
}
//账号注册接口
export const userRegisterService = (jsonData) => {
return request.post('/user/register', jsonData, {
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
}
//手机号注册接口
export const userPhoneRegisterService = (jsonData) => {
return request.post('/user/phone/register', jsonData, {
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
}
//手机号验证码请求
export const userCodeRegisterService = (phoness) => {
return request.post('/user/code?phone=' + phoness)
}
//用户信息接口
export const userGetInfoService = (token) =>
request.get('/user/info', {
headers: { Authorization: `Bearer ${token}` },
})
//修改用户信息
export const userChangeService = (jsonData) => {
return request.post('/user/update', jsonData, {
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
}
//获取身份信息
export const userIdenService = (id) => {
return request.post('/user/personal/info?id=' + id)
}
//修改身份信息
export const userIdenChangeService = (jsonData) => {
return request.post('', jsonData, {
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})
}

@ -3,8 +3,8 @@
<!-- 过度动画 -->
<transition name="fade">
<!-- 要渲染的组件 -->
<div v-if="refresh" >
<component :is="Component"/>
<div v-if="refresh">
<component :is="Component" />
</div>
</transition>
</router-view>

@ -76,4 +76,4 @@ export default {
name: 'Menu',
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped></style>

@ -19,6 +19,8 @@ import 'virtual:svg-icons-register'
// 获取并创建实例对象
const app = createApp(App)
// 引入自动注册全局组件脚本
//持久化
import gloablComponent from '@/components/index'
app.use(gloablComponent)
// 注册仓库

@ -9,64 +9,65 @@ import 'nprogress/nprogress.css'
const userStore = useUserStore(pinia)
const usePermissionStore = permissionStore(pinia)
// const whitelist = ['/login', '/404']
// router.beforeEach(async (to, form, next) => {
// // 进度条开始\
// nprogress.configure({ showSpinner: false })
// nprogress.start()
// // 判断是否登录
// if (userStore.token) {
// // 登录成功访问登录页则跳转到首页
// if (to.path == '/login') {
// next({ path: '/' })
// } else {
// // 登录成功判断是否获取到了用户信息
// if (userStore.userName) {
// next()
// } else {
// try {
// // 没有获取到用户信息 就获取用户信息 然后放行
// await userStore.getUserInfo()
// // 获取筛选到的路由
// const asyncRouter = await usePermissionStore.getAsyncRoutes(
// userStore.routes,
// )
// // 遍历筛选出来的路由通过addRoute添加到路由表
// asyncRouter.forEach((item: any) => {
// router.addRoute(item)
// })
// // 在最后向路由表添加一个404规则
// // 切记不要写到路由表内 否者刷新页面会跳转到404页面
// router.addRoute({
// path: '/:pathMatch(.*)*',
// component: () => import('@/views/404/index.vue'),
// name: 'Any',
// meta: {
// title: '任意',
// hidden: true,
// },
// })
// next({ ...to, replace: true }) // 这里相当于push到一个页面 不在进入路由拦截
// } catch (error) {
// // 如果获取用户信息失败了则执行登出操作让重新登录
// console.log(error)
// userStore.logout()
// next({ path: '/login' })
// }
// }
// }
// } else {
// // 没有token访问登录页放行
// if (to.path == '/login') {
// next()
// } else {
// // 访问其他页面则阻止
// next({ path: '/login', query: { redirect: to.path } })
// }
// }
// })
router.beforeEach((to,form,next) => {
next()
router.beforeEach(async (to, form, next) => {
// 进度条开始\
nprogress.configure({ showSpinner: false })
nprogress.start()
// 判断是否登录
if (userStore.token) {
// 登录成功访问登录页则跳转到首页
if (to.path == '/login') {
next({ path: '/' })
} else {
// 登录成功判断是否获取到了用户信息
if (userStore.userName) {
next()
} else {
try {
// 没有获取到用户信息 就获取用户信息 然后放行
await userStore.getUserInfo()
// 获取筛选到的路由
const asyncRouter = await usePermissionStore.getAsyncRoutes(
userStore.routes,
)
// 遍历筛选出来的路由通过addRoute添加到路由表
asyncRouter.forEach((item: any) => {
router.addRoute(item)
})
// 在最后向路由表添加一个404规则
// 切记不要写到路由表内 否者刷新页面会跳转到404页面
router.addRoute({
path: '/:pathMatch(.*)*',
component: () => import('@/views/404/index.vue'),
name: 'Any',
meta: {
title: '任意',
hidden: true,
},
})
next({ ...to, replace: true }) // 这里相当于push到一个页面 不在进入路由拦截
} catch (error) {
// 如果获取用户信息失败了则执行登出操作让重新登录
console.log(error)
userStore.logout()
next({ path: '/login' })
}
}
}
} else {
// 没有token访问登录页放行
if (to.path == '/login') {
next()
} else {
// 访问其他页面则阻止
next({ path: '/login', query: { redirect: to.path } })
}
}
})
// router.beforeEach((to, form, next) => {
// console.log(to, 'to')
// next()
// })
router.afterEach((to, form, next) => {
nprogress.done()
})

@ -1,6 +1,9 @@
// 引入仓库
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
// 创建仓库
const pinia = createPinia()
// 暴露仓库
pinia.use(persist)
export default pinia
export * from './modules/user'

@ -5,4 +5,5 @@ export interface LoginStoreType {
userName: string // 用户姓名
avatar: string // 用户头像
routes: string
phone: string
}

@ -14,68 +14,108 @@ import { GET_TKOEN, SET_TKOEN, REMOVE_TOKEN } from '@/utils/token'
import { constantRoute } from '@/router/routers'
import permissionStore from './permission'
// 引入路由
const useUserStore = defineStore('User', {
state: (): LoginStoreType => {
return {
token: GET_TKOEN('TOKEN') || '',
menuRoutes: constantRoute, //存储生成菜单路由数据
userName: '', // 用户姓名
avatar: '', // 用户头像
routes: '',
}
},
actions: {
// 登录事件
async userLogin(data: loginType) {
const res: loginResponseType = await reqLogin(data)
this.token = res.data.token as string
import {
userRegisterService,
userLoginService,
userGetInfoService,
userPhoneLoginService,
} from '@/api/user/user.js'
if (res.code === 200) {
SET_TKOEN('TOKEN', this.token)
// localStorage.setItem('TOKEN', this.token)
ElNotification({
type: 'success',
message: '登录成功!',
title: `Hi ${getTime()}!`,
})
return 'ok'
} else {
ElNotification({
type: 'error',
message: res.data.message,
})
return Promise.reject(new Error(res.data.message))
const useUserStore = defineStore(
'User',
{
state: (): LoginStoreType => {
return {
token: GET_TKOEN('TOKEN') || '',
menuRoutes: constantRoute, //存储生成菜单路由数据
userName: '', // 用户姓名
avatar: '', // 用户头像
routes: '',
}
},
// 获取用户信息事件
async getUserInfo() {
const result = await getUserInfo()
if (result.code === 200) {
console.log(result)
this.userName = result.data.checkUser.username
this.avatar = result.data.checkUser.avatar
this.routes = result.data.checkUser.routes
return 'ok'
} else {
return Promise.reject('登录过期')
}
},
// 退出登录事件
logout() {
const usePermissionStore = permissionStore()
console.log(usePermissionStore)
// 清除token
REMOVE_TOKEN('TOKEN')
;(this.userName = ''), (this.avatar = '')
this.token = ''
this.routes = ''
usePermissionStore.removeRouter()
location.reload()
actions: {
// 登录事件
async userLogin(data: loginType) {
console.log(data, 'pinia')
const res: loginResponseType = await userLoginService(data)
this.token = res.data.token as string
console.log(res, '传回的值1')
if (res.code === 200) {
SET_TKOEN('TOKEN', this.token)
// localStorage.setItem('TOKEN', this.token)
ElNotification({
type: 'success',
message: '登录成功!',
title: `Hi ${getTime()}!`,
})
return 'ok'
} else {
console.log(res, '33333')
ElNotification({
// type: 'error',
message: res.message,
})
return Promise.reject(new Error(res.data.message))
}
},
//手机号登录
async userPhoneLogin(data: loginType) {
const res: loginResponseType = await userPhoneLoginService(data)
this.token = res.data.token as string //接收返回的token
console.log(res, 11111)
if (res.code === 200) {
SET_TKOEN('TOKEN', this.token)
// localStorage.setItem('TOKEN', this.token)
ElNotification({
type: 'success',
message: '登录成功!',
title: `Hi ${getTime()}!`,
})
return 'ok'
} else {
ElNotification({
type: 'error',
message: res.data.message,
})
return Promise.reject(new Error(res.data.message))
}
},
// 获取用户信息事件
async getUserInfo() {
const result = await userGetInfoService(GET_TKOEN('TOKEN'))
if (result.code === 200) {
// console.log(result)
this.userName = result.data.username
// this.avatar = result.data.checkUser.avatar
this.routes = result.data.menus.map((item) => {
return item.name
})
return {
result,
}
} else {
return Promise.reject('登录过期')
}
},
// 退出登录事件
logout() {
const usePermissionStore = permissionStore()
console.log(usePermissionStore)
// 清除token
REMOVE_TOKEN('TOKEN'),
// ;(this.userName = ''), (this.avatar = '')
(this.userName = ''),
(this.avatar = '')
this.token = ''
this.routes = ''
usePermissionStore.removeRouter()
location.reload()
},
},
getters: {},
},
{
persist: true, // 持久化
},
getters: {},
})
)
export default useUserStore

@ -0,0 +1,25 @@
import OSS from 'ali-oss'
import { nanoid } from 'nanoid'
export const client = new OSS({
region: 'oss-cn-beijing', //创建的时候,bucket所在的区域,华北2->oss-cn-beijing ;其他的可以去百度
accessKeyId: 'LTAI5tPutTqQcDZjPXTVmuLy', // 阿里云控制台创建的AccessKey
accessKeySecret: '3PmB75969OJt6uOMkRJTZjpSbVI4iL', //阿里云控制台创建的AccessSecret
bucket: 'teaching-edu123', //创建的bucket的名称
endpoint: 'oss-cn-beijing.aliyuncs.com', //地域节点
secure: false, //http:false,https:ture
})
export const tool = {
oss: {
async upload(file) {
// // console.log(11, file, client)
// 文件名
const uuid = nanoid() // 文件后缀名
const index = file.name.lastIndexOf('.')
const suffix = file.name.substring(index + 1)
let fileName = uuid + '.' + suffix
console.log(uuid, file, suffix) // return await client.multipartUpload(fileName, file, {
return await client.put(fileName, file)
},
},
}

@ -2,7 +2,6 @@ import axios from 'axios'
import { ElMessage } from 'element-plus'
// 获取用户相关小仓库
import useUserStore from '@/store/modules/user'
//创建axios实例
const request = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,

@ -1,14 +1,9 @@
<template>
<div>
课程点赞
</div>
<div>课程点赞</div>
</template>
<script lang='ts' setup>
import { } from 'vue'
<script lang="ts" setup>
import {} from 'vue'
</script>
<style lang='scss' scoped>
</style>
<style lang="scss" scoped></style>

@ -1,14 +1,9 @@
<template>
<div>
课程收藏
</div>
<div>课程收藏</div>
</template>
<script lang='ts' setup>
import { } from 'vue'
<script lang="ts" setup>
import {} from 'vue'
</script>
<style lang='scss' scoped>
</style>
<style lang="scss" scoped></style>

@ -1,14 +1,9 @@
<template>
<div>
学习过程
</div>
<div>学习过程</div>
</template>
<script lang='ts' setup>
import { } from 'vue'
<script lang="ts" setup>
import {} from 'vue'
</script>
<style lang='scss' scoped>
</style>
<style lang="scss" scoped></style>

@ -1,14 +1,9 @@
<template>
<div>
课程目标
</div>
<div>课程目标</div>
</template>
<script lang='ts' setup>
import { } from 'vue'
<script lang="ts" setup>
import {} from 'vue'
</script>
<style lang='scss' scoped>
</style>
<style lang="scss" scoped></style>

@ -1,14 +1,9 @@
<template>
<div>
课程图谱
</div>
<div>课程图谱</div>
</template>
<script lang='ts' setup>
import { } from 'vue'
<script lang="ts" setup>
import {} from 'vue'
</script>
<style lang='scss' scoped>
</style>
<style lang="scss" scoped></style>

@ -1,9 +1,181 @@
<template>
<div>分组</div>
<div class="container">
<div class="main">
<div class="head">
<img src="../src/assets/static/image/demo.png" alt="" />
<div class="title">智慧物业管理平台</div>
</div>
<!-- 登录内容区域 -->
<div class="login">
<div class="login-options">
<div
class="login-item"
v-for="(item, index) in loginitems"
:key="item.id"
:class="current === index ? 'move' : ''"
@click="change(index)"
>
{{ item.text }}
</div>
</div>
<div class="con">
<div class="con-item" v-if="current == 0">
<div class="input-items">
<input
type="text"
v-model="phonevalue"
placeholder="输入手机号"
/>
</div>
<div class="input-items">
<input type="text" v-model="codevalue" placeholder="输入验证码" />
<div class="getcode" @click="getcode">
<p v-if="isok == 1">获取验证码</p>
<p v-else>{{ countTime }}</p>
</div>
</div>
<div class="login-btn" @click="login">登录</div>
</div>
<div class="con-item" v-if="current == 1">
<div class="input-items">
<input
type="text"
v-model="phonevalue"
placeholder="输入手机号"
/>
</div>
<el-input
v-model="passwordinput"
style="height: 41px; border: 0; outline: none"
type="password"
placeholder="输入密码"
show-password
/>
<div class="reset" @click="gotoforgetPassword">重置密码></div>
<div class="login-btn" @click="login">登录</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {} from 'vue'
<script>
export default {
data() {
return {
imageUrl: '',
}
},
methods: {
previewImage(event) {
const input = event.target
const reader = new FileReader()
reader.onload = () => {
this.imageUrl = reader.result
}
reader.readAsDataURL(input.files[0])
},
},
}
</script>
<style lang="scss" scoped></style>
<style>
.container {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
.main {
display: flex;
align-items: center;
flex-direction: column;
}
.head {
display: flex;
align-items: center;
flex-direction: column;
}
.head .title {
font-size: 22px;
font-weight: 600;
}
.head img {
width: 300px;
height: 200px;
}
.move {
color: #f5b90f;
padding-bottom: 5px;
font-weight: 600;
border-bottom: 2px solid #f5b90f;
}
.login-options {
margin: 20px 0 10px 0;
display: flex;
align-items: center;
}
.login {
width: 300px;
display: flex;
flex-direction: column;
}
.login-item {
box-sizing: border-box;
padding-bottom: 5px;
margin-right: 10px;
}
.input-items {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
height: 40px;
margin: 10px 0;
}
input {
padding-left: 10px;
outline: none;
padding-right: 10px;
border: 0;
font-size: 14px;
}
.getcode {
margin-right: 5px;
}
.con-item {
position: relative;
}
.getcode p {
width: 80px;
height: 25px;
text-align: center;
background-color: #f5b90f;
color: #fff;
font-size: 12px;
line-height: 25px;
border-radius: 5px;
cursor: pointer;
}
.login-btn {
width: 100%;
height: 30px;
border-radius: 5px;
text-align: center;
line-height: 30px;
color: #fff;
margin: 45px 0;
background-color: #f5b90f;
cursor: pointer;
}
.reset {
position: absolute;
right: 0;
color: #f5b90f;
font-size: 12px;
margin: 10px 0;
}
</style>

@ -1,10 +1,46 @@
<script setup>
import conheader from '@/views/home/components/ConHeader.vue'
const courses = [
{
name: 'Vue.js Basics',
image: 'vuejs-basics.jpg',
hours: 20,
},
{
name: 'Reac',
image: 'react-fundamentals.jpg',
hours: 25,
},
{
name: 'Reacts',
image: 'react-fundamentals.jpg',
hours: 25,
},
{
name: 'React Fund',
image: 'react-fundamentals.jpg',
hours: 25,
},
]
</script>
<template>
<div class="info_container" style="width: 214px; height: 326px">
<conheader title="已开科目"></conheader>
<div class="info_content" style="width: 214px; height: 264px"></div>
<div class="info_content" style="width: 214px; height: 264px">
<div class="course-list">
<div
v-for="(course, index) in courses"
:key="index"
class="course-item"
>
<img :src="course.image" alt="course image" class="course-image" />
<div class="course-info">
<p class="course-name">{{ course.name }}</p>
<p class="course-hours">{{ course.hours }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
@ -13,18 +49,60 @@ import conheader from '@/views/home/components/ConHeader.vue'
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
display: flex;
justify-content: center;
align-items: center;
}
.info_container {
display: flex;
justify-content: space-between;
}
.info_content {
display: flex;
justify-content: space-between;
}
.course-list {
display: flex;
flex-direction: column;
width: 132px;
height: 48px;
}
.course-item {
display: flex;
margin: 10px;
}
.course-image {
width: 48px;
height: 48px;
margin-right: 16px;
}
.course-info {
display: flex;
flex-direction: column;
width: 75px;
height: 46px;
}
.course-name {
font-family: Inter, Inter;
font-weight: normal;
font-size: 14px;
color: rgba(0, 0, 0, 0.9);
line-height: 22px;
text-align: left;
font-style: normal;
text-transform: none;
}
.course-hours {
font-family: Inter, Inter;
font-weight: 400;
font-size: 12px;
color: rgba(0, 0, 0, 0.4);
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
}
</style>

@ -2,9 +2,10 @@
<div class="info_title">
<div class="title_name">{{ props.title }}</div>
<div class="icon">
<img :src="ellipsis" />
<img :src="ellipsis" @click="handleClick" />
</div>
</div>
<slot name="drawer"></slot>
</template>
<script lang="ts" setup>
@ -12,7 +13,12 @@ import { defineProps } from 'vue'
import ellipsis from '@/assets/images/ellipsis.png'
const props = defineProps({
title: String,
modelValue: Boolean,
})
const emit = defineEmits(['update:modelValue'])
function handleClick() {
emit('update:modelValue', true)
}
</script>
<style lang="scss" scoped>
@ -31,6 +37,7 @@ const props = defineProps({
text-transform: none;
}
}
.icon {
width: 32px;
height: 32px;
@ -39,4 +46,19 @@ const props = defineProps({
justify-content: center;
align-items: center;
}
.overlay {
position: absolute;
top: 181px;
left: 1278px;
width: 40px;
height: 30px;
// background-color: lightgray;
border: 1px solid black;
z-index: 1000;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
margin-bottom: 10px;
}
</style>

@ -4,9 +4,7 @@ import conheader from '@/views/home/components/ConHeader.vue'
</script>
<template>
<div class="info_container" style="width: 1257px; height: 384px">
<div class="info_title">
<conheader title="主页访问数据(次)"></conheader>
</div>
<conheader title="主页访问数据(次)"></conheader>
<div class="info_content" style="width: 766px; height: 352px">
<echarts></echarts>
</div>
@ -18,33 +16,13 @@ import conheader from '@/views/home/components/ConHeader.vue'
flex-direction: column;
justify-content: space-between;
}
.info_content {
display: grid;
display: wrap;
grid-template-columns: repeat(4, 1fr);
gap: 10px; /* 设置元素之间的间隔 */
}
.echarts {
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
margin-bottom: 16px;
display: flex;
justify-content: center;
align-items: center;
.info_container {
display: flex;
justify-content: space-between;
}
}
.echarts {
width: 1321px;
height: 444px;
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
}
</style>

@ -1,7 +1,75 @@
<script setup>
import conheader from '@/views/home/components/ConHeader.vue'
const products = [
{ name: 'Macbook', content: 9.9 },
import { onMounted, ref } from 'vue'
// import { getTeaUserService } from '@/api/user/userinfo'
// const userList = ref([])
// const getUserList = async () => {
// const res = await getTeaUserService()
// userList.value = res.data
// }
// getUserList()
import { toRaw } from 'vue'
import { userIdenService, userIdenChangeService } from '@/api/user/user.js'
import { Edit } from '@element-plus/icons-vue'
const props = defineProps({
data: {
type: Object,
},
})
const id = props.data.id
console.log(id.value, 'id')
const formModel = ref()
// console.log(formModel.value, 'value')
const loading = ref()
//true
const editDialogVisible = ref(false)
const editForm = ref({
name: '',
content: '',
})
const editRowIndex = ref(null)
const openEditDialog = (row) => {
editForm.value = { ...row }
editRowIndex.value = formModel.value.indexOf(row)
editDialogVisible.value = true
}
//
const cancelEdit = () => {
editDialogVisible.value = false
//
editForm.value = {
name: '',
content: '',
}
}
//
const saveEdit = () => {
if (editRowIndex.value !== null) {
//dialogdrawer
console.log(editRowIndex.value)
formModel.value.splice(editRowIndex, 1, { ...editForm })
// editRowIndex.value = null
console.log(editForm.value, 'edit')
console.log(editForm.value, 'formModel')
}
editDialogVisible.value = false
}
//
const data1 = ref()
const getInfo = async (id) => {
const res = await userIdenService(id)
// formModel.value = res.data
data1.value = toRaw(res.data)
// console.log(data1.value.name, 'pin')
}
onMounted(() => {
getInfo(id)
})
console.log(toRaw(data1), '2222222222')
//
formModel.value = [
{ name: '名字', content: 9.9 },
{ name: 'iPhone', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
@ -11,12 +79,81 @@ const products = [
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
]
// 1
// import type { DrawerProps } from 'element-plus'
const drawer2 = ref(false)
const direction = ref('rtl')
function cancelClick() {
drawer2.value = false
}
function confirmClick() {
drawer2.value = false
userIdenChangeService()
}
</script>
<template>
<div class="info_container" style="width: 1265px; height: 306px">
<conheader title="个人信息"></conheader>
<div class="info_content" style="width: 776px; height: 244px">
<div class="item" :key="item.name" v-for="item in products">
<conheader title="个人信息" v-model="drawer2">
<template #drawer>
<el-drawer v-model="drawer2" :direction="direction" class="table">
<template #header>
<h4>个人信息</h4>
</template>
<template #default>
<div>
<el-table :data="formModel" style="width: 100%">
<el-table-column prop="name" label="标题"></el-table-column>
<el-table-column prop="content" label="内容"></el-table-column>
<el-table-column label="操作">
<template #default="{ row, $index }">
<el-button
type="primary"
class="el-icon-edit"
:icon="Edit"
plain
circle
@click="openEditDialog(row, $index)"
></el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<template #footer>
<div style="flex: auto">
<el-button @click="cancelClick" size="medium">取消</el-button>
<el-button type="primary" @click="confirmClick">确认</el-button>
</div>
</template>
</el-drawer>
<el-dialog
v-model="editDialogVisible"
title="详细信息"
style="width: 800px; text-align: center"
>
<el-form :model="editForm" label-width="80px">
<el-form-item label="名称" prop="name">
<el-input v-model="editForm.name"></el-input>
</el-form-item>
<el-form-item label="内容" prop="content">
<el-input v-model="editForm.content"></el-input>
</el-form-item>
</el-form>
<template v-slot:footer>
<div>
<el-button @click="cancelEdit">取消</el-button>
<el-button type="primary" @click="saveEdit">保存</el-button>
</div>
</template>
</el-dialog>
</template>
</conheader>
<div
class="info_content"
style="width: 776px; height: 244px"
v-loading="loading"
>
<div class="item" :key="item.name" v-for="item in formModel">
<div class="name">{{ item.name }}</div>
<div class="content">{{ item.content }}</div>
</div>
@ -62,4 +199,16 @@ const products = [
font-style: normal;
text-transform: none;
}
.table {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.custom-input {
margin-bottom: 20px;
height: 40px; /* 自定义输入框高度 */
padding: 0 10px; /* 自定义输入框内边距 */
font-size: 16px; /* 自定义输入框字体大小 */
/* 其他样式自定义 */
}
</style>

@ -1,21 +1,149 @@
<script setup>
import add from '@/assets/images/add.png'
// import { Plus } from '@element-plus/icons-vue'
import { userChangeService } from '@/api/user/user.js'
import { client, tool } from '@/utils/img'
const dialogFormVisible = ref(false)
import { ref } from 'vue'
const props = defineProps({
data: {
type: Object,
},
})
// console.log(props.data.icon, 44444)
const imageUrl = ref('')
//
const form = ref()
//
// const rules = {
// username: [
// { required: true, message: '', trigger: 'blur' },
// {
// max: 10,
// min: 5,
// message: '',
// trigger: ['change', 'blur'],
// },
// ],
// nickName: [{ trigger: 'blur' }],
// phone: [
// { trigger: 'blur' },
// {
// pattern: /^1[3456789]\d{9}$/,
// message: '',
// trigger: 'blur',
// // pattern
// },
// ],
// code: [{ message: '', trigger: 'blur' }],
// }
form.value = props.data
const formLabelWidth = '140px'
//
imageUrl.value = props.data.icon
//
const openFileInput = () => {
const input = document.createElement('input')
input.type = 'file'
input.accept = 'image/*'
input.style.display = 'none'
input.onchange = (event) => {
const file = event.target.files[0]
if (file) {
const reader = new FileReader()
reader.onload = () => {
imageUrl.value = reader.result
}
reader.readAsDataURL(file)
}
}
document.body.appendChild(input)
input.click()
document.body.removeChild(input)
}
// const upload = async (option) => {
// console.log(option, 88888)
// const res = await tool.oss.upload(option.file) // console.log(res.url)
// imageUrl.value = res.url
// // imgUrl.value = formModel.value.img
// console.log(imageUrl.value, 1111155)
// }
const upload = async (option) => {
// console.log(option)
const res = await tool.oss.upload(option.file)
// console.log(res.url)
imageUrl.value = res.url
// imgUrl.value = formModel.value.img
console.log(imageUrl.value)
}
//
const changeInfo = async () => {
const userInfo = {
username: form.value.username,
phone: form.value.phone,
id: form.value.id,
nickName: form.value.nickName,
icon: imageUrl.value,
}
const jsonData = JSON.stringify(userInfo)
console.log(jsonData)
await userChangeService(jsonData)
dialogFormVisible.value = false
}
</script>
<template>
<div class="ident" style="width: 278px; height: 274px; background: #375fff">
<div class="info_container" style="width: 201px; height: 178px">
<div class="avater">
<span>T</span>
<div class="click"><img :src="add" /></div>
<img :src="imageUrl" alt="Uploaded" v-if="imageUrl" class="avater" />
</div>
<div class="click" @click="openFileInput" :http-request="upload">
<img :src="add" />
</div>
<div class="info_content" style="width: 126px; height: 58px">
<div class="name">王亚楠</div>
<div
class="info_content"
style="height: 58px"
@click="dialogFormVisible = true"
>
<div class="name">{{ props.data.username }}</div>
<div class="iden">身份:某某专业教师</div>
</div>
</div>
</div>
<el-dialog
v-model="dialogFormVisible"
title="登录信息"
style="
width: 800px;
text-align: center;
/* display: flex;
justify-content: center; */
"
>
<el-form :model="form" style="" :data="form" :rules="rules">
<el-form-item label="姓名" :label-width="formLabelWidth">
<el-input v-model="form.username" autocomplete="off" />
</el-form-item>
<el-form-item label="昵称" :label-width="formLabelWidth">
<el-input v-model="form.nickName" autocomplete="off" />
</el-form-item>
<el-form-item label="手机" :label-width="formLabelWidth">
<el-input v-model="form.phone" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="changeInfo">确认</el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped>
.el-form-item {
display: flex;
}
.ident {
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
@ -35,7 +163,6 @@ import add from '@/assets/images/add.png'
justify-content: space-between;
}
.name {
width: 60px;
height: 28px;
font-family: Inter, Inter;
font-weight: normal;
@ -64,13 +191,6 @@ import add from '@/assets/images/add.png'
background: #b5c7ff;
border-radius: 40px 40px 40px 40px;
display: flex;
display: grid;
grid-template-areas:
'element1 .'
'. element2';
grid-template-columns: auto auto; /* 定义三列 */
grid-template-rows: auto auto; /* 定义三行 */
gap: 5px; /* 设置网格之间的间隙 */
}
span {
width: 24px;
@ -100,5 +220,7 @@ span {
display: flex;
justify-content: center;
align-items: center;
margin-left: 60px;
margin-top: -60px;
}
</style>

@ -1,10 +1,53 @@
<script setup>
import conheader from '@/views/home/components/ConHeader.vue'
import { ref } from 'vue'
const name = ['Aa', 'Bb', 'Cc', 'Dd']
const getRandomColor = () => {
//
return '#' + Math.floor(Math.random() * 16777215).toString(16)
}
const drawer2 = ref(false)
const direction = ref('rtl')
function cancelClick() {
drawer2.value = false
}
function confirmClick() {
drawer2.value = false
}
</script>
<template>
<div class="info_container" style="width: 222px; height: 174px">
<conheader title="学生分组"></conheader>
<div class="info_content" style="width: 194px; height: 112px"></div>
<conheader title="学生分组" v-model="drawer2">
<div>编辑</div>
<template #drawer>
<el-drawer v-model="drawer2" :direction="direction">
<template #header>
<h4>78</h4>
</template>
<template #default>
<div></div>
</template>
<template #footer>
<div style="flex: auto">
<el-button @click="cancelClick">cancel</el-button>
<el-button type="primary" @click="confirmClick">
confirm
</el-button>
</div>
</template>
</el-drawer>
</template>
</conheader>
<div class="info_content" style="width: 194px; height: 112px">
<div
v-for="(item, index) in name"
:key="index"
class="stu_color"
:style="{ backgroundColor: getRandomColor() }"
>
{{ item }}
</div>
</div>
</div>
</template>
<style scoped>
@ -19,12 +62,36 @@ import conheader from '@/views/home/components/ConHeader.vue'
}
.info_container {
display: flex;
justify-content: space-between;
}
.info_content {
width: 194px;
height: 112px;
flex-wrap: wrap;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px; /* 设置元素之间的间隔 */
grid-row-gap: 16px;
}
.stu_color {
display: flex;
justify-content: space-between;
justify-content: center;
align-items: center;
width: 49px;
height: 48px;
border-radius: 40px 40px 40px 40px;
font-family: Inter, Inter;
font-weight: normal;
font-size: 14px;
color: rgba(255, 255, 255, 0.9);
line-height: 22px;
text-align: left;
font-style: normal;
text-transform: none;
transition: background-color 0.3s;
}
.stu_color:hover {
background-color: #f2f2f2; /* 鼠标移入时的背景色变化 */
filter: brightness(1.1);
}
</style>

@ -1,7 +1,13 @@
<script setup></script>
<script setup>
const props = defineProps({
username: String,
})
</script>
<template>
<div class="nav_info">
<div class="name">王亚楠</div>
<div class="name">
{{ props.username }}
</div>
<div class="intro">今天是你的第一天~~</div>
</div>
</template>
@ -14,7 +20,6 @@
align-items: center;
}
.name {
width: 91px;
height: 28px;
font-family: Inter, Inter;
font-weight: bold;

@ -5,22 +5,43 @@ import Info from './components/Info.vue'
import echarts from './components/Echarts.vue'
import Class from './components/Class.vue'
import Student from './components/Student.vue'
import useUserStore from '@/store/modules/user'
import { ref, onMounted } from 'vue'
import { toRaw } from 'vue'
const userStore = useUserStore()
// const info = userStore.getUserInfo()
// console.log(info)
const username = ref()
const data = ref(null)
const flag = ref(false)
const getData = async () => {
await userStore.getUserInfo().then((response) => {
const res = toRaw(response.result.data)
username.value = res.username
data.value = res
flag.value = true
console.log(data.value, 333333)
})
}
onMounted(() => {
getData()
})
</script>
<template>
<div class="container">
<div class="left">
<div class="nav">
<welcome></welcome>
<welcome :username="username"></welcome>
</div>
<div class="info" style="width: 1321px; height: 368px">
<Info></Info>
<Info :data="data" v-if="flag"></Info>
</div>
<div class="echarts" style="width: 1321px; height: 444px">
<echarts></echarts>
</div>
</div>
<div class="right">
<status></status>
<status :data="data" v-if="flag"></status>
<div class="class" style="width: 278px; height: 388px">
<Class></Class>
</div>

@ -1,6 +1,5 @@
<template>
<div id="your-element-selector" style="width: 100%; height: 100vh"></div>
<div class="login_container">
<el-row>
<el-col
@ -15,38 +14,240 @@
>
<el-form
class="loin_form"
:model="loginForm"
ref="formRules"
:model="formModel"
ref="form"
:rules="rules"
v-if="isRegister"
>
<h1>Hello</h1>
<h2>同学</h2>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
:prefix-icon="User"
size="large"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
:prefix-icon="Lock"
size="large"
show-password
></el-input>
<!-- <h2>同学</h2> -->
<div class="taggle">
<h2 @click="isToggle = true">账号</h2>
<h2 @click="isToggle = false">手机号</h2>
</div>
<!-- 账号登录 -->
<div v-if="isToggle">
<el-form-item prop="username">
<el-input
v-model="formModel.username"
:prefix-icon="User"
size="large"
placeholder="请输入账号"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="formModel.password"
type="password"
:prefix-icon="Lock"
size="large"
placeholder="请输入密码"
show-password
></el-input>
</el-form-item>
<el-form-item>
<el-button
:loading="isBtnLoading"
size="large"
class="login-btn"
type="primary"
@click="login"
>
登录
</el-button>
</el-form-item>
</div>
<!-- 手机号登录 -->
<div v-else>
<el-form-item prop="phone" class="content">
<el-input
type="text"
v-model="formModel.phone"
:prefix-icon="Phone"
placeholder="请输入手机号"
size="large"
style="
flex: 1;
/* height: 40px;
line-height: 40px;
border: 1px solid #dcdfe6;
width: 50%;
color: #dcdfe6; */
"
/>
</el-form-item>
<el-form-item prop="code" class="content">
<el-input
type="text"
v-model="formModel.code"
placeholder="请输入验证码"
@click="userCodeLogin"
size="large"
style="flex: 1"
/>
<div class="p" v-if="countdown > 0">
<p>{{ countdown }} </p>
</div>
<el-button
class="code"
v-if="countdown <= 0"
:disabled="countdown > 0"
@click="codeSubmit"
>
发送验证码
</el-button>
</el-form-item>
<el-form-item>
<el-button
:loading="isBtnLoading"
size="large"
class="login-btn"
type="primary"
@click="phoneLogin"
>
登录
</el-button>
</el-form-item>
</div>
<el-form-item class="flex">
<el-link type="info" :underline="false" @click="isRegister = false">
注册
</el-link>
</el-form-item>
<el-form-item>
<el-button
:loading="isBtnLoading"
size="large"
class="login-btn"
type="primary"
@click="login"
>
</el-form>
<!-- 注册 -->
<el-form
class="loin_form"
:model="formModel"
ref="form"
:rules="rules"
v-else
>
<h1>Hello</h1>
<div class="taggle">
<h2 @click="isToggle = true">账号</h2>
<h2 @click="isToggle = false">手机号</h2>
</div>
<!-- 账号注册 -->
<div v-if="isToggle">
<el-form-item prop="username">
<el-input
v-model="formModel.username"
placeholder="请输入用户名"
:prefix-icon="User"
size="large"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="formModel.password"
placeholder="请输入密码"
type="password"
:prefix-icon="Lock"
size="large"
show-password
></el-input>
</el-form-item>
<el-form-item prop="repassword">
<el-input
v-model="formModel.repassword"
placeholder="请再次输入密码"
type="password"
:prefix-icon="Check"
size="large"
show-repassword
></el-input>
</el-form-item>
<el-form-item>
<el-button
size="large"
class="login-btn"
type="primary"
@click="handleSubmit"
>
注册
</el-button>
</el-form-item>
</div>
<!-- 手机号注册 -->
<div v-else>
<div v-if="isinfo">
<el-form-item prop="phone">
<el-input
v-model="formModel.phone"
:prefix-icon="Phone"
size="large"
placeholder="请输入手机号"
></el-input>
</el-form-item>
<el-form-item prop="code" class="content">
<el-input
type="text"
v-model="formModel.code"
placeholder="请输入验证码"
size="large"
style="
flex: 1;
/* height: 40px;
line-height: 40px;
border: 1px solid #dcdfe6;
width: 50%;
color: #dcdfe6; */
"
/>
<div class="p" v-if="countdown > 0">
<p>{{ countdown }} </p>
</div>
<el-button
class="code1"
v-if="countdown <= 0"
@click="codeSubmit"
:disabled="countdown > 0"
>
发送验证码
</el-button>
</el-form-item>
<el-form-item>
<el-button
size="large"
class="login-btn"
type="primary"
@click="phoneSubmit"
>
注册
</el-button>
</el-form-item>
</div>
<div v-else>
<el-form-item prop="password">
<el-input
v-model="formModel.password"
type="password"
:prefix-icon="Lock"
size="large"
placeholder="请输入密码"
show-password
></el-input>
</el-form-item>
<el-form-item class="codeVerify">
<el-button
class="code"
@click="phoneSubmit"
style="
margin-left: 650px;
background-color: #5577ff;
color: #fff;
"
>
确认
</el-button>
</el-form-item>
</div>
</div>
<el-form-item class="flex">
<el-link type="info" :underline="false" @click="isRegister = true">
登录
</el-button>
</el-link>
</el-form-item>
</el-form>
</el-col>
@ -56,19 +257,53 @@
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted, onUnmounted } from 'vue'
import { User, Lock } from '@element-plus/icons-vue'
import {
// reactive,
ref,
onMounted,
onUnmounted,
onBeforeUnmount,
watch,
} from 'vue'
import { User, Lock, Phone, Check } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import useUserStore from '@/store/modules/user'
import { useRouter, useRoute } from 'vue-router'
let loginForm = reactive({ username: 'admin', password: '111111' })
//token
// import jwtDecode from 'jwt-decode'
//
import {
userRegisterService,
userPhoneRegisterService,
userCodeRegisterService,
} from '@/api/user/user.js'
// let loginForm = reactive({ username: 'admin', password: '111111' })
//
let userStore = useUserStore()
//
const $router = useRouter()
const $route = useRoute()
const isBtnLoading = ref<boolean>(false)
//
//model,form
const formModel = ref({
username: '',
password: '',
repassword: '',
phone: '',
code: '',
nickName: '',
icon: '',
})
const form = ref()
//
const isRegister = ref(true)
//
const isToggle = ref(true)
//
const isinfo = ref(true)
// form
const formRules = ref<any>(null)
// const formRules = ref<any>(null)
//
const rules = {
username: [
@ -89,14 +324,187 @@ const rules = {
trigger: ['change', 'blur'],
},
],
repassword: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{
pattern: /^\S{6,15}$/,
// pattern
max: 15,
min: 6,
message: '密码长度最小六位最大十五位',
trigger: ['change', 'blur'],
},
{
validater: (rule, value, callback) => {
//valuepassword
if ((value = formModel.value.password)) {
callback(new Error('两次输入的密码不一致'))
} else {
callback()
}
},
trigger: ['change', 'blur'],
},
],
phone: [
{ required: true, message: '手机号不能为空', trigger: 'blur' },
{
pattern: /^1[3456789]\d{9}$/,
message: '手机号格式不正确',
trigger: 'blur',
// pattern
},
],
code: [{ message: '验证码不能为空', trigger: 'blur' }],
}
//--
const countdown = ref(0)
let timer
const startCountdown = () => {
countdown.value = 60 //
timer = setInterval(updateCountdown, 1000) //
}
const updateCountdown = () => {
if (countdown.value > 0) {
countdown.value--
} else {
clearInterval(timer) //
}
}
onBeforeUnmount(() => {
clearInterval(timer) //
})
//
// watch(isRegister, () => {
// formModel.value = {
// username: '',
// password: '',
// repassword: '',
// phone: '',
// code: '',
// }
// })
//
watch(isToggle, () => {
formModel.value = {
username: '',
password: '',
repassword: '',
phone: '',
code: '',
}
})
//json
const handleSubmit = async () => {
try {
//
await form.value.validate()
console.log('开始注册')
const dataToSend = {
username: formModel.value.username,
password: formModel.value.password,
phone: formModel.value.phone,
nickName: formModel.value.nickName,
icon: formModel.value.icon,
}
const jsonData = JSON.stringify(dataToSend)
console.log(jsonData)
//
userRegisterService(jsonData)
//
ElMessage.success('注册成功')
// //
isRegister.value = true
} catch (error) {
//
//
ElMessage.error('Oops, this is a error message.')
}
}
// const userGetInfoService
//
const codecode = ref()
const codeSubmit = async () => {
await form.value.validate()
console.log('开始发起注册验证码')
const phoness = formModel.value.phone
// const jsonData = JSON.stringify(dataToSend)
// console.log(phoness)
//
const res = userCodeRegisterService(phoness)
console.log(res)
userCodeRegisterService(phoness).then((response) => {
codecode.value = response.data
console.log(codecode.value)
})
}
//
const phoneSubmit = async () => {
await form.value.validate()
console.log('开始手机号注册')
try {
//
if (formModel.value.code == codecode.value) {
isinfo.value = false
const dataToSend = {
phone: formModel.value.phone,
password: formModel.value.password,
}
const jsonData = JSON.stringify(dataToSend)
//
await userPhoneRegisterService(jsonData)
console.log(jsonData)
//
isinfo.value = false
}
} catch (error) {
//
//
ElMessage.error('注册失败返回错误信息')
}
}
//
const login = async () => {
//
await formRules.value.validate()
await form.value.validate()
try {
isBtnLoading.value = true
await userStore.userLogin(loginForm)
await userStore.userLogin(formModel.value)
isBtnLoading.value = false
$router.push('/home')
if ($route.query.redirect) {
$router.push($route.query.redirect as string)
} else {
$router.push('/')
}
} catch (error) {
isBtnLoading.value = false
console.log(error, '111111')
}
}
//
// const log = ref()
const phoneLogin = async () => {
//
await form.value.validate()
try {
isBtnLoading.value = true
if (formModel.value.code == codecode.value) {
const phones = formModel.value.phone
// console.log(dataToSend)
userStore.userPhoneLogin(phones)
// console.log(res)
// .then((response) => {
// log.value = response.data
// console.log(log.value)
// })
// await userStore.userPhoneLogin(dataToSend)
}
isBtnLoading.value = false
$router.push('/home')
if ($route.query.redirect) {
$router.push($route.query.redirect as string)
} else {
@ -113,6 +521,7 @@ const keyDown = (e: any) => {
login()
}
}
onMounted(() => {
//@ts-expect-error tsVANTAGLOBE cdn
VANTA.GLOBE({
@ -156,6 +565,10 @@ onUnmounted(() => {
padding: 40px;
border-radius: 10px;
box-shadow: 7px 7px 42px rgba(0, 0, 0, 0.17);
.taggle {
display: flex;
justify-content: space-between;
}
h1 {
font-size: 40px;
color: #5577ff;
@ -168,6 +581,31 @@ onUnmounted(() => {
.login-btn {
width: 100%;
}
.p {
height: 40px;
text-align: center;
line-height: 40px;
width: 90px;
}
.codeVerify {
display: flex;
justify-content: end;
}
.code,
.code1 {
height: 40px;
width: 100px;
border: 1px solid #dcdfe6;
// background-color: #dcdfe6;
color: #000;
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
display: flex;
justify-content: space-between;
flex-wrap: none;
line-height: 40px;
}
}
}
</style>

@ -1,9 +1,107 @@
<template>
<div>消息</div>
<div>
<el-button type="primary" @click="openDrawer">Open Drawer</el-button>
<el-drawer
title="Drawer Title"
v-model:visible="drawerVisible"
:direction="drawerDirection"
:width="drawerWidth"
>
<el-table :data="formModel" style="width: 100%">
<el-table-column prop="name" label="Name"></el-table-column>
<el-table-column prop="content" label="Content">
<template #default="{ row }">
<el-button type="text" @click="openEditDialog(row)">Edit</el-button>
</template>
</el-table-column>
</el-table>
</el-drawer>
<el-dialog v-model:visible="editDialogVisible">
<el-form :model="editForm" label-width="80px">
<el-form-item label="Name" prop="name">
<el-input v-model="editForm.name"></el-input>
</el-form-item>
<el-form-item label="Content" prop="content">
<el-input v-model="editForm.content"></el-input>
</el-form-item>
</el-form>
<template v-slot:footer>
<div>
<el-button @click="cancelEdit">Cancel</el-button>
<el-button type="primary" @click="saveEdit">Save</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import {} from 'vue'
</script>
<script>
import { reactive } from 'vue'
export default {
setup() {
const data = reactive([
{ name: 'Macbook', content: 9.9 },
{ name: 'iPhone', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
])
const drawerVisible = reactive(false)
const drawerDirection = reactive('ltr')
const drawerWidth = reactive('500px')
const editDialogVisible = reactive(false)
const editForm = reactive({
name: '',
content: '',
})
let editRowIndex = null
<style lang="scss" scoped></style>
const openDrawer = () => {
drawerVisible.value = true
}
const openEditDialog = (row) => {
editForm.name = row.name
editForm.content = row.content
editRowIndex = data.indexOf(row)
editDialogVisible.value = true
}
const cancelEdit = () => {
editDialogVisible.value = false
editForm.name = ''
editForm.content = ''
}
const saveEdit = () => {
if (editRowIndex !== null) {
data.splice(editRowIndex, 1, { ...editForm })
editRowIndex = null
}
editDialogVisible.value = false
}
return {
formModel: data,
drawerVisible,
drawerDirection,
drawerWidth,
editDialogVisible,
editForm,
openDrawer,
openEditDialog,
cancelEdit,
saveEdit,
}
},
}
</script>

@ -1,14 +1,173 @@
<template>
<div>
课程首页
</div>
</template>
<div id="your-element-selector" style="width: 100%; height: 100vh"></div>
<script lang='ts' setup>
import { } from 'vue'
<div class="login_container">
<el-row>
<el-col
:span="12"
:xs="24"
style="
width: 100%;
height: 100%;
display: flex;
justify-content: center;
"
>
<el-form
class="loin_form"
:model="loginForm"
ref="formRules"
:rules="rules"
>
<h1>Hello</h1>
<h2>同学</h2>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
:prefix-icon="User"
size="large"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
:prefix-icon="Lock"
size="large"
show-password
></el-input>
</el-form-item>
<el-form-item>
<el-button
:loading="isBtnLoading"
size="large"
class="login-btn"
type="primary"
@click="login"
>
登录
</el-button>
</el-form-item>
</el-form>
</el-col>
<el-col :span="12" :xs="0"></el-col>
</el-row>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted, onUnmounted } from 'vue'
import { User, Lock } from '@element-plus/icons-vue'
import useUserStore from '@/store/modules/user'
import { useRouter, useRoute } from 'vue-router'
let loginForm = reactive({ username: 'admin', password: '111111' })
//
let userStore = useUserStore()
//
const $router = useRouter()
const $route = useRoute()
const isBtnLoading = ref<boolean>(false)
// form
const formRules = ref<any>(null)
//
const rules = {
username: [
{ required: true, message: '用户名不能为空', trigger: 'blur' },
{
max: 10,
min: 5,
message: '用户名长度最小五位最大十位',
trigger: ['change', 'blur'],
},
],
password: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{
max: 15,
min: 6,
message: '密码长度最小六位最大十五位',
trigger: ['change', 'blur'],
},
],
}
const login = async () => {
//
await formRules.value.validate()
try {
isBtnLoading.value = true
await userStore.userLogin(loginForm)
isBtnLoading.value = false
if ($route.query.redirect) {
$router.push($route.query.redirect as string)
} else {
$router.push('/')
}
} catch (error) {
isBtnLoading.value = false
}
}
const keyDown = (e: any) => {
//
if (e.keyCode == 13) {
//
login()
}
}
onMounted(() => {
//@ts-expect-error tsVANTAGLOBE cdn
VANTA.GLOBE({
el: '#your-element-selector',
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.0,
minWidth: 200.0,
scale: 1.0,
scaleMobile: 1.0,
color: 0x5adc,
color2: 0x1efc,
size: 0.8,
backgroundColor: 0xffffff,
})
window.addEventListener('keydown', keyDown)
})
onUnmounted(() => {
window.removeEventListener('keydown', keyDown, false)
})
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.login_container {
width: 100%;
height: 100vh;
position: absolute;
top: 0;
// background: url('../../assets/images/background.jpg') no-repeat;
.loin_form {
position: relative;
top: 30vh;
width: 80%;
// background: url('../../assets/images/login_form.png') no-repeat;
// background-size: cover;
// background-color: #5577ff;
background: rgba(255, 255, 255, 0.1);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
padding: 40px;
border-radius: 10px;
box-shadow: 7px 7px 42px rgba(0, 0, 0, 0.17);
h1 {
font-size: 40px;
color: #5577ff;
}
h2 {
font-size: 20px;
color: #5577ff;
margin: 20px 0;
}
.login-btn {
width: 100%;
}
}
}
</style>

@ -1,9 +1,536 @@
<script setup></script>
<template>
<div>学生</div>
<div id="your-element-selector" style="width: 100%; height: 100vh"></div>
<div class="login_container">
<!-- <el-row>
<el-col
:span="12"
:xs="24"
style="
width: 100%;
height: 100%;
display: flex;
justify-content: center;
"
>
<el-form
class="loin_form"
:model="loginForm"
ref="formRules"
:rules="rules"
>
<h1>Hello</h1>
<h2>同学</h2>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
:prefix-icon="User"
size="large"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
:prefix-icon="Lock"
size="large"
show-password
></el-input>
</el-form-item>
<el-form-item>
<el-button
:loading="isBtnLoading"
size="large"
class="login-btn"
type="primary"
@click="login"
>
登录
</el-button>
</el-form-item>
</el-form>
</el-col>
<el-col :span="12" :xs="0"></el-col>
</el-row> -->
<el-row>
<el-col
:span="12"
:xs="24"
style="
width: 100%;
height: 100%;
display: flex;
justify-content: center;
"
>
<el-form
class="loin_form"
v-if="isRegister"
:model="loginForm"
ref="form"
:rules="rules"
>
<h1>Hello</h1>
<!-- <h2>同学</h2> -->
<div class="taggle">
<h2 @click="isToggle = true">账号</h2>
<h2 @click="isToggle = false">手机号</h2>
</div>
<!-- 账号登录 -->
<div v-if="isToggle">
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
:prefix-icon="User"
size="large"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
:prefix-icon="Lock"
size="large"
show-password
></el-input>
</el-form-item>
</div>
<!-- 手机号登录 -->
<div v-else>
<el-form-item prop="phone">
<el-input
v-model="formModel.phone"
:prefix-icon="Phone"
size="large"
placeholder="请输入手机号"
></el-input>
</el-form-item>
<el-form-item prop="code" class="content">
<!-- <el-input
v-model="loginForm.password"
type="password"
:prefix-icon="Lock"
size="large"
show-password
></el-input> -->
<input
type="text"
v-model="loginForm.code"
placeholder="请输入验证码"
size="large"
style="
flex: 1;
height: 40px;
line-height: 40px;
border: 1px solid #dcdfe6;
/* color: #dcdfe6; */
"
/>
<div class="p" v-if="countdown > 0">
<p>{{ countdown }} </p>
</div>
<button
class="code"
v-if="countdown <= 0"
@click="startCountdown"
:disabled="countdown > 0"
>
发送验证码
</button>
</el-form-item>
</div>
<el-form-item>
<el-button
:loading="isBtnLoading"
size="large"
class="login-btn"
type="primary"
@click="login"
>
登录
</el-button>
</el-form-item>
<el-form-item class="flex">
<el-link type="info" :underline="false" @click="isRegister = false">
注册
</el-link>
</el-form-item>
</el-form>
<!-- 注册 -->
<el-form
class="loin_form"
:model="formModel"
ref="form"
:rules="rules"
v-else
>
<h1>Hello</h1>
<div class="taggle">
<h2 @click="isToggle = true">账号</h2>
<h2 @click="isToggle = false">手机号</h2>
</div>
<!-- 账号注册 -->
<div v-if="isToggle">
<el-form-item prop="username">
<el-input
v-model="formModel.username"
:prefix-icon="User"
size="large"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="formModel.password"
type="password"
:prefix-icon="Lock"
size="large"
show-password
></el-input>
</el-form-item>
<el-form-item prop="repassword">
<el-input
v-model="formModel.repassword"
type="password"
:prefix-icon="Check"
size="large"
show-repassword
></el-input>
</el-form-item>
</div>
<!-- 手机号注册 -->
<div v-else>
<el-form-item prop="phone">
<el-input
v-model="formModel.phone"
:prefix-icon="Phone"
size="large"
placeholder="请输入手机号"
></el-input>
</el-form-item>
<el-form-item prop="code">
<!-- <el-input
v-model="loginForm.password"
type="password"
:prefix-icon="Lock"
size="large"
show-password
></el-input> -->
<input
type="text"
v-model="loginForm.code"
placeholder="请输入验证码"
size="large"
style="
flex: 1;
line-height: 40px;
height: 40px;
border: 1px solid #dcdfe6;
/* color: #dcdfe6; */
"
/>
<div class="p" v-if="countdown > 0">
<p>{{ countdown }} </p>
</div>
<button
class="code"
v-if="countdown <= 0"
@click="startCountdown"
:disabled="countdown > 0"
>
发送验证码
</button>
</el-form-item>
</div>
<!-- <h2>老师</h2> -->
<!-- <el-form-item prop="username">
<el-input
v-model="loginForm.username"
:prefix-icon="User"
size="large"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
:prefix-icon="Lock"
size="large"
show-password
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
:prefix-icon="Lock"
size="large"
show-password
></el-input>
</el-form-item> -->
<el-form-item>
<el-button
:loading="isBtnLoading"
size="large"
class="login-btn"
type="primary"
@click="register"
>
注册
</el-button>
</el-form-item>
<el-form-item class="flex">
<el-link type="info" :underline="false" @click="isRegister = true">
登录
</el-link>
</el-form-item>
</el-form>
</el-col>
<el-col :span="12" :xs="0"></el-col>
</el-row>
</div>
</template>
<script lang="ts" setup>
import {} from 'vue'
import {
reactive,
ref,
onMounted,
onUnmounted,
onBeforeUnmount,
watch,
} from 'vue'
import { User, Lock, Phone, Check } from '@element-plus/icons-vue'
import useUserStore from '@/store/modules/user'
import { useRouter, useRoute } from 'vue-router'
import { userRegisterService } from '@/api/user/index'
//
const isRegister = ref(true)
//
const isToggle = ref(true)
//model,form
const formModel = ref({
username: '',
password: '',
repassword: '',
phone: '',
})
//
let loginForm = reactive({ username: 'admin', password: '111111' })
//
let userStore = useUserStore()
//
const $router = useRouter()
const $route = useRoute()
const isBtnLoading = ref<boolean>(false)
// form
const formRules = ref<any>(null)
//
const rules = {
username: [
{ required: true, message: '用户名不能为空', trigger: 'blur' },
{
max: 10,
min: 5,
message: '用户名长度最小五位最大十位',
trigger: ['change', 'blur'],
},
],
password: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{
pattern: /^\S{6,15}$/,
// pattern
max: 15,
min: 6,
message: '密码长度最小六位最大十五位',
trigger: ['change', 'blur'],
},
],
repassword: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{
pattern: /^\S{6,15}$/,
// pattern
max: 15,
min: 6,
message: '密码长度最小六位最大十五位',
trigger: ['change', 'blur'],
},
{
validater: (rule, value, callback) => {
//valuepassword
if ((value = loginForm.value.password)) {
callback(new Error('两次输入的密码不一致'))
} else {
callback()
}
},
trigger: ['change', 'blur'],
},
],
phone: [
{ required: true, message: '手机号不能为空', trigger: 'blur' },
{
pattern: /^1[3456789]\d{9}$/,
message: '手机号格式不正确',
trigger: 'blur',
// pattern
},
],
}
//
const countdown = ref(0)
let timer
const startCountdown = () => {
countdown.value = 60 //
timer = setInterval(updateCountdown, 1000) //
}
const updateCountdown = () => {
if (countdown.value > 0) {
countdown.value--
} else {
clearInterval(timer) //
}
}
onBeforeUnmount(() => {
clearInterval(timer) //
})
//form
const form = ref()
//
const register = async () => {
await form.value.validate()
// console.log('')
await userRegisterService(formModel.value)
// ElMessage.success('')
isRegister.value = false
}
//
watch(isRegister, () => {
formModel.value = {
username: '',
password: '',
repassword: '',
phone: '',
}
})
const login = async () => {
//
await formRules.value.validate()
try {
isBtnLoading.value = true
await userStore.userLogin(loginForm)
isBtnLoading.value = false
if ($route.query.redirect) {
$router.push($route.query.redirect as string)
} else {
$router.push('/')
}
} catch (error) {
isBtnLoading.value = false
}
}
const keyDown = (e: any) => {
//
if (e.keyCode == 13) {
//
login()
}
}
onMounted(() => {
//@ts-expect-error tsVANTAGLOBE cdn
VANTA.GLOBE({
el: '#your-element-selector',
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.0,
minWidth: 200.0,
scale: 1.0,
scaleMobile: 1.0,
color: 0x5adc,
color2: 0x1efc,
size: 0.8,
backgroundColor: 0xffffff,
})
window.addEventListener('keydown', keyDown)
})
onUnmounted(() => {
window.removeEventListener('keydown', keyDown, false)
})
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.login_container {
width: 100%;
height: 100vh;
position: absolute;
top: 0;
// background: url('../../assets/images/background.jpg') no-repeat;
.loin_form {
position: relative;
top: 30vh;
width: 80%;
// background: url('../../assets/images/login_form.png') no-repeat;
// background-size: cover;
// background-color: #5577ff;
background: rgba(255, 255, 255, 0.1);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
padding: 40px;
border-radius: 10px;
box-shadow: 7px 7px 42px rgba(0, 0, 0, 0.17);
.taggle {
display: flex;
justify-content: space-between;
}
h1 {
font-size: 40px;
color: #5577ff;
}
h2 {
font-size: 20px;
color: #5577ff;
margin: 20px 0;
}
.login-btn {
width: 100%;
}
.p {
height: 40px;
text-align: center;
line-height: 40px;
width: 90px;
}
.code {
height: 40px;
width: 100px;
border: 1px solid #dcdfe6;
// background-color: #dcdfe6;
color: #000;
background: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
display: flex;
justify-content: space-between;
flex-wrap: none;
line-height: 40px;
}
}
.el-form-item {
flex-wrap: none;
}
.content {
display: flex;
justify-content: space-between;
}
}
</style>

@ -0,0 +1,68 @@
<template>
<div>
<el-button type="primary" @click="openDrawer">Open Drawer</el-button>
<el-drawer
title="Drawer Title"
v-model:visible="drawerVisible"
:direction="drawerDirection"
>
<el-table :data="formModel" style="width: 100%">
<el-table-column prop="name" label="Name"></el-table-column>
<el-table-column prop="content" label="Content">
<template v-slot="scope">
<el-input
v-model="scope.row.content"
@input="handleInput(scope.row)"
></el-input>
</template>
</el-table-column>
</el-table>
<template v-slot:footer>
<div>
<el-button @click="cancelEdit">Cancel</el-button>
<el-button type="primary" @click="saveEdit">Save</el-button>
</div>
</template>
</el-drawer>
</div>
</template>
<script>
export default {
data() {
return {
formModel: [
{ name: 'Macbook', content: 9.9 },
{ name: 'iPhone', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
{ name: '小米电脑', content: 9.9 },
],
drawerVisible: false,
drawerDirection: 'ltr',
}
},
methods: {
openDrawer() {
this.drawerVisible = true
},
cancelEdit() {
this.drawerVisible = false
},
saveEdit() {
//
console.log('Save Edit:', this.formModel)
this.drawerVisible = false
},
handleInput(row) {
//
console.log('Handle Input:', row)
},
},
}
</script>
Loading…
Cancel
Save