Commit a47d42d0 authored by tyyin lan's avatar tyyin lan

feat: 登录页面

parent 6934461d
...@@ -3,7 +3,7 @@ VITE_APP_ENV = 'DEV' ...@@ -3,7 +3,7 @@ VITE_APP_ENV = 'DEV'
VITE_APP_NAME = 'DIGITAL_PERSON_FE' VITE_APP_NAME = 'DIGITAL_PERSON_FE'
VITE_APP_THEME_COLOR = '#2468f2' VITE_APP_THEME_COLOR = '#2468f2'
VITE_PORT = 8848 VITE_PORT = 8848
VITE_PUBLIC_PATH = / VITE_PUBLIC_PATH = /fe
VITE_ROUTER_MODE = 'hash' VITE_ROUTER_MODE = 'h5'
VITE_VITEST = true VITE_VITEST = true
VITE_HIDE_HOME = false VITE_HIDE_HOME = false
...@@ -68,6 +68,7 @@ export default [ ...@@ -68,6 +68,7 @@ export default [
'@typescript-eslint/array-type': 'error', '@typescript-eslint/array-type': 'error',
'@unocss/order': 'off', '@unocss/order': 'off',
'unocss/order': 'off',
}, },
}, },
......
...@@ -16,13 +16,16 @@ ...@@ -16,13 +16,16 @@
"preinstall": "npx only-allow pnpm" "preinstall": "npx only-allow pnpm"
}, },
"dependencies": { "dependencies": {
"@icon-park/vue-next": "^1.4.2",
"@iconify/vue": "^4.1.2", "@iconify/vue": "^4.1.2",
"@unocss/reset": "^0.61.9", "@unocss/reset": "^0.61.9",
"@vueuse/core": "^10.11.1", "@vueuse/core": "^10.11.1",
"axios": "^1.7.7", "axios": "^1.7.7",
"nanoid": "^5.0.7", "nanoid": "^5.0.7",
"pinia": "^2.2.2", "pinia": "^2.2.2",
"vue": "^3.5.3", "spark-md5": "^3.0.2",
"validator": "^13.12.0",
"vue": "^3.5.5",
"vue-router": "^4.4.3" "vue-router": "^4.4.3"
}, },
"devDependencies": { "devDependencies": {
...@@ -30,6 +33,8 @@ ...@@ -30,6 +33,8 @@
"@commitlint/config-conventional": "^19.4.1", "@commitlint/config-conventional": "^19.4.1",
"@commitlint/types": "^19.0.3", "@commitlint/types": "^19.0.3",
"@types/node": "^20.16.5", "@types/node": "^20.16.5",
"@types/spark-md5": "^3.0.4",
"@types/validator": "^13.12.1",
"@typescript-eslint/parser": "^7.18.0", "@typescript-eslint/parser": "^7.18.0",
"@unocss/eslint-config": "^0.61.9", "@unocss/eslint-config": "^0.61.9",
"@vitejs/plugin-vue": "^4.6.2", "@vitejs/plugin-vue": "^4.6.2",
......
...@@ -8,15 +8,18 @@ importers: ...@@ -8,15 +8,18 @@ importers:
.: .:
dependencies: dependencies:
'@icon-park/vue-next':
specifier: ^1.4.2
version: 1.4.2(vue@3.5.5(typescript@5.5.4))
'@iconify/vue': '@iconify/vue':
specifier: ^4.1.2 specifier: ^4.1.2
version: 4.1.2(vue@3.5.3(typescript@5.5.4)) version: 4.1.2(vue@3.5.5(typescript@5.5.4))
'@unocss/reset': '@unocss/reset':
specifier: ^0.61.9 specifier: ^0.61.9
version: 0.61.9 version: 0.61.9
'@vueuse/core': '@vueuse/core':
specifier: ^10.11.1 specifier: ^10.11.1
version: 10.11.1(vue@3.5.3(typescript@5.5.4)) version: 10.11.1(vue@3.5.5(typescript@5.5.4))
axios: axios:
specifier: ^1.7.7 specifier: ^1.7.7
version: 1.7.7 version: 1.7.7
...@@ -25,13 +28,19 @@ importers: ...@@ -25,13 +28,19 @@ importers:
version: 5.0.7 version: 5.0.7
pinia: pinia:
specifier: ^2.2.2 specifier: ^2.2.2
version: 2.2.2(typescript@5.5.4)(vue@3.5.3(typescript@5.5.4)) version: 2.2.2(typescript@5.5.4)(vue@3.5.5(typescript@5.5.4))
spark-md5:
specifier: ^3.0.2
version: 3.0.2
validator:
specifier: ^13.12.0
version: 13.12.0
vue: vue:
specifier: ^3.5.3 specifier: ^3.5.5
version: 3.5.3(typescript@5.5.4) version: 3.5.5(typescript@5.5.4)
vue-router: vue-router:
specifier: ^4.4.3 specifier: ^4.4.3
version: 4.4.3(vue@3.5.3(typescript@5.5.4)) version: 4.4.3(vue@3.5.5(typescript@5.5.4))
devDependencies: devDependencies:
'@commitlint/cli': '@commitlint/cli':
specifier: ^19.4.1 specifier: ^19.4.1
...@@ -45,6 +54,12 @@ importers: ...@@ -45,6 +54,12 @@ importers:
'@types/node': '@types/node':
specifier: ^20.16.5 specifier: ^20.16.5
version: 20.16.5 version: 20.16.5
'@types/spark-md5':
specifier: ^3.0.4
version: 3.0.4
'@types/validator':
specifier: ^13.12.1
version: 13.12.1
'@typescript-eslint/parser': '@typescript-eslint/parser':
specifier: ^7.18.0 specifier: ^7.18.0
version: 7.18.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) version: 7.18.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)
...@@ -53,7 +68,7 @@ importers: ...@@ -53,7 +68,7 @@ importers:
version: 0.61.9(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) version: 0.61.9(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)
'@vitejs/plugin-vue': '@vitejs/plugin-vue':
specifier: ^4.6.2 specifier: ^4.6.2
version: 4.6.2(vite@5.4.3(@types/node@20.16.5)(sass@1.78.0))(vue@3.5.3(typescript@5.5.4)) version: 4.6.2(vite@5.4.3(@types/node@20.16.5)(sass@1.78.0))(vue@3.5.5(typescript@5.5.4))
autoprefixer: autoprefixer:
specifier: ^10.4.20 specifier: ^10.4.20
version: 10.4.20(postcss@8.4.45) version: 10.4.20(postcss@8.4.45)
...@@ -80,7 +95,7 @@ importers: ...@@ -80,7 +95,7 @@ importers:
version: 15.2.10 version: 15.2.10
naive-ui: naive-ui:
specifier: ^2.39.0 specifier: ^2.39.0
version: 2.39.0(vue@3.5.3(typescript@5.5.4)) version: 2.39.0(vue@3.5.5(typescript@5.5.4))
postcss: postcss:
specifier: ^8.4.45 specifier: ^8.4.45
version: 8.4.45 version: 8.4.45
...@@ -131,10 +146,10 @@ importers: ...@@ -131,10 +146,10 @@ importers:
version: 0.61.9(postcss@8.4.45)(rollup@4.21.2)(vite@5.4.3(@types/node@20.16.5)(sass@1.78.0)) version: 0.61.9(postcss@8.4.45)(rollup@4.21.2)(vite@5.4.3(@types/node@20.16.5)(sass@1.78.0))
unplugin-auto-import: unplugin-auto-import:
specifier: ^0.17.8 specifier: ^0.17.8
version: 0.17.8(@vueuse/core@10.11.1(vue@3.5.3(typescript@5.5.4)))(rollup@4.21.2)(webpack-sources@3.2.3) version: 0.17.8(@vueuse/core@10.11.1(vue@3.5.5(typescript@5.5.4)))(rollup@4.21.2)(webpack-sources@3.2.3)
unplugin-vue-components: unplugin-vue-components:
specifier: ^0.26.0 specifier: ^0.26.0
version: 0.26.0(@babel/parser@7.25.6)(rollup@4.21.2)(vue@3.5.3(typescript@5.5.4))(webpack-sources@3.2.3) version: 0.26.0(@babel/parser@7.25.6)(rollup@4.21.2)(vue@3.5.5(typescript@5.5.4))(webpack-sources@3.2.3)
vite: vite:
specifier: ^5.4.3 specifier: ^5.4.3
version: 5.4.3(@types/node@20.16.5)(sass@1.78.0) version: 5.4.3(@types/node@20.16.5)(sass@1.78.0)
...@@ -721,6 +736,12 @@ packages: ...@@ -721,6 +736,12 @@ packages:
resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
engines: {node: '>=18.18'} engines: {node: '>=18.18'}
'@icon-park/vue-next@1.4.2':
resolution: {integrity: sha512-+QklF255wkfBOabY+xw6FAI0Bwln/RhdwCunNy/9sKdKuChtaU67QZqU67KGAvZUTeeBgsL+yaHHxqfQeGZXEQ==}
engines: {node: '>= 8.0.0', npm: '>= 5.0.0'}
peerDependencies:
vue: 3.x
'@iconify/types@2.0.0': '@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
...@@ -888,6 +909,12 @@ packages: ...@@ -888,6 +909,12 @@ packages:
'@types/node@20.16.5': '@types/node@20.16.5':
resolution: {integrity: sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==} resolution: {integrity: sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==}
'@types/spark-md5@3.0.4':
resolution: {integrity: sha512-qtOaDz+IXiNndPgYb6t1YoutnGvFRtWSNzpVjkAPCfB2UzTyybuD4Tjgs7VgRawum3JnJNRwNQd4N//SvrHg1Q==}
'@types/validator@13.12.1':
resolution: {integrity: sha512-w0URwf7BQb0rD/EuiG12KP0bailHKHP5YVviJG9zw3ykAokL0TuxU2TUqMB7EwZ59bDHYdeTIvjI5m0S7qHfOA==}
'@types/web-bluetooth@0.0.20': '@types/web-bluetooth@0.0.20':
resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
...@@ -1059,17 +1086,17 @@ packages: ...@@ -1059,17 +1086,17 @@ packages:
'@volar/typescript@2.4.2': '@volar/typescript@2.4.2':
resolution: {integrity: sha512-m2uZduhaHO1SZuagi30OsjI/X1gwkaEAC+9wT/nCNAtJ5FqXEkKvUncHmffG7ESDZPlFFUBK4vJ0D9Hfr+f2EA==} resolution: {integrity: sha512-m2uZduhaHO1SZuagi30OsjI/X1gwkaEAC+9wT/nCNAtJ5FqXEkKvUncHmffG7ESDZPlFFUBK4vJ0D9Hfr+f2EA==}
'@vue/compiler-core@3.5.3': '@vue/compiler-core@3.5.5':
resolution: {integrity: sha512-adAfy9boPkP233NTyvLbGEqVuIfK/R0ZsBsIOW4BZNfb4BRpRW41Do1u+ozJpsb+mdoy80O20IzAsHaihRb5qA==} resolution: {integrity: sha512-ZrxcY8JMoV+kgDrmRwlDufz0SjDZ7jfoNZiIBluAACMBmgr55o/jTbxnyrccH6VSJXnFaDI4Ik1UFCiq9r8i7w==}
'@vue/compiler-dom@3.5.3': '@vue/compiler-dom@3.5.5':
resolution: {integrity: sha512-wnzFArg9zpvk/811CDOZOadJRugf1Bgl/TQ3RfV4nKfSPok4hi0w10ziYUQR6LnnBAUlEXYLUfZ71Oj9ds/+QA==} resolution: {integrity: sha512-HSvK5q1gmBbxRse3S0Wt34RcKuOyjDJKDDMuF3i7NC+QkDFrbAqw8NnrEm/z7zFDxWZa4/5eUwsBOMQzm1RHBA==}
'@vue/compiler-sfc@3.5.3': '@vue/compiler-sfc@3.5.5':
resolution: {integrity: sha512-P3uATLny2tfyvMB04OQFe7Sczteno7SLFxwrOA/dw01pBWQHB5HL15a8PosoNX2aG/EAMGqnXTu+1LnmzFhpTQ==} resolution: {integrity: sha512-MzBHDxwZhgQPHrwJ5tj92gdTYRCuPDSZr8PY3+JFv8cv2UD5/WayH5yo0kKCkKfrtJhc39jNSMityHrkMSbfnA==}
'@vue/compiler-ssr@3.5.3': '@vue/compiler-ssr@3.5.5':
resolution: {integrity: sha512-F/5f+r2WzL/2YAPl7UlKcJWHrvoZN8XwEBLnT7S4BXwncH25iDOabhO2M2DWioyTguJAGavDOawejkFXj8EM1w==} resolution: {integrity: sha512-oFasHnpv/upubjJEmqiTKQYb4qS3ziJddf4UVWuFw6ebk/QTrTUc+AUoTJdo39x9g+AOQBzhOU0ICCRuUjvkmw==}
'@vue/compiler-vue2@2.7.16': '@vue/compiler-vue2@2.7.16':
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
...@@ -1085,22 +1112,22 @@ packages: ...@@ -1085,22 +1112,22 @@ packages:
typescript: typescript:
optional: true optional: true
'@vue/reactivity@3.5.3': '@vue/reactivity@3.5.5':
resolution: {integrity: sha512-2w61UnRWTP7+rj1H/j6FH706gRBHdFVpIqEkSDAyIpafBXYH8xt4gttstbbCWdU3OlcSWO8/3mbKl/93/HSMpw==} resolution: {integrity: sha512-V4tTWElZQhT73PSK3Wnax9R9m4qvMX+LeKHnfylZc6SLh4Jc5/BPakp6e3zEhKWi5AN8TDzRkGnLkp8OqycYng==}
'@vue/runtime-core@3.5.3': '@vue/runtime-core@3.5.5':
resolution: {integrity: sha512-5b2AQw5OZlmCzSsSBWYoZOsy75N4UdMWenTfDdI5bAzXnuVR7iR8Q4AOzQm2OGoA41xjk53VQKrqQhOz2ktWaw==} resolution: {integrity: sha512-2/CFaRN17jgsXy4MpigWFBCAMmLkXPb4CjaHrndglwYSra7ajvkH2cat21dscuXaH91G8fXAeg5gCyxWJ+wCRA==}
'@vue/runtime-dom@3.5.3': '@vue/runtime-dom@3.5.5':
resolution: {integrity: sha512-wPR1DEGc3XnQ7yHbmkTt3GoY0cEnVGQnARRdAkDzZ8MbUKEs26gogCQo6AOvvgahfjIcnvWJzkZArQ1fmWjcSg==} resolution: {integrity: sha512-0bQGgCuL+4Muz5PsCLgF4Ata9BTdhHi5VjsxtTDyI0Wy4MgoSvBGaA6bDc7W7CGgZOyirf9LNeetMYHQ05pgpw==}
'@vue/server-renderer@3.5.3': '@vue/server-renderer@3.5.5':
resolution: {integrity: sha512-28volmaZVG2PGO3V3+gBPKoSHvLlE8FGfG/GKXKkjjfxLuj/50B/0OQGakM/g6ehQeqCrZYM4eHC4Ks48eig1Q==} resolution: {integrity: sha512-XjRamLIq5f47cxgy+hiX7zUIY+4RHdPDVrPvvMDAUTdW5RJWX/S0ji/rCbm3LWTT/9Co9bvQME8ZI15ahL4/Qw==}
peerDependencies: peerDependencies:
vue: 3.5.3 vue: 3.5.5
'@vue/shared@3.5.3': '@vue/shared@3.5.5':
resolution: {integrity: sha512-Jp2v8nylKBT+PlOUjun2Wp/f++TfJVFjshLzNtJDdmFJabJa7noGMncqXRM1vXGX+Yo2V7WykQFNxusSim8SCA==} resolution: {integrity: sha512-0KyMXyEgnmFAs6rNUL+6eUHtUCqCaNrVd+AW3MX3LyA0Yry5SA0Km03CDKiOua1x1WWnIr+W9+S0GMFoSDWERQ==}
'@vueuse/core@10.11.1': '@vueuse/core@10.11.1':
resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==} resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==}
...@@ -2471,10 +2498,17 @@ packages: ...@@ -2471,10 +2498,17 @@ packages:
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
source-map@0.7.4: source-map@0.7.4:
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
spark-md5@3.0.2:
resolution: {integrity: sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==}
split2@4.2.0: split2@4.2.0:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'} engines: {node: '>= 10.x'}
...@@ -2754,6 +2788,10 @@ packages: ...@@ -2754,6 +2788,10 @@ packages:
util-deprecate@1.0.2: util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
validator@13.12.0:
resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==}
engines: {node: '>= 0.10'}
vdirs@0.1.8: vdirs@0.1.8:
resolution: {integrity: sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==} resolution: {integrity: sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==}
peerDependencies: peerDependencies:
...@@ -2881,8 +2919,8 @@ packages: ...@@ -2881,8 +2919,8 @@ packages:
peerDependencies: peerDependencies:
typescript: '>=5.0.0' typescript: '>=5.0.0'
vue@3.5.3: vue@3.5.5:
resolution: {integrity: sha512-xvRbd0HpuLovYbOHXRHlSBsSvmUJbo0pzbkKTApWnQGf3/cu5Z39mQeA5cZdLRVIoNf3zI6MSoOgHUT5i2jO+Q==} resolution: {integrity: sha512-ybC+xn67K4+df1yVeov4UjBGyVcXM0a1g7JVZr+pWVUX3xF6ntXU0wIjkTkduZBUIpxTlsftJSxz2kwhsT7dgA==}
peerDependencies: peerDependencies:
typescript: '*' typescript: '*'
peerDependenciesMeta: peerDependenciesMeta:
...@@ -3290,9 +3328,9 @@ snapshots: ...@@ -3290,9 +3328,9 @@ snapshots:
dependencies: dependencies:
css-render: 0.15.14 css-render: 0.15.14
'@css-render/vue3-ssr@0.15.14(vue@3.5.3(typescript@5.5.4))': '@css-render/vue3-ssr@0.15.14(vue@3.5.5(typescript@5.5.4))':
dependencies: dependencies:
vue: 3.5.3(typescript@5.5.4) vue: 3.5.5(typescript@5.5.4)
'@csstools/css-parser-algorithms@3.0.1(@csstools/css-tokenizer@3.0.1)': '@csstools/css-parser-algorithms@3.0.1(@csstools/css-tokenizer@3.0.1)':
dependencies: dependencies:
...@@ -3491,6 +3529,10 @@ snapshots: ...@@ -3491,6 +3529,10 @@ snapshots:
'@humanwhocodes/retry@0.3.0': {} '@humanwhocodes/retry@0.3.0': {}
'@icon-park/vue-next@1.4.2(vue@3.5.5(typescript@5.5.4))':
dependencies:
vue: 3.5.5(typescript@5.5.4)
'@iconify/types@2.0.0': {} '@iconify/types@2.0.0': {}
'@iconify/utils@2.1.32': '@iconify/utils@2.1.32':
...@@ -3505,10 +3547,10 @@ snapshots: ...@@ -3505,10 +3547,10 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@iconify/vue@4.1.2(vue@3.5.3(typescript@5.5.4))': '@iconify/vue@4.1.2(vue@3.5.5(typescript@5.5.4))':
dependencies: dependencies:
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
vue: 3.5.3(typescript@5.5.4) vue: 3.5.5(typescript@5.5.4)
'@jridgewell/gen-mapping@0.3.5': '@jridgewell/gen-mapping@0.3.5':
dependencies: dependencies:
...@@ -3619,6 +3661,10 @@ snapshots: ...@@ -3619,6 +3661,10 @@ snapshots:
dependencies: dependencies:
undici-types: 6.19.8 undici-types: 6.19.8
'@types/spark-md5@3.0.4': {}
'@types/validator@13.12.1': {}
'@types/web-bluetooth@0.0.20': {} '@types/web-bluetooth@0.0.20': {}
'@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)': '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)':
...@@ -3882,10 +3928,10 @@ snapshots: ...@@ -3882,10 +3928,10 @@ snapshots:
- rollup - rollup
- supports-color - supports-color
'@vitejs/plugin-vue@4.6.2(vite@5.4.3(@types/node@20.16.5)(sass@1.78.0))(vue@3.5.3(typescript@5.5.4))': '@vitejs/plugin-vue@4.6.2(vite@5.4.3(@types/node@20.16.5)(sass@1.78.0))(vue@3.5.5(typescript@5.5.4))':
dependencies: dependencies:
vite: 5.4.3(@types/node@20.16.5)(sass@1.78.0) vite: 5.4.3(@types/node@20.16.5)(sass@1.78.0)
vue: 3.5.3(typescript@5.5.4) vue: 3.5.5(typescript@5.5.4)
'@volar/language-core@2.4.2': '@volar/language-core@2.4.2':
dependencies: dependencies:
...@@ -3899,35 +3945,35 @@ snapshots: ...@@ -3899,35 +3945,35 @@ snapshots:
path-browserify: 1.0.1 path-browserify: 1.0.1
vscode-uri: 3.0.8 vscode-uri: 3.0.8
'@vue/compiler-core@3.5.3': '@vue/compiler-core@3.5.5':
dependencies: dependencies:
'@babel/parser': 7.25.6 '@babel/parser': 7.25.6
'@vue/shared': 3.5.3 '@vue/shared': 3.5.5
entities: 4.5.0 entities: 4.5.0
estree-walker: 2.0.2 estree-walker: 2.0.2
source-map-js: 1.2.0 source-map-js: 1.2.1
'@vue/compiler-dom@3.5.3': '@vue/compiler-dom@3.5.5':
dependencies: dependencies:
'@vue/compiler-core': 3.5.3 '@vue/compiler-core': 3.5.5
'@vue/shared': 3.5.3 '@vue/shared': 3.5.5
'@vue/compiler-sfc@3.5.3': '@vue/compiler-sfc@3.5.5':
dependencies: dependencies:
'@babel/parser': 7.25.6 '@babel/parser': 7.25.6
'@vue/compiler-core': 3.5.3 '@vue/compiler-core': 3.5.5
'@vue/compiler-dom': 3.5.3 '@vue/compiler-dom': 3.5.5
'@vue/compiler-ssr': 3.5.3 '@vue/compiler-ssr': 3.5.5
'@vue/shared': 3.5.3 '@vue/shared': 3.5.5
estree-walker: 2.0.2 estree-walker: 2.0.2
magic-string: 0.30.11 magic-string: 0.30.11
postcss: 8.4.45 postcss: 8.4.45
source-map-js: 1.2.0 source-map-js: 1.2.1
'@vue/compiler-ssr@3.5.3': '@vue/compiler-ssr@3.5.5':
dependencies: dependencies:
'@vue/compiler-dom': 3.5.3 '@vue/compiler-dom': 3.5.5
'@vue/shared': 3.5.3 '@vue/shared': 3.5.5
'@vue/compiler-vue2@2.7.16': '@vue/compiler-vue2@2.7.16':
dependencies: dependencies:
...@@ -3939,9 +3985,9 @@ snapshots: ...@@ -3939,9 +3985,9 @@ snapshots:
'@vue/language-core@2.0.29(typescript@5.5.4)': '@vue/language-core@2.0.29(typescript@5.5.4)':
dependencies: dependencies:
'@volar/language-core': 2.4.2 '@volar/language-core': 2.4.2
'@vue/compiler-dom': 3.5.3 '@vue/compiler-dom': 3.5.5
'@vue/compiler-vue2': 2.7.16 '@vue/compiler-vue2': 2.7.16
'@vue/shared': 3.5.3 '@vue/shared': 3.5.5
computeds: 0.0.1 computeds: 0.0.1
minimatch: 9.0.5 minimatch: 9.0.5
muggle-string: 0.4.1 muggle-string: 0.4.1
...@@ -3949,45 +3995,45 @@ snapshots: ...@@ -3949,45 +3995,45 @@ snapshots:
optionalDependencies: optionalDependencies:
typescript: 5.5.4 typescript: 5.5.4
'@vue/reactivity@3.5.3': '@vue/reactivity@3.5.5':
dependencies: dependencies:
'@vue/shared': 3.5.3 '@vue/shared': 3.5.5
'@vue/runtime-core@3.5.3': '@vue/runtime-core@3.5.5':
dependencies: dependencies:
'@vue/reactivity': 3.5.3 '@vue/reactivity': 3.5.5
'@vue/shared': 3.5.3 '@vue/shared': 3.5.5
'@vue/runtime-dom@3.5.3': '@vue/runtime-dom@3.5.5':
dependencies: dependencies:
'@vue/reactivity': 3.5.3 '@vue/reactivity': 3.5.5
'@vue/runtime-core': 3.5.3 '@vue/runtime-core': 3.5.5
'@vue/shared': 3.5.3 '@vue/shared': 3.5.5
csstype: 3.1.3 csstype: 3.1.3
'@vue/server-renderer@3.5.3(vue@3.5.3(typescript@5.5.4))': '@vue/server-renderer@3.5.5(vue@3.5.5(typescript@5.5.4))':
dependencies: dependencies:
'@vue/compiler-ssr': 3.5.3 '@vue/compiler-ssr': 3.5.5
'@vue/shared': 3.5.3 '@vue/shared': 3.5.5
vue: 3.5.3(typescript@5.5.4) vue: 3.5.5(typescript@5.5.4)
'@vue/shared@3.5.3': {} '@vue/shared@3.5.5': {}
'@vueuse/core@10.11.1(vue@3.5.3(typescript@5.5.4))': '@vueuse/core@10.11.1(vue@3.5.5(typescript@5.5.4))':
dependencies: dependencies:
'@types/web-bluetooth': 0.0.20 '@types/web-bluetooth': 0.0.20
'@vueuse/metadata': 10.11.1 '@vueuse/metadata': 10.11.1
'@vueuse/shared': 10.11.1(vue@3.5.3(typescript@5.5.4)) '@vueuse/shared': 10.11.1(vue@3.5.5(typescript@5.5.4))
vue-demi: 0.14.10(vue@3.5.3(typescript@5.5.4)) vue-demi: 0.14.10(vue@3.5.5(typescript@5.5.4))
transitivePeerDependencies: transitivePeerDependencies:
- '@vue/composition-api' - '@vue/composition-api'
- vue - vue
'@vueuse/metadata@10.11.1': {} '@vueuse/metadata@10.11.1': {}
'@vueuse/shared@10.11.1(vue@3.5.3(typescript@5.5.4))': '@vueuse/shared@10.11.1(vue@3.5.5(typescript@5.5.4))':
dependencies: dependencies:
vue-demi: 0.14.10(vue@3.5.3(typescript@5.5.4)) vue-demi: 0.14.10(vue@3.5.5(typescript@5.5.4))
transitivePeerDependencies: transitivePeerDependencies:
- '@vue/composition-api' - '@vue/composition-api'
- vue - vue
...@@ -4939,10 +4985,10 @@ snapshots: ...@@ -4939,10 +4985,10 @@ snapshots:
muggle-string@0.4.1: {} muggle-string@0.4.1: {}
naive-ui@2.39.0(vue@3.5.3(typescript@5.5.4)): naive-ui@2.39.0(vue@3.5.5(typescript@5.5.4)):
dependencies: dependencies:
'@css-render/plugin-bem': 0.15.14(css-render@0.15.14) '@css-render/plugin-bem': 0.15.14(css-render@0.15.14)
'@css-render/vue3-ssr': 0.15.14(vue@3.5.3(typescript@5.5.4)) '@css-render/vue3-ssr': 0.15.14(vue@3.5.5(typescript@5.5.4))
'@types/katex': 0.16.7 '@types/katex': 0.16.7
'@types/lodash': 4.17.7 '@types/lodash': 4.17.7
'@types/lodash-es': 4.17.12 '@types/lodash-es': 4.17.12
...@@ -4957,10 +5003,10 @@ snapshots: ...@@ -4957,10 +5003,10 @@ snapshots:
lodash-es: 4.17.21 lodash-es: 4.17.21
seemly: 0.3.8 seemly: 0.3.8
treemate: 0.3.11 treemate: 0.3.11
vdirs: 0.1.8(vue@3.5.3(typescript@5.5.4)) vdirs: 0.1.8(vue@3.5.5(typescript@5.5.4))
vooks: 0.2.12(vue@3.5.3(typescript@5.5.4)) vooks: 0.2.12(vue@3.5.5(typescript@5.5.4))
vue: 3.5.3(typescript@5.5.4) vue: 3.5.5(typescript@5.5.4)
vueuc: 0.4.58(vue@3.5.3(typescript@5.5.4)) vueuc: 0.4.58(vue@3.5.5(typescript@5.5.4))
nanoid@3.3.7: {} nanoid@3.3.7: {}
...@@ -5070,11 +5116,11 @@ snapshots: ...@@ -5070,11 +5116,11 @@ snapshots:
pidtree@0.6.0: {} pidtree@0.6.0: {}
pinia@2.2.2(typescript@5.5.4)(vue@3.5.3(typescript@5.5.4)): pinia@2.2.2(typescript@5.5.4)(vue@3.5.5(typescript@5.5.4)):
dependencies: dependencies:
'@vue/devtools-api': 6.6.3 '@vue/devtools-api': 6.6.3
vue: 3.5.3(typescript@5.5.4) vue: 3.5.5(typescript@5.5.4)
vue-demi: 0.14.10(vue@3.5.3(typescript@5.5.4)) vue-demi: 0.14.10(vue@3.5.5(typescript@5.5.4))
optionalDependencies: optionalDependencies:
typescript: 5.5.4 typescript: 5.5.4
...@@ -5256,8 +5302,12 @@ snapshots: ...@@ -5256,8 +5302,12 @@ snapshots:
source-map-js@1.2.0: {} source-map-js@1.2.0: {}
source-map-js@1.2.1: {}
source-map@0.7.4: {} source-map@0.7.4: {}
spark-md5@3.0.2: {}
split2@4.2.0: {} split2@4.2.0: {}
string-argv@0.3.2: {} string-argv@0.3.2: {}
...@@ -5544,7 +5594,7 @@ snapshots: ...@@ -5544,7 +5594,7 @@ snapshots:
- rollup - rollup
- supports-color - supports-color
unplugin-auto-import@0.17.8(@vueuse/core@10.11.1(vue@3.5.3(typescript@5.5.4)))(rollup@4.21.2)(webpack-sources@3.2.3): unplugin-auto-import@0.17.8(@vueuse/core@10.11.1(vue@3.5.5(typescript@5.5.4)))(rollup@4.21.2)(webpack-sources@3.2.3):
dependencies: dependencies:
'@antfu/utils': 0.7.10 '@antfu/utils': 0.7.10
'@rollup/pluginutils': 5.1.0(rollup@4.21.2) '@rollup/pluginutils': 5.1.0(rollup@4.21.2)
...@@ -5555,12 +5605,12 @@ snapshots: ...@@ -5555,12 +5605,12 @@ snapshots:
unimport: 3.11.1(rollup@4.21.2)(webpack-sources@3.2.3) unimport: 3.11.1(rollup@4.21.2)(webpack-sources@3.2.3)
unplugin: 1.13.1(webpack-sources@3.2.3) unplugin: 1.13.1(webpack-sources@3.2.3)
optionalDependencies: optionalDependencies:
'@vueuse/core': 10.11.1(vue@3.5.3(typescript@5.5.4)) '@vueuse/core': 10.11.1(vue@3.5.5(typescript@5.5.4))
transitivePeerDependencies: transitivePeerDependencies:
- rollup - rollup
- webpack-sources - webpack-sources
unplugin-vue-components@0.26.0(@babel/parser@7.25.6)(rollup@4.21.2)(vue@3.5.3(typescript@5.5.4))(webpack-sources@3.2.3): unplugin-vue-components@0.26.0(@babel/parser@7.25.6)(rollup@4.21.2)(vue@3.5.5(typescript@5.5.4))(webpack-sources@3.2.3):
dependencies: dependencies:
'@antfu/utils': 0.7.10 '@antfu/utils': 0.7.10
'@rollup/pluginutils': 5.1.0(rollup@4.21.2) '@rollup/pluginutils': 5.1.0(rollup@4.21.2)
...@@ -5572,7 +5622,7 @@ snapshots: ...@@ -5572,7 +5622,7 @@ snapshots:
minimatch: 9.0.5 minimatch: 9.0.5
resolve: 1.22.8 resolve: 1.22.8
unplugin: 1.13.1(webpack-sources@3.2.3) unplugin: 1.13.1(webpack-sources@3.2.3)
vue: 3.5.3(typescript@5.5.4) vue: 3.5.5(typescript@5.5.4)
optionalDependencies: optionalDependencies:
'@babel/parser': 7.25.6 '@babel/parser': 7.25.6
transitivePeerDependencies: transitivePeerDependencies:
...@@ -5599,10 +5649,12 @@ snapshots: ...@@ -5599,10 +5649,12 @@ snapshots:
util-deprecate@1.0.2: {} util-deprecate@1.0.2: {}
vdirs@0.1.8(vue@3.5.3(typescript@5.5.4)): validator@13.12.0: {}
vdirs@0.1.8(vue@3.5.5(typescript@5.5.4)):
dependencies: dependencies:
evtd: 0.2.4 evtd: 0.2.4
vue: 3.5.3(typescript@5.5.4) vue: 3.5.5(typescript@5.5.4)
vite-plugin-checker@0.7.2(eslint@9.9.1(jiti@1.21.6))(optionator@0.9.4)(stylelint@16.9.0(typescript@5.5.4))(typescript@5.5.4)(vite@5.4.3(@types/node@20.16.5)(sass@1.78.0))(vue-tsc@2.0.29(typescript@5.5.4)): vite-plugin-checker@0.7.2(eslint@9.9.1(jiti@1.21.6))(optionator@0.9.4)(stylelint@16.9.0(typescript@5.5.4))(typescript@5.5.4)(vite@5.4.3(@types/node@20.16.5)(sass@1.78.0))(vue-tsc@2.0.29(typescript@5.5.4)):
dependencies: dependencies:
...@@ -5638,10 +5690,10 @@ snapshots: ...@@ -5638,10 +5690,10 @@ snapshots:
fsevents: 2.3.3 fsevents: 2.3.3
sass: 1.78.0 sass: 1.78.0
vooks@0.2.12(vue@3.5.3(typescript@5.5.4)): vooks@0.2.12(vue@3.5.5(typescript@5.5.4)):
dependencies: dependencies:
evtd: 0.2.4 evtd: 0.2.4
vue: 3.5.3(typescript@5.5.4) vue: 3.5.5(typescript@5.5.4)
vscode-jsonrpc@6.0.0: {} vscode-jsonrpc@6.0.0: {}
...@@ -5666,9 +5718,9 @@ snapshots: ...@@ -5666,9 +5718,9 @@ snapshots:
vscode-uri@3.0.8: {} vscode-uri@3.0.8: {}
vue-demi@0.14.10(vue@3.5.3(typescript@5.5.4)): vue-demi@0.14.10(vue@3.5.5(typescript@5.5.4)):
dependencies: dependencies:
vue: 3.5.3(typescript@5.5.4) vue: 3.5.5(typescript@5.5.4)
vue-eslint-parser@9.4.3(eslint@9.9.1(jiti@1.21.6)): vue-eslint-parser@9.4.3(eslint@9.9.1(jiti@1.21.6)):
dependencies: dependencies:
...@@ -5683,10 +5735,10 @@ snapshots: ...@@ -5683,10 +5735,10 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
vue-router@4.4.3(vue@3.5.3(typescript@5.5.4)): vue-router@4.4.3(vue@3.5.5(typescript@5.5.4)):
dependencies: dependencies:
'@vue/devtools-api': 6.6.3 '@vue/devtools-api': 6.6.3
vue: 3.5.3(typescript@5.5.4) vue: 3.5.5(typescript@5.5.4)
vue-tsc@2.0.29(typescript@5.5.4): vue-tsc@2.0.29(typescript@5.5.4):
dependencies: dependencies:
...@@ -5695,26 +5747,26 @@ snapshots: ...@@ -5695,26 +5747,26 @@ snapshots:
semver: 7.6.3 semver: 7.6.3
typescript: 5.5.4 typescript: 5.5.4
vue@3.5.3(typescript@5.5.4): vue@3.5.5(typescript@5.5.4):
dependencies: dependencies:
'@vue/compiler-dom': 3.5.3 '@vue/compiler-dom': 3.5.5
'@vue/compiler-sfc': 3.5.3 '@vue/compiler-sfc': 3.5.5
'@vue/runtime-dom': 3.5.3 '@vue/runtime-dom': 3.5.5
'@vue/server-renderer': 3.5.3(vue@3.5.3(typescript@5.5.4)) '@vue/server-renderer': 3.5.5(vue@3.5.5(typescript@5.5.4))
'@vue/shared': 3.5.3 '@vue/shared': 3.5.5
optionalDependencies: optionalDependencies:
typescript: 5.5.4 typescript: 5.5.4
vueuc@0.4.58(vue@3.5.3(typescript@5.5.4)): vueuc@0.4.58(vue@3.5.5(typescript@5.5.4)):
dependencies: dependencies:
'@css-render/vue3-ssr': 0.15.14(vue@3.5.3(typescript@5.5.4)) '@css-render/vue3-ssr': 0.15.14(vue@3.5.5(typescript@5.5.4))
'@juggle/resize-observer': 3.4.0 '@juggle/resize-observer': 3.4.0
css-render: 0.15.14 css-render: 0.15.14
evtd: 0.2.4 evtd: 0.2.4
seemly: 0.3.8 seemly: 0.3.8
vdirs: 0.1.8(vue@3.5.3(typescript@5.5.4)) vdirs: 0.1.8(vue@3.5.5(typescript@5.5.4))
vooks: 0.2.12(vue@3.5.3(typescript@5.5.4)) vooks: 0.2.12(vue@3.5.5(typescript@5.5.4))
vue: 3.5.3(typescript@5.5.4) vue: 3.5.5(typescript@5.5.4)
webpack-sources@3.2.3: webpack-sources@3.2.3:
optional: true optional: true
......
import { request } from '@/utils/request' import { request } from '@/utils/request'
export function fetchLogin<T>(payload: { username: string; password: string }) { export function fetchLogin<T>(payload: {
return request.post<T>(`/oauth/auth?username=${payload.username}&password=${payload.password}`, null, { loginChannel: 'MEMBER_PLATFOMR_SMS' | 'MEMBER_PLATFOMR_EMAIL' | 'MEMBER_PLATFOMR_PW'
ignoreErrorCheck: true, account: string
}) password?: string
authCode?: string
}) {
return request.post<T>('/bizMemberInfoRest/doLogin.json', payload)
}
export function fetchSMSCode<T>(phoneNumber: string) {
return request.post<T>(`/smsRest/smsDelivered.json?phone=${phoneNumber}`)
}
export function fetchEmailCode<T>(emailAddress: string) {
return request.post<T>(`/sendEmailRest/sendEmailCode.json?emailAddress=${emailAddress}`)
} }
export const BASE_URLS: Record<'DEV' | 'PROD', string> = { export const BASE_URLS: Record<'DEV' | 'PROD', string> = {
DEV: '', DEV: 'https://poc-sit.gsstcloud.com',
PROD: '', PROD: '',
} }
...@@ -3,7 +3,7 @@ import { GlobalThemeOverrides } from 'naive-ui' ...@@ -3,7 +3,7 @@ import { GlobalThemeOverrides } from 'naive-ui'
export const themeOverrides: GlobalThemeOverrides = { export const themeOverrides: GlobalThemeOverrides = {
common: { common: {
primaryColor: '#2468f2', primaryColor: '#2468f2',
primaryColorHover: '#45a9bb', primaryColorHover: '#528eff',
primaryColorPressed: '#398c9b', primaryColorPressed: '#398c9b',
primaryColorSuppl: '#45a9bb', primaryColorSuppl: '#45a9bb',
}, },
......
...@@ -5,25 +5,28 @@ import { useUserStore } from '@/store/modules/user' ...@@ -5,25 +5,28 @@ import { useUserStore } from '@/store/modules/user'
const whitePathList = ['/login'] const whitePathList = ['/login']
export function createRouterGuards(router: Router) { export function createRouterGuards(router: Router) {
router.beforeEach((to) => { router.beforeEach((to, _from, next) => {
window.$loadingBar.start() window.$loadingBar.start()
const userStore = useUserStore() const userStore = useUserStore()
if (userStore.isLogin && to.fullPath === '/login') { if (userStore.isLogin && to.name === 'Login') {
return false next({ name: 'Root' })
return
} }
// 白名单直接跳过 // 白名单直接跳过
if (whitePathList.includes(to.path)) { if (whitePathList.includes(to.path)) {
return true next()
return
} }
if (!userStore.isLogin && !whitePathList.includes(to.fullPath)) { if (!userStore.isLogin && !whitePathList.includes(to.fullPath)) {
return { path: '/login', query: { redirect: encodeURIComponent(to.fullPath) } } next({ path: '/login', query: { redirect: encodeURIComponent(to.fullPath) } })
return
} }
return true next()
}) })
router.afterEach((to) => { router.afterEach((to) => {
......
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ss } from '@/utils/storage' import { ss } from '@/utils/storage'
import { TOKEN, IS_LOGIN, USER_INFO } from '@/utils/storage-key' import { type UserState, type UserInfo, UserStoreStorageKeyEnum } from '../types/user'
import type { UserState, UserInfo } from '../types/user'
function getDefaultUserInfo(): UserInfo { function createDefaultUserInfoFactory(): UserInfo {
return { return {
account: '', memberId: null,
userAccount: '', avatarUrl: '',
userId: null, nickName: '',
userName: '用户', mobilePhone: '',
createdTime: '',
avatar: '',
} }
} }
export const useUserStore = defineStore('user-store', { export const useUserStore = defineStore('user-store', {
state: (): UserState => ({ state: (): UserState => ({
isLogin: ss.get(IS_LOGIN), isLogin: ss.get(UserStoreStorageKeyEnum.isLogin),
token: ss.get(TOKEN) || '', token: ss.get(UserStoreStorageKeyEnum.token) || '',
userInfo: ss.get(USER_INFO) || getDefaultUserInfo(), userInfo: ss.get(UserStoreStorageKeyEnum.userInfo) || createDefaultUserInfoFactory(),
}), }),
actions: { actions: {
async logout() { async logout() {
this.isLogin = false this.isLogin = false
this.token = '' this.token = ''
this.userInfo = getDefaultUserInfo() this.userInfo = createDefaultUserInfoFactory()
ss.remove(IS_LOGIN) ss.remove(UserStoreStorageKeyEnum.isLogin)
ss.remove(TOKEN) ss.remove(UserStoreStorageKeyEnum.token)
ss.remove(USER_INFO) ss.remove(UserStoreStorageKeyEnum.userInfo)
},
updateIsLogin(status: boolean) {
this.isLogin = status
ss.set(IS_LOGIN, status)
}, },
updateToken(token: string) { updateToken(token: string) {
this.token = token this.token = token
ss.set(TOKEN, token) ss.set(UserStoreStorageKeyEnum.token, token)
if (token) {
this.isLogin = true
ss.set(UserStoreStorageKeyEnum.isLogin, true)
} else {
this.isLogin = false
ss.set(UserStoreStorageKeyEnum.isLogin, false)
}
}, },
updateUserInfo(userInfo: UserInfo) { updateUserInfo(userInfo: UserInfo) {
this.userInfo = userInfo this.userInfo = userInfo
ss.set(USER_INFO, userInfo) ss.set(UserStoreStorageKeyEnum.userInfo, userInfo)
}, },
}, },
}) })
export interface UserInfo { export interface UserInfo {
account: string memberId: number | null
userAccount: string mobilePhone: string
userId: null | number nickName: string
userName: string avatarUrl: string
createdTime: string
avatar: string
} }
export interface UserState { export interface UserState {
...@@ -12,3 +10,9 @@ export interface UserState { ...@@ -12,3 +10,9 @@ export interface UserState {
token: string token: string
userInfo: UserInfo userInfo: UserInfo
} }
export enum UserStoreStorageKeyEnum {
userInfo = 'USER_INFO',
token = 'TOKEN',
isLogin = 'IS_LOGIN',
}
...@@ -26,7 +26,7 @@ function handleLogout() { ...@@ -26,7 +26,7 @@ function handleLogout() {
} }
const service = axios.create({ const service = axios.create({
baseURL: `${BASE_URLS[ENV]}/api`, baseURL: `${BASE_URLS[ENV]}/api/rest`,
timeout: 7000, timeout: 7000,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
......
export const TOKEN = 'TOKEN'
export const IS_LOGIN = 'IS_LOGIN'
export const USER_INFO = 'USER_INFO'
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref, shallowReadonly, useTemplateRef, watchEffect } from 'vue'
import { appConfig } from '@/config/app-config' import type { FormInst, FormRules, FormItemRule, CountdownInst } from 'naive-ui'
import CustomIcon from '@/components/custom-icon/custom-icon.vue' import { Mail, Lock, Iphone, Down, User } from '@icon-park/vue-next'
import { FormInst } from 'naive-ui' import isMobilePhone from 'validator/es/lib/isMobilePhone'
// import { fetchLogin } from '@/apis/user' import isEmail from 'validator/es/lib/isEmail'
import { ss } from '@/utils/storage'
import { fetchEmailCode, fetchLogin, fetchSMSCode } from '@/apis/user'
import SparkMD5 from 'spark-md5'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
// import type { UserInfo } from '@/store/types/user' import type { UserInfo } from '@/store/types/user'
import { useRouter } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
enum StorageKeyEnum {
smsCountdownTime = 'SMS_COUNTDOWN_TIME',
emailCountdownTime = 'MAIL_COUNTDOWN_TIME',
}
type LoginMethod = 'password' | 'sms' | 'email'
interface LoginPayload {
loginChannel: 'MEMBER_PLATFOMR_SMS' | 'MEMBER_PLATFOMR_EMAIL' | 'MEMBER_PLATFOMR_PW'
account: string
password?: string
authCode?: string
}
const userStore = useUserStore() const userStore = useUserStore()
const router = useRouter() const router = useRouter()
const route = useRoute()
const passwordLoginFormRef = useTemplateRef<FormInst>('passwordLoginFormRef')
const smsLoginFormRef = useTemplateRef<FormInst>('smsLoginFormRef')
const emailLoginFormRef = useTemplateRef<FormInst>('emailLoginFormRef')
const countdownRef = useTemplateRef<CountdownInst>('countdownRef')
const currentLoginMethod = ref<LoginMethod>('password')
const showCardReserveAnimation = ref(false)
const loginForm = ref({ const loginBtnLoading = ref(false)
username: '',
const passwordLoginForm = ref({
account: '',
password: '', password: '',
}) })
const loginBtnLoading = ref(false) const smsLoginForm = ref({
const loginFormRef = ref<FormInst | null>(null) phoneNumber: '',
code: '',
})
const emailLoginForm = ref({
email: '',
code: '',
})
const passwordLoginFormRules = shallowReadonly<FormRules>({
account: { required: true, message: '請輸入用戶名', trigger: 'blur' },
password: { required: true, message: '請輸入密碼', trigger: 'blur' },
})
const smsLoginFormRules = shallowReadonly<FormRules>({
phoneNumber: {
key: 'phoneNumber',
required: true,
validator: (_rule: FormItemRule, value: string) => {
if (!value) {
return new Error('請輸入手機號')
} else if (!isMobilePhone(value, ['zh-CN', 'zh-HK'])) {
return new Error('請輸入正確手機號')
}
return
},
},
code: { required: true, message: '請輸入驗証碼' },
})
const emailLoginFormRules = shallowReadonly<FormRules>({
email: {
key: 'email',
required: true,
validator: (_rule: FormItemRule, value: string) => {
if (!value) {
return new Error('請輸入郵箱地址')
} else if (!isEmail(value)) {
return new Error('請輸入正確郵箱地址')
}
return
},
},
code: { required: true, message: '請輸入驗証碼' },
})
const phoneNumberAreaOptions = shallowReadonly([
{
label: '+86 中國大陸',
value: '+86',
},
{
label: '+852 中國香港',
value: '+852',
},
])
const currentPhoneNumberArea = ref<'+86' | '+852'>('+852')
const countdownActive = ref(true)
const isShowCountdown = ref(false)
const countdownDuration = ref<number>(60000)
watchEffect(() => {
let timeStringDraft = ''
switch (currentLoginMethod.value) {
case 'sms':
{
timeStringDraft = ss.get(StorageKeyEnum.smsCountdownTime)
}
break
case 'email':
{
timeStringDraft = ss.get(StorageKeyEnum.emailCountdownTime)
}
break
}
if (timeStringDraft) {
const time = Math.floor(Date.now() - parseInt(timeStringDraft))
if (time < 60000) {
countdownDuration.value = 60000 - time
countdownRef.value?.reset()
isShowCountdown.value = true
}
}
})
function onlyAllowNumber(value: string) {
return !value || /^\d+$/.test(value)
}
function noSideSpace(value: string) {
return !value.startsWith(' ') && !value.endsWith(' ')
}
function countdownRender({ seconds, minutes }: { seconds: number; minutes: number }) {
if (minutes && minutes === 1) {
return '60 s'
}
const loginFormRules = { return `${seconds} s`
username: { required: true, message: '请输入用户名', trigger: 'blur' },
password: { required: true, message: '请输入密码', trigger: 'blur' },
} }
function handleLogin(e: MouseEvent) { function onCardReserveAnimationEnd() {
e.preventDefault() showCardReserveAnimation.value = false
}
loginFormRef.value?.validate((errors) => { function onCountdownFinish() {
if (!errors) { isShowCountdown.value = false
// loginBtnLoading.value = true }
function getInputPhoneNumber() {
return currentPhoneNumberArea.value !== '+86'
? `${currentPhoneNumberArea.value}${smsLoginForm.value.phoneNumber}`
: smsLoginForm.value.phoneNumber
}
userStore.updateIsLogin(true) function handleLoginSubmit(method: LoginMethod) {
userStore.updateToken('res.data.token') let payload: LoginPayload = {
const userInfoRes = { loginChannel: 'MEMBER_PLATFOMR_PW',
account: 'account', account: '',
userAccount: 'userAccount', password: '',
userId: 134,
userName: 'userName',
createdTime: 'createdTime',
} }
userStore.updateUserInfo({
account: userInfoRes.account, new Promise((resolve) => {
userAccount: userInfoRes.userAccount, switch (method) {
userId: userInfoRes.userId, case 'password':
userName: userInfoRes.userName, {
createdTime: userInfoRes.createdTime, passwordLoginFormRef.value?.validate((errors) => {
avatar: '', if (errors) return ''
payload = {
loginChannel: 'MEMBER_PLATFOMR_PW',
account: passwordLoginForm.value.account,
password: SparkMD5.hash(passwordLoginForm.value.password),
}
resolve(true)
}) })
}
break
case 'sms':
{
smsLoginFormRef.value?.validate((errors) => {
if (errors) return ''
const currentRoute = router.currentRoute.value payload = {
const redirectUrl = decodeURIComponent((currentRoute.query.redirect as string) || '') loginChannel: 'MEMBER_PLATFOMR_SMS',
account: getInputPhoneNumber(),
authCode: smsLoginForm.value.code,
}
if (redirectUrl) { resolve(true)
router.replace({ path: redirectUrl }) })
return }
break
case 'email':
{
emailLoginFormRef.value?.validate((errors) => {
if (errors) return ''
payload = {
loginChannel: 'MEMBER_PLATFOMR_EMAIL',
account: emailLoginForm.value.email,
authCode: emailLoginForm.value.code,
} }
router.replace({ name: 'Root' }) resolve(true)
})
// fetchLogin<{ token: string; user: UserInfo }>(loginForm.value)
// .then((res) => {
// if (res.code !== null && res.code !== 0) {
// window.$message.error(res.message || '登录失败请重试')
// return
// }
// userStore.updateIsLogin(true)
// userStore.updateToken(res.data.token)
// const userInfoRes = res.data.user || {}
// userStore.updateUserInfo({
// account: userInfoRes.account,
// userAccount: userInfoRes.userAccount,
// userId: userInfoRes.userId,
// userName: userInfoRes.userName,
// createdTime: userInfoRes.createdTime,
// avatar: '',
// })
// const currentRoute = router.currentRoute.value
// const redirectUrl = decodeURIComponent((currentRoute.query.redirect as string) || '')
// if (redirectUrl) {
// router.replace({ path: redirectUrl })
// return
// }
// router.replace({ name: 'Root' })
// })
// .catch((err) => {
// console.log(err)
// })
// .finally(() => {
// loginBtnLoading.value = false
// })
} }
break
}
}).then(() => {
loginBtnLoading.value = true
fetchLogin<UserInfo & { token: string }>(payload)
.then((res) => {
if (res.code !== 0) return ''
userStore.updateToken(res.data.token)
userStore.updateUserInfo({
avatarUrl: res.data.avatarUrl,
memberId: res.data.memberId,
mobilePhone: res.data.mobilePhone,
nickName: res.data.nickName,
})
const redirectUrl = decodeURIComponent((route.query.redirect as string) || '')
router.replace({ path: redirectUrl ? redirectUrl : '/' })
window.$message.success('登錄成功')
ss.remove(StorageKeyEnum.smsCountdownTime)
ss.remove(StorageKeyEnum.emailCountdownTime)
})
.finally(() => {
loginBtnLoading.value = false
})
})
}
function handleLoginMethodChange(method: LoginMethod) {
showCardReserveAnimation.value = true
setTimeout(() => {
currentLoginMethod.value = method
}, 500)
}
function handleSMSCodeGain() {
smsLoginFormRef.value?.validate(
(errors) => {
if (errors) return ''
countdownDuration.value = 60000
ss.set(StorageKeyEnum.smsCountdownTime, Date.now())
countdownRef.value?.reset()
isShowCountdown.value = true
fetchSMSCode(getInputPhoneNumber()).then((res) => {
if (res.code !== 0) return ''
window.$message.success('獲取成功')
}) })
},
(rule) => {
return rule.key === 'phoneNumber'
},
)
}
function handleEmailCodeGain() {
emailLoginFormRef.value?.validate(
(errors) => {
if (errors) return ''
countdownDuration.value = 60000
ss.set(StorageKeyEnum.emailCountdownTime, Date.now())
countdownRef.value?.reset()
isShowCountdown.value = true
fetchEmailCode(encodeURIComponent(emailLoginForm.value.email)).then((res) => {
if (res.code !== 0) return ''
window.$message.success('獲取成功')
})
},
(rule) => {
return rule.key === 'email'
},
)
} }
</script> </script>
<template> <template>
<div class="bg-svg-login-bg h-full w-full"> <div class="bg-px-login-bg-png relative h-screen min-h-[750px] w-full min-w-[600px] bg-cover bg-center bg-no-repeat">
<div class="fixed left-1/2 top-1/3 w-[90%] -translate-x-1/2 -translate-y-1/3 rounded-lg bg-white p-6 sm:w-[410px]"> <div class="absolute right-[14%] top-1/2 h-[458px] w-[390px] -translate-y-1/2">
<div class="mb-6 flex justify-center sm:mb-7"> <div
<div class="bg-px-logo-png h-24 w-36"></div> class="h-full w-full rounded-[10px] bg-[#fff] px-[29px]"
</div> :class="{ 'animate-card-reverse': showCardReserveAnimation }"
<div class="mb-8 text-center text-2xl font-bold text-[#999] outline-none sm:mb-10 sm:text-2xl"> style="transform-style: preserve-3d"
{{ appConfig.title }} @animationend="onCardReserveAnimationEnd"
</div> >
<h1 class="font-600 py-[34px] text-center text-[22px]">歡迎使用萃想智能雲創</h1>
<div> <div>
<NForm ref="loginFormRef" label-placement="left" size="large" :model="loginForm" :rules="loginFormRules"> <!-- 密码登录 -->
<NFormItem path="username"> <n-form
<NInput v-model:value="loginForm.username" placeholder="请输入用户名"> v-if="currentLoginMethod === 'password'"
ref="passwordLoginFormRef"
label-placement="left"
size="large"
:model="passwordLoginForm"
:rules="passwordLoginFormRules"
>
<n-form-item path="account">
<n-input
v-model:value="passwordLoginForm.account"
:allow-input="noSideSpace"
:maxlength="11"
placeholder="請輸入用戶名"
>
<template #prefix> <template #prefix>
<CustomIcon class="h-5 w-5 text-[#868686]" icon="material-symbols:person-outline" /> <div class="mr-[6px]">
<User theme="outline" size="16" fill="#868686" :stroke-width="3" />
</div>
</template> </template>
</NInput> </n-input>
</NFormItem> </n-form-item>
<NFormItem path="password"> <n-form-item path="password">
<NInput <n-input
v-model:value="loginForm.password" v-model:value="passwordLoginForm.password"
:allow-input="noSideSpace"
type="password" type="password"
show-password-on="click" show-password-on="click"
placeholder="请输入密码" placeholder="請輸入密碼"
>
<template #prefix>
<div class="mr-[6px]">
<Lock theme="outline" size="16" fill="#868686" :stroke-width="3" />
</div>
</template>
</n-input>
</n-form-item>
<n-form-item class="mt-4">
<n-button
type="primary"
size="large"
block
:loading="loginBtnLoading"
:disabled="!passwordLoginForm.account || !passwordLoginForm.password"
@click="handleLoginSubmit('password')"
>
登錄
</n-button>
</n-form-item>
</n-form>
<!-- SMS登录 -->
<n-form
v-if="currentLoginMethod === 'sms'"
ref="smsLoginFormRef"
label-placement="left"
size="large"
:model="smsLoginForm"
:rules="smsLoginFormRules"
>
<n-form-item path="phoneNumber">
<n-input
v-model:value.trim="smsLoginForm.phoneNumber"
:allow-input="onlyAllowNumber"
:maxlength="currentPhoneNumberArea === '+852' ? 8 : 11"
placeholder="請輸入手機號"
>
<template #prefix>
<div class="flex items-center">
<n-popselect
v-model:value="currentPhoneNumberArea"
:options="phoneNumberAreaOptions"
trigger="click"
>
<div class="flex w-[62px] cursor-pointer items-center">
<div class="mr-[4px]">{{ currentPhoneNumberArea }}</div>
<Down theme="outline" size="18" fill="#333" :stroke-width="3" />
</div>
</n-popselect>
<div class="mx-[8px] h-[18px] w-[1px] bg-[#868686]"></div>
</div>
</template>
</n-input>
</n-form-item>
<n-form-item path="code">
<n-input
v-model:value="smsLoginForm.code"
:allow-input="onlyAllowNumber"
show-password-on="click"
:maxlength="6"
placeholder="請輸入驗証碼"
> >
<template #suffix>
<div class="flex items-center">
<div class="mx-[6px] h-[18px] w-[1px] bg-[#868686]"></div>
<div class="w-[90px] text-end">
<n-button
v-show="!isShowCountdown"
class="!text-[11px]"
type="tertiary"
size="small"
@click="handleSMSCodeGain"
>
獲取驗証碼
</n-button>
<div v-show="isShowCountdown" class="inline-block w-[50px] text-center">
<n-countdown
ref="countdownRef"
:duration="countdownDuration"
:active="countdownActive"
:render="countdownRender"
:on-finish="onCountdownFinish"
/>
</div>
</div>
</div>
</template>
</n-input>
</n-form-item>
<n-form-item class="mt-4">
<n-button
type="primary"
size="large"
block
:loading="loginBtnLoading"
:disabled="!smsLoginForm.phoneNumber || !smsLoginForm.code"
@click="handleLoginSubmit('sms')"
>
登錄
</n-button>
</n-form-item>
</n-form>
<!-- 邮箱登录 -->
<n-form
v-if="currentLoginMethod === 'email'"
ref="emailLoginFormRef"
label-placement="left"
size="large"
:model="emailLoginForm"
:rules="emailLoginFormRules"
>
<n-form-item path="email">
<n-input v-model:value="emailLoginForm.email" :allow-input="noSideSpace" placeholder="請輸入郵箱地址">
<template #prefix> <template #prefix>
<CustomIcon class="h-5 w-5 text-[#868686]" icon="mdi:lock-outline" /> <div class="mr-[6px]">
<Mail theme="outline" size="16" fill="#868686" :stroke-width="3" />
</div>
</template>
</n-input>
</n-form-item>
<n-form-item path="code">
<n-input
v-model:value="emailLoginForm.code"
:allow-input="onlyAllowNumber"
show-password-on="click"
:maxlength="6"
placeholder="請輸入驗証碼"
>
<template #suffix>
<div class="flex items-center">
<div class="mx-[6px] h-[18px] w-[1px] bg-[#868686]"></div>
<div class="w-[90px] text-end">
<n-button
v-show="!isShowCountdown"
class="!text-[11px]"
type="tertiary"
size="small"
@click="handleEmailCodeGain"
>
獲取驗証碼
</n-button>
<div v-show="isShowCountdown" class="inline-block w-[50px] text-center">
<n-countdown
ref="countdownRef"
:duration="countdownDuration"
:active="countdownActive"
:render="countdownRender"
:on-finish="onCountdownFinish"
/>
</div>
</div>
</div>
</template> </template>
</NInput> </n-input>
</NFormItem> </n-form-item>
<NFormItem class="mt-4"> <n-form-item class="mt-4">
<NButton <n-button
type="primary" type="primary"
size="large" size="large"
block block
:loading="loginBtnLoading" :loading="loginBtnLoading"
:disabled="!loginForm.username || !loginForm.password" :disabled="!emailLoginForm.email || !emailLoginForm.code"
@click="handleLogin" @click="handleLoginSubmit('email')"
>
登錄
</n-button>
</n-form-item>
</n-form>
</div>
<div class="absolute bottom-[22px] left-0 w-full">
<div class="mb-[32px]">
<div class="mb-[12px] text-center text-[12px] text-[#999999]">其他登錄方式</div>
<div class="flex items-center justify-center">
<button
v-show="currentLoginMethod !== 'email'"
class="mx-[10px] flex h-[34px] w-[34px] items-center justify-center rounded-full bg-[#f1f1f1] transition hover:bg-[#e0e0e0]"
@click="handleLoginMethodChange('email')"
>
<Mail theme="outline" size="18" fill="#666666" :stroke-width="3" />
</button>
<button
v-show="currentLoginMethod !== 'sms'"
class="mx-[10px] flex h-[34px] w-[34px] items-center justify-center rounded-full bg-[#f1f1f1] transition hover:bg-[#e0e0e0]"
@click="handleLoginMethodChange('sms')"
>
<Iphone theme="outline" size="17" fill="#666666" :stroke-width="3" />
</button>
<button
v-show="currentLoginMethod !== 'password'"
class="mx-[10px] flex h-[34px] w-[34px] items-center justify-center rounded-full bg-[#f1f1f1] transition hover:bg-[#e0e0e0]"
@click="handleLoginMethodChange('password')"
> >
登录 <Lock theme="outline" size="17" fill="#666666" :stroke-width="3" />
</NButton> </button>
</NFormItem> </div>
</NForm> </div>
<!-- <div class="text-center">
<n-checkbox size="small"><span class="text-[12px]">閱讀並同意協議</span></n-checkbox>
</div> -->
</div>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -15,4 +15,20 @@ export default defineConfig({ ...@@ -15,4 +15,20 @@ export default defineConfig({
}), }),
], ],
], ],
theme: {
animation: {
keyframes: {
'card-reverse': `{ 0% { transform: rotateY(0deg); } 100% { transform: rotateY(1turn); } }`,
},
durations: {
'card-reverse': '1s',
},
timingFns: {
'card-reverse': 'ease-in-out',
},
counts: {
'card-reverse': '1',
},
},
},
}) })
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment