feat(projects): 迁移登录完成

This commit is contained in:
Soybean 2022-01-05 01:35:32 +08:00
parent f5a36a05cb
commit b93b80cb4b
54 changed files with 1679 additions and 260 deletions

View File

@ -1,14 +1,61 @@
import type { MockMethod } from 'vite-plugin-mock';
const token: ApiAuth.Token = {
token: '__TEMP_TOKEN__',
refreshToken: '__TEMP_REFRESH_TOKEN__'
};
const apis: MockMethod[] = [
// 获取验证码
{
url: '/mock/getUser',
method: 'get',
response: (): Service.BackendServiceResult => {
url: '/mock/getSmsCode',
method: 'post',
response: (): Service.BackendServiceResult<boolean> => {
return {
code: 200,
message: 'ok',
data: '测试mock数据'
data: true
};
}
},
// 密码登录
{
url: '/mock/loginByPwd',
method: 'post',
response: (): Service.BackendServiceResult<ApiAuth.Token> => {
return {
code: 200,
message: 'ok',
data: token
};
}
},
// 验证码登录
{
url: '/mock/loginByCode',
method: 'post',
response: (): Service.BackendServiceResult<ApiAuth.Token> => {
return {
code: 200,
message: 'ok',
data: token
};
}
},
// 获取用户信息(请求头携带token)
{
url: '/mock/getUserInfo',
method: 'get',
response: (): Service.BackendServiceResult<ApiAuth.UserInfo> => {
return {
code: 200,
message: 'ok',
data: {
userId: '0',
userName: 'Soybean',
userPhone: '15170283876',
userRole: 'super'
}
};
}
}

View File

@ -73,7 +73,7 @@ const routes: AuthRoute.Route[] = [
const routeHome: AuthRoute.RoutePath = '/dashboard/analysis';
const data: ApiRoute.ResponseRoute = {
const data: ApiRoute.Route = {
routes,
home: routeHome
};

View File

@ -37,13 +37,13 @@
"devDependencies": {
"@commitlint/cli": "^16.0.1",
"@commitlint/config-conventional": "^16.0.0",
"@iconify/json": "^1.1.451",
"@iconify/json": "^1.1.452",
"@iconify/vue": "^3.1.1",
"@types/crypto-js": "^4.1.0",
"@types/node": "^17.0.6",
"@types/qs": "^6.9.7",
"@typescript-eslint/eslint-plugin": "^5.8.1",
"@typescript-eslint/parser": "^5.8.1",
"@typescript-eslint/parser": "^5.9.0",
"@vitejs/plugin-vue": "^2.0.1",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^10.0.0",
@ -54,11 +54,11 @@
"eslint": "^8.6.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.2.0",
"husky": "^7.0.4",
"lint-staged": "^12.1.4",
"lint-staged": "^12.1.5",
"mockjs": "^1.1.0",
"patch-package": "^6.4.7",
"postinstall-postinstall": "^2.1.0",
@ -72,7 +72,7 @@
"vite-plugin-html": "^2.1.2",
"vite-plugin-mock": "^2.9.6",
"vite-plugin-windicss": "^1.6.1",
"vue-tsc": "^0.30.1",
"vue-tsc": "^0.30.2",
"vueuc": "^0.4.19",
"windicss": "^3.4.2"
}

View File

@ -3,13 +3,13 @@ lockfileVersion: 5.3
specifiers:
'@commitlint/cli': ^16.0.1
'@commitlint/config-conventional': ^16.0.0
'@iconify/json': ^1.1.451
'@iconify/json': ^1.1.452
'@iconify/vue': ^3.1.1
'@types/crypto-js': ^4.1.0
'@types/node': ^17.0.6
'@types/qs': ^6.9.7
'@typescript-eslint/eslint-plugin': ^5.8.1
'@typescript-eslint/parser': ^5.8.1
'@typescript-eslint/parser': ^5.9.0
'@vitejs/plugin-vue': ^2.0.1
'@vue/eslint-config-prettier': ^7.0.0
'@vue/eslint-config-typescript': ^10.0.0
@ -25,12 +25,12 @@ specifiers:
eslint: ^8.6.0
eslint-config-airbnb-base: ^15.0.0
eslint-config-prettier: ^8.3.0
eslint-plugin-import: ^2.25.3
eslint-plugin-import: ^2.25.4
eslint-plugin-prettier: ^4.0.0
eslint-plugin-vue: ^8.2.0
form-data: ^4.0.0
husky: ^7.0.4
lint-staged: ^12.1.4
lint-staged: ^12.1.5
lodash-es: ^4.17.21
mockjs: ^1.1.0
naive-ui: ^2.23.2
@ -50,7 +50,7 @@ specifiers:
vite-plugin-windicss: ^1.6.1
vue: ^3.2.26
vue-router: ^4.0.12
vue-tsc: ^0.30.1
vue-tsc: ^0.30.2
vueuc: ^0.4.19
windicss: ^3.4.2
@ -71,13 +71,13 @@ dependencies:
devDependencies:
'@commitlint/cli': registry.npmmirror.com/@commitlint/cli/16.0.1_@types+node@17.0.6
'@commitlint/config-conventional': registry.npmmirror.com/@commitlint/config-conventional/16.0.0
'@iconify/json': registry.npmmirror.com/@iconify/json/1.1.451
'@iconify/json': registry.npmmirror.com/@iconify/json/1.1.452
'@iconify/vue': registry.npmmirror.com/@iconify/vue/3.1.1_vue@3.2.26
'@types/crypto-js': registry.npmmirror.com/@types/crypto-js/4.1.0
'@types/node': registry.npmmirror.com/@types/node/17.0.6
'@types/qs': registry.npmmirror.com/@types/qs/6.9.7
'@typescript-eslint/eslint-plugin': registry.npmmirror.com/@typescript-eslint/eslint-plugin/5.8.1_13039593e64cd539d0b4c5c2da390958
'@typescript-eslint/parser': registry.npmmirror.com/@typescript-eslint/parser/5.8.1_eslint@8.6.0+typescript@4.5.4
'@typescript-eslint/eslint-plugin': registry.npmmirror.com/@typescript-eslint/eslint-plugin/5.8.1_bd2fd93dbcc607ad2f21b784bccfe0c8
'@typescript-eslint/parser': registry.npmmirror.com/@typescript-eslint/parser/5.9.0_eslint@8.6.0+typescript@4.5.4
'@vitejs/plugin-vue': registry.npmmirror.com/@vitejs/plugin-vue/2.0.1_vite@2.7.10+vue@3.2.26
'@vue/eslint-config-prettier': registry.npmmirror.com/@vue/eslint-config-prettier/7.0.0_eslint@8.6.0+prettier@2.5.1
'@vue/eslint-config-typescript': registry.npmmirror.com/@vue/eslint-config-typescript/10.0.0_57f850728139557a3a27f1248f77f964
@ -86,13 +86,13 @@ devDependencies:
cz-conventional-changelog: registry.nlark.com/cz-conventional-changelog/3.3.0_@types+node@17.0.6
cz-customizable: registry.npmmirror.com/cz-customizable/6.3.0
eslint: registry.npmmirror.com/eslint/8.6.0
eslint-config-airbnb-base: registry.npmmirror.com/eslint-config-airbnb-base/15.0.0_b54597effeb7d09a84a334cc3c3dadae
eslint-config-airbnb-base: registry.npmmirror.com/eslint-config-airbnb-base/15.0.0_b5a36b8c1535387c8dd00eff7ec6b551
eslint-config-prettier: registry.nlark.com/eslint-config-prettier/8.3.0_eslint@8.6.0
eslint-plugin-import: registry.npmmirror.com/eslint-plugin-import/2.25.3_eslint@8.6.0
eslint-plugin-import: registry.npmmirror.com/eslint-plugin-import/2.25.4_eslint@8.6.0
eslint-plugin-prettier: registry.npmmirror.com/eslint-plugin-prettier/4.0.0_1c588f61426b1faf18812943f1678311
eslint-plugin-vue: registry.npmmirror.com/eslint-plugin-vue/8.2.0_eslint@8.6.0
husky: registry.npmmirror.com/husky/7.0.4
lint-staged: registry.npmmirror.com/lint-staged/12.1.4
lint-staged: registry.npmmirror.com/lint-staged/12.1.5
mockjs: registry.npmmirror.com/mockjs/1.1.0
patch-package: registry.nlark.com/patch-package/6.4.7
postinstall-postinstall: registry.npmmirror.com/postinstall-postinstall/2.1.0
@ -106,7 +106,7 @@ devDependencies:
vite-plugin-html: registry.npmmirror.com/vite-plugin-html/2.1.2_vite@2.7.10
vite-plugin-mock: registry.npmmirror.com/vite-plugin-mock/2.9.6_mockjs@1.1.0+vite@2.7.10
vite-plugin-windicss: registry.npmmirror.com/vite-plugin-windicss/1.6.1_vite@2.7.10
vue-tsc: registry.npmmirror.com/vue-tsc/0.30.1_typescript@4.5.4
vue-tsc: registry.npmmirror.com/vue-tsc/0.30.2_typescript@4.5.4
vueuc: registry.npmmirror.com/vueuc/0.4.19_vue@3.2.26
windicss: registry.npmmirror.com/windicss/3.4.2
@ -312,7 +312,7 @@ packages:
engines: {node: '>= 8'}
dependencies:
normalize-path: registry.nlark.com/normalize-path/3.0.0
picomatch: registry.nlark.com/picomatch/2.3.0
picomatch: registry.npmmirror.com/picomatch/2.3.0
dev: true
registry.nlark.com/argparse/2.0.1:
@ -919,7 +919,7 @@ packages:
version: 0.3.6
dependencies:
debug: registry.npmmirror.com/debug/3.2.7
resolve: registry.nlark.com/resolve/1.20.0
resolve: registry.npmmirror.com/resolve/1.20.0
dev: true
registry.nlark.com/eslint-utils/3.0.0_eslint@8.6.0:
@ -1675,7 +1675,7 @@ packages:
version: 1.0.0
dependencies:
is-promise: registry.nlark.com/is-promise/2.2.2
promise: registry.nlark.com/promise/7.3.1
promise: registry.npmmirror.com/promise/7.3.1
dev: true
registry.nlark.com/kind-of/6.0.3:
@ -1806,7 +1806,7 @@ packages:
engines: {node: '>=8.6'}
dependencies:
braces: registry.nlark.com/braces/3.0.2
picomatch: registry.nlark.com/picomatch/2.3.0
picomatch: registry.npmmirror.com/picomatch/2.3.0
dev: true
registry.nlark.com/mimic-fn/1.2.0:
@ -1888,7 +1888,7 @@ packages:
version: 2.5.0
dependencies:
hosted-git-info: registry.npmmirror.com/hosted-git-info/2.8.9
resolve: registry.nlark.com/resolve/1.20.0
resolve: registry.npmmirror.com/resolve/1.20.0
semver: registry.nlark.com/semver/5.7.1
validate-npm-package-license: registry.nlark.com/validate-npm-package-license/3.0.4
dev: true
@ -2186,14 +2186,6 @@ packages:
fast-diff: registry.nlark.com/fast-diff/1.2.0
dev: true
registry.nlark.com/promise/7.3.1:
resolution: {integrity: sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=, registry: http://registry.npm.taobao.org/, tarball: https://registry.nlark.com/promise/download/promise-7.3.1.tgz}
name: promise
version: 7.3.1
dependencies:
asap: registry.nlark.com/asap/2.0.6
dev: true
registry.nlark.com/pug-attrs/3.0.0:
resolution: {integrity: sha1-sQRR4DSBZeMfrRzCPr3dncc0fEE=, registry: http://registry.npm.taobao.org/, tarball: https://registry.nlark.com/pug-attrs/download/pug-attrs-3.0.0.tgz}
name: pug-attrs
@ -2228,7 +2220,7 @@ packages:
jstransformer: registry.nlark.com/jstransformer/1.0.0
pug-error: registry.npmmirror.com/pug-error/2.0.0
pug-walk: registry.nlark.com/pug-walk/2.0.0
resolve: registry.nlark.com/resolve/1.20.0
resolve: registry.npmmirror.com/resolve/1.20.0
dev: true
registry.nlark.com/pug-lexer/5.0.1:
@ -2288,21 +2280,6 @@ packages:
version: 2.0.0
dev: true
registry.nlark.com/pug/3.0.2:
resolution: {integrity: sha1-81xxBzQ0VOQ7wnrg/3bHMbeOpTU=, registry: http://registry.npm.taobao.org/, tarball: https://registry.nlark.com/pug/download/pug-3.0.2.tgz}
name: pug
version: 3.0.2
dependencies:
pug-code-gen: registry.nlark.com/pug-code-gen/3.0.2
pug-filters: registry.nlark.com/pug-filters/4.0.0
pug-lexer: registry.nlark.com/pug-lexer/5.0.1
pug-linker: registry.nlark.com/pug-linker/4.0.0
pug-load: registry.nlark.com/pug-load/3.0.0
pug-parser: registry.nlark.com/pug-parser/6.0.0
pug-runtime: registry.nlark.com/pug-runtime/3.0.1
pug-strip-comments: registry.nlark.com/pug-strip-comments/2.0.0
dev: true
registry.nlark.com/punycode/2.1.1:
resolution: {integrity: sha1-tYsBCsQMIsVldhbI0sLALHv0eew=, registry: http://registry.npm.taobao.org/, tarball: https://registry.nlark.com/punycode/download/punycode-2.1.1.tgz}
name: punycode
@ -2352,7 +2329,7 @@ packages:
version: 3.6.0
engines: {node: '>=8.10.0'}
dependencies:
picomatch: registry.nlark.com/picomatch/2.3.0
picomatch: registry.npmmirror.com/picomatch/2.3.0
dev: true
registry.nlark.com/redent/3.0.0:
@ -3386,10 +3363,10 @@ packages:
version: 1.2.1
dev: true
registry.npmmirror.com/@iconify/json/1.1.451:
resolution: {integrity: sha512-A+GgU4xetmBfofDQSBNENRsbZo3vMZTF4pkCxFObWs+y9j/C/oGwJwqiV3yrU9jFecF/MkffBbwNFug8j5EAtw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@iconify/json/download/@iconify/json-1.1.451.tgz}
registry.npmmirror.com/@iconify/json/1.1.452:
resolution: {integrity: sha512-3eUNfCvfap5dE4JUePnul+3wCuWMSqD1zC9oGHqob5FRbJ05tGQ/n0FsL7WthqO5C+dLaMUMNRRG5VRwMzyYfA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@iconify/json/download/@iconify/json-1.1.452.tgz}
name: '@iconify/json'
version: 1.1.451
version: 1.1.452
dev: true
registry.npmmirror.com/@iconify/types/1.0.12:
@ -3443,7 +3420,7 @@ packages:
builtin-modules: registry.nlark.com/builtin-modules/3.2.0
deepmerge: registry.nlark.com/deepmerge/4.2.2
is-module: registry.npmmirror.com/is-module/1.0.0
resolve: registry.nlark.com/resolve/1.20.0
resolve: registry.npmmirror.com/resolve/1.20.0
dev: true
registry.npmmirror.com/@rollup/pluginutils/3.1.0:
@ -3456,7 +3433,7 @@ packages:
dependencies:
'@types/estree': registry.npmmirror.com/@types/estree/0.0.39
estree-walker: registry.npmmirror.com/estree-walker/1.0.1
picomatch: registry.nlark.com/picomatch/2.3.0
picomatch: registry.npmmirror.com/picomatch/2.3.0
dev: true
registry.npmmirror.com/@rollup/pluginutils/4.1.2:
@ -3594,7 +3571,7 @@ packages:
dependencies:
'@types/yargs-parser': registry.npmmirror.com/@types/yargs-parser/20.2.1
registry.npmmirror.com/@typescript-eslint/eslint-plugin/5.8.1_13039593e64cd539d0b4c5c2da390958:
registry.npmmirror.com/@typescript-eslint/eslint-plugin/5.8.1_bd2fd93dbcc607ad2f21b784bccfe0c8:
resolution: {integrity: sha512-wTZ5oEKrKj/8/366qTM366zqhIKAp6NCMweoRONtfuC07OAU9nVI2GZZdqQ1qD30WAAtcPdkH+npDwtRFdp4Rw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/download/@typescript-eslint/eslint-plugin-5.8.1.tgz}
id: registry.npmmirror.com/@typescript-eslint/eslint-plugin/5.8.1
name: '@typescript-eslint/eslint-plugin'
@ -3609,7 +3586,7 @@ packages:
optional: true
dependencies:
'@typescript-eslint/experimental-utils': registry.npmmirror.com/@typescript-eslint/experimental-utils/5.8.1_eslint@8.6.0+typescript@4.5.4
'@typescript-eslint/parser': registry.npmmirror.com/@typescript-eslint/parser/5.8.1_eslint@8.6.0+typescript@4.5.4
'@typescript-eslint/parser': registry.npmmirror.com/@typescript-eslint/parser/5.9.0_eslint@8.6.0+typescript@4.5.4
'@typescript-eslint/scope-manager': registry.npmmirror.com/@typescript-eslint/scope-manager/5.8.1
debug: registry.npmmirror.com/debug/4.3.3
eslint: registry.npmmirror.com/eslint/8.6.0
@ -3644,11 +3621,11 @@ packages:
- typescript
dev: true
registry.npmmirror.com/@typescript-eslint/parser/5.8.1_eslint@8.6.0+typescript@4.5.4:
resolution: {integrity: sha512-K1giKHAjHuyB421SoXMXFHHVI4NdNY603uKw92++D3qyxSeYvC10CBJ/GE5Thpo4WTUvu1mmJI2/FFkz38F2Gw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/parser/download/@typescript-eslint/parser-5.8.1.tgz}
id: registry.npmmirror.com/@typescript-eslint/parser/5.8.1
registry.npmmirror.com/@typescript-eslint/parser/5.9.0_eslint@8.6.0+typescript@4.5.4:
resolution: {integrity: sha512-/6pOPz8yAxEt4PLzgbFRDpZmHnXCeZgPDrh/1DaVKOjvn/UPMlWhbx/gA96xRi2JxY1kBl2AmwVbyROUqys5xQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/parser/download/@typescript-eslint/parser-5.9.0.tgz}
id: registry.npmmirror.com/@typescript-eslint/parser/5.9.0
name: '@typescript-eslint/parser'
version: 5.8.1
version: 5.9.0
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
@ -3657,9 +3634,9 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/scope-manager': registry.npmmirror.com/@typescript-eslint/scope-manager/5.8.1
'@typescript-eslint/types': registry.npmmirror.com/@typescript-eslint/types/5.8.1
'@typescript-eslint/typescript-estree': registry.npmmirror.com/@typescript-eslint/typescript-estree/5.8.1_typescript@4.5.4
'@typescript-eslint/scope-manager': registry.npmmirror.com/@typescript-eslint/scope-manager/5.9.0
'@typescript-eslint/types': registry.npmmirror.com/@typescript-eslint/types/5.9.0
'@typescript-eslint/typescript-estree': registry.npmmirror.com/@typescript-eslint/typescript-estree/5.9.0_typescript@4.5.4
debug: registry.npmmirror.com/debug/4.3.3
eslint: registry.npmmirror.com/eslint/8.6.0
typescript: registry.npmmirror.com/typescript/4.5.4
@ -3677,6 +3654,16 @@ packages:
'@typescript-eslint/visitor-keys': registry.npmmirror.com/@typescript-eslint/visitor-keys/5.8.1
dev: true
registry.npmmirror.com/@typescript-eslint/scope-manager/5.9.0:
resolution: {integrity: sha512-DKtdIL49Qxk2a8icF6whRk7uThuVz4A6TCXfjdJSwOsf+9ree7vgQWcx0KOyCdk0i9ETX666p4aMhrRhxhUkyg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/scope-manager/download/@typescript-eslint/scope-manager-5.9.0.tgz}
name: '@typescript-eslint/scope-manager'
version: 5.9.0
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
'@typescript-eslint/types': registry.npmmirror.com/@typescript-eslint/types/5.9.0
'@typescript-eslint/visitor-keys': registry.npmmirror.com/@typescript-eslint/visitor-keys/5.9.0
dev: true
registry.npmmirror.com/@typescript-eslint/types/5.8.1:
resolution: {integrity: sha512-L/FlWCCgnjKOLefdok90/pqInkomLnAcF9UAzNr+DSqMC3IffzumHTQTrINXhP1gVp9zlHiYYjvozVZDPleLcA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/types/download/@typescript-eslint/types-5.8.1.tgz}
name: '@typescript-eslint/types'
@ -3684,6 +3671,13 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
registry.npmmirror.com/@typescript-eslint/types/5.9.0:
resolution: {integrity: sha512-mWp6/b56Umo1rwyGCk8fPIzb9Migo8YOniBGPAQDNC6C52SeyNGN4gsVwQTAR+RS2L5xyajON4hOLwAGwPtUwg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/types/download/@typescript-eslint/types-5.9.0.tgz}
name: '@typescript-eslint/types'
version: 5.9.0
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
registry.npmmirror.com/@typescript-eslint/typescript-estree/5.8.1_typescript@4.5.4:
resolution: {integrity: sha512-26lQ8l8tTbG7ri7xEcCFT9ijU5Fk+sx/KRRyyzCv7MQ+rZZlqiDPtMKWLC8P7o+dtCnby4c+OlxuX1tp8WfafQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/typescript-estree/download/@typescript-eslint/typescript-estree-5.8.1.tgz}
id: registry.npmmirror.com/@typescript-eslint/typescript-estree/5.8.1
@ -3708,6 +3702,30 @@ packages:
- supports-color
dev: true
registry.npmmirror.com/@typescript-eslint/typescript-estree/5.9.0_typescript@4.5.4:
resolution: {integrity: sha512-kxo3xL2mB7XmiVZcECbaDwYCt3qFXz99tBSuVJR4L/sR7CJ+UNAPrYILILktGj1ppfZ/jNt/cWYbziJUlHl1Pw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/typescript-estree/download/@typescript-eslint/typescript-estree-5.9.0.tgz}
id: registry.npmmirror.com/@typescript-eslint/typescript-estree/5.9.0
name: '@typescript-eslint/typescript-estree'
version: 5.9.0
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/types': registry.npmmirror.com/@typescript-eslint/types/5.9.0
'@typescript-eslint/visitor-keys': registry.npmmirror.com/@typescript-eslint/visitor-keys/5.9.0
debug: registry.npmmirror.com/debug/4.3.3
globby: registry.nlark.com/globby/11.0.4
is-glob: registry.npmmirror.com/is-glob/4.0.3
semver: registry.nlark.com/semver/7.3.5
tsutils: registry.nlark.com/tsutils/3.21.0_typescript@4.5.4
typescript: registry.npmmirror.com/typescript/4.5.4
transitivePeerDependencies:
- supports-color
dev: true
registry.npmmirror.com/@typescript-eslint/visitor-keys/5.8.1:
resolution: {integrity: sha512-SWgiWIwocK6NralrJarPZlWdr0hZnj5GXHIgfdm8hNkyKvpeQuFyLP6YjSIe9kf3YBIfU6OHSZLYkQ+smZwtNg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/visitor-keys/download/@typescript-eslint/visitor-keys-5.8.1.tgz}
name: '@typescript-eslint/visitor-keys'
@ -3718,6 +3736,16 @@ packages:
eslint-visitor-keys: registry.npmmirror.com/eslint-visitor-keys/3.1.0
dev: true
registry.npmmirror.com/@typescript-eslint/visitor-keys/5.9.0:
resolution: {integrity: sha512-6zq0mb7LV0ThExKlecvpfepiB+XEtFv/bzx7/jKSgyXTFD7qjmSu1FoiS0x3OZaiS+UIXpH2vd9O89f02RCtgw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@typescript-eslint/visitor-keys/download/@typescript-eslint/visitor-keys-5.9.0.tgz}
name: '@typescript-eslint/visitor-keys'
version: 5.9.0
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
'@typescript-eslint/types': registry.npmmirror.com/@typescript-eslint/types/5.9.0
eslint-visitor-keys: registry.npmmirror.com/eslint-visitor-keys/3.1.0
dev: true
registry.npmmirror.com/@vitejs/plugin-vue/2.0.1_vite@2.7.10+vue@3.2.26:
resolution: {integrity: sha512-wtdMnGVvys9K8tg+DxowU1ytTrdVveXr3LzdhaKakysgGXyrsfaeds2cDywtvujEASjWOwWL/OgWM+qoeM8Plg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vitejs/plugin-vue/download/@vitejs/plugin-vue-2.0.1.tgz}
id: registry.npmmirror.com/@vitejs/plugin-vue/2.0.1
@ -3732,61 +3760,62 @@ packages:
vue: registry.npmmirror.com/vue/3.2.26
dev: true
registry.npmmirror.com/@volar/code-gen/0.30.1:
resolution: {integrity: sha512-qPT0ZGzLaaUArZ1b5qcso2GAFpgjxsPDRXnq0lgsSOpNO6lXJN5ZcWzFZXYPjMJSV1Rkm0ehK5tSUD+sD+pPWg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/code-gen/download/@volar/code-gen-0.30.1.tgz}
registry.npmmirror.com/@volar/code-gen/0.30.2:
resolution: {integrity: sha512-75rlb3rw/O/HiXxrOsVJ8JhEssqtN4dJ0i6oG4kL0udr+QM0TwN0PqaEhoMRyMFV6kBPPSunBBJQ3XNAb0PtGA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/code-gen/download/@volar/code-gen-0.30.2.tgz}
name: '@volar/code-gen'
version: 0.30.1
version: 0.30.2
dependencies:
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.1
'@volar/source-map': registry.npmmirror.com/@volar/source-map/0.30.1
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.2
'@volar/source-map': registry.npmmirror.com/@volar/source-map/0.30.2
dev: true
registry.npmmirror.com/@volar/html2pug/0.30.1:
resolution: {integrity: sha512-ojJPrb4qSLrVNl9LTtdjZ5MFyeHmwJK4OVPTgFc/lyQ94nPS9JHba86SaTwDb2XEgiXBWQVEo12bRr3lW9H2+Q==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/html2pug/download/@volar/html2pug-0.30.1.tgz}
registry.npmmirror.com/@volar/html2pug/0.30.2:
resolution: {integrity: sha512-k/DLGoXALaQgnacP1MoJ77AwnCHlKcsQKJJug8Qdou3+yOrzYjSSEP6uwG8BS0Fv1h4d4JYmlXsxW8gJPGXSQQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/html2pug/download/@volar/html2pug-0.30.2.tgz}
name: '@volar/html2pug'
version: 0.30.1
version: 0.30.2
dependencies:
domelementtype: registry.nlark.com/domelementtype/2.2.0
domhandler: registry.npmmirror.com/domhandler/4.3.0
htmlparser2: registry.npmmirror.com/htmlparser2/7.2.0
pug: registry.nlark.com/pug/3.0.2
pug: registry.npmmirror.com/pug/3.0.2
dev: true
registry.npmmirror.com/@volar/shared/0.30.1:
resolution: {integrity: sha512-6F5yQYeN+gbXAKplxHDvj4Ei+rHCjNhwkfZnGpaSpEU92uSI2vK/HfEdd/zTKdAZpwz0RjliNuFoXLi6umtQ0w==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/shared/download/@volar/shared-0.30.1.tgz}
registry.npmmirror.com/@volar/shared/0.30.2:
resolution: {integrity: sha512-93Q7i758WjScg4ptvDcpk66r4Paz9StVMH/M5RCsU4/9F5a1xSCUJbkbpwE0zESkVzcuBatDqk79PaZ8TZKqRg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/shared/download/@volar/shared-0.30.2.tgz}
name: '@volar/shared'
version: 0.30.1
version: 0.30.2
dependencies:
upath: registry.nlark.com/upath/2.0.1
vscode-jsonrpc: registry.npmmirror.com/vscode-jsonrpc/8.0.0-next.4
vscode-uri: registry.npmmirror.com/vscode-uri/3.0.3
dev: true
registry.npmmirror.com/@volar/source-map/0.30.1:
resolution: {integrity: sha512-QGi36KBGHZ4gq81jPSi3W2wRcpso9Apd59AZOv/H98k5hU9zo8wA5hwartZHiybTlT1q/0Yno3agSj+vK2vocw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/source-map/download/@volar/source-map-0.30.1.tgz}
registry.npmmirror.com/@volar/source-map/0.30.2:
resolution: {integrity: sha512-gwa9OiSjUOZWYutJX53m/KDU/zaF0yN3RP2B8J0aMVyT5dE/VaIfknSxPAW5QFC6AT79Ea1HKGtChuSBgqHCfA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/source-map/download/@volar/source-map-0.30.2.tgz}
name: '@volar/source-map'
version: 0.30.1
version: 0.30.2
dependencies:
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.1
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.2
vscode-languageserver-textdocument: registry.npmmirror.com/vscode-languageserver-textdocument/1.0.3
dev: true
registry.npmmirror.com/@volar/transforms/0.30.1:
resolution: {integrity: sha512-dWFyfQGLoZ8LZI93zud0c5uoCdlkwDBi90G/XaaKfXxkX+3eiJrM0lJ/d1Nc0L04t9mb28I5hpVK68vH90+Tlw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/transforms/download/@volar/transforms-0.30.1.tgz}
registry.npmmirror.com/@volar/transforms/0.30.2:
resolution: {integrity: sha512-bc+55NGlBbMLHkpChqAEgsblYJxjNHiKVMbVUMi52xKa2l9gOWXNbn5WRsHfz4AR+Cq/Zm0AvVJ10ehLQsGsow==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/transforms/download/@volar/transforms-0.30.2.tgz}
name: '@volar/transforms'
version: 0.30.1
version: 0.30.2
dependencies:
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.1
vscode-languageserver: registry.npmmirror.com/vscode-languageserver/8.0.0-next.5
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.2
vscode-languageserver-types: registry.npmmirror.com/vscode-languageserver-types/3.17.0-next.5
dev: true
registry.npmmirror.com/@volar/vue-code-gen/0.30.1:
resolution: {integrity: sha512-+0egr84YOYLudP6jRXRm+xtAL92GTPaq0U0lsorLTBp/MB14Fap6HMUr/LEeNB5tnND36UQJiUWHM5eTDAAb4Q==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/vue-code-gen/download/@volar/vue-code-gen-0.30.1.tgz}
registry.npmmirror.com/@volar/vue-code-gen/0.30.2:
resolution: {integrity: sha512-kJyVkQFhMvVQ32aDaC6h5DdXG1GJbJjeeAkCnjfCJfMmuYjM5R4QNZHDz1TI0dETgF9vP+kbAukQKNWecHK3qg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@volar/vue-code-gen/download/@volar/vue-code-gen-0.30.2.tgz}
name: '@volar/vue-code-gen'
version: 0.30.1
version: 0.30.2
dependencies:
'@volar/code-gen': registry.npmmirror.com/@volar/code-gen/0.30.1
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.1
'@volar/source-map': registry.npmmirror.com/@volar/source-map/0.30.1
'@volar/code-gen': registry.npmmirror.com/@volar/code-gen/0.30.2
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.2
'@volar/source-map': registry.npmmirror.com/@volar/source-map/0.30.2
'@vue/compiler-core': registry.npmmirror.com/@vue/compiler-core/3.2.26
'@vue/compiler-dom': registry.npmmirror.com/@vue/compiler-dom/3.2.26
'@vue/shared': registry.npmmirror.com/@vue/shared/3.2.26
@ -3881,8 +3910,8 @@ packages:
eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
eslint-plugin-vue: ^8.0.1
dependencies:
'@typescript-eslint/eslint-plugin': registry.npmmirror.com/@typescript-eslint/eslint-plugin/5.8.1_13039593e64cd539d0b4c5c2da390958
'@typescript-eslint/parser': registry.npmmirror.com/@typescript-eslint/parser/5.8.1_eslint@8.6.0+typescript@4.5.4
'@typescript-eslint/eslint-plugin': registry.npmmirror.com/@typescript-eslint/eslint-plugin/5.8.1_bd2fd93dbcc607ad2f21b784bccfe0c8
'@typescript-eslint/parser': registry.npmmirror.com/@typescript-eslint/parser/5.9.0_eslint@8.6.0+typescript@4.5.4
eslint: registry.npmmirror.com/eslint/8.6.0
eslint-plugin-vue: registry.npmmirror.com/eslint-plugin-vue/8.2.0_eslint@8.6.0
vue-eslint-parser: registry.npmmirror.com/vue-eslint-parser/8.0.1_eslint@8.6.0
@ -4766,7 +4795,7 @@ packages:
esbuild-windows-arm64: registry.npmmirror.com/esbuild-windows-arm64/0.13.15
dev: true
registry.npmmirror.com/eslint-config-airbnb-base/15.0.0_b54597effeb7d09a84a334cc3c3dadae:
registry.npmmirror.com/eslint-config-airbnb-base/15.0.0_b5a36b8c1535387c8dd00eff7ec6b551:
resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/eslint-config-airbnb-base/download/eslint-config-airbnb-base-15.0.0.tgz}
id: registry.npmmirror.com/eslint-config-airbnb-base/15.0.0
name: eslint-config-airbnb-base
@ -4778,28 +4807,27 @@ packages:
dependencies:
confusing-browser-globals: registry.npmmirror.com/confusing-browser-globals/1.0.11
eslint: registry.npmmirror.com/eslint/8.6.0
eslint-plugin-import: registry.npmmirror.com/eslint-plugin-import/2.25.3_eslint@8.6.0
eslint-plugin-import: registry.npmmirror.com/eslint-plugin-import/2.25.4_eslint@8.6.0
object.assign: registry.nlark.com/object.assign/4.1.2
object.entries: registry.npmmirror.com/object.entries/1.1.5
semver: registry.nlark.com/semver/6.3.0
dev: true
registry.npmmirror.com/eslint-module-utils/2.7.1:
resolution: {integrity: sha1-tDUAHJ+N1Kt/bQ78rkuWltTCS3w=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/eslint-module-utils/download/eslint-module-utils-2.7.1.tgz}
registry.npmmirror.com/eslint-module-utils/2.7.2:
resolution: {integrity: sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/eslint-module-utils/download/eslint-module-utils-2.7.2.tgz}
name: eslint-module-utils
version: 2.7.1
version: 2.7.2
engines: {node: '>=4'}
dependencies:
debug: registry.npmmirror.com/debug/3.2.7
find-up: registry.npmmirror.com/find-up/2.1.0
pkg-dir: registry.npmmirror.com/pkg-dir/2.0.0
dev: true
registry.npmmirror.com/eslint-plugin-import/2.25.3_eslint@8.6.0:
resolution: {integrity: sha512-RzAVbby+72IB3iOEL8clzPLzL3wpDrlwjsTBAQXgyp5SeTqqY+0bFubwuo+y/HLhNZcXV4XqTBO4LGsfyHIDXg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/eslint-plugin-import/download/eslint-plugin-import-2.25.3.tgz}
id: registry.npmmirror.com/eslint-plugin-import/2.25.3
registry.npmmirror.com/eslint-plugin-import/2.25.4_eslint@8.6.0:
resolution: {integrity: sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/eslint-plugin-import/download/eslint-plugin-import-2.25.4.tgz}
id: registry.npmmirror.com/eslint-plugin-import/2.25.4
name: eslint-plugin-import
version: 2.25.3
version: 2.25.4
engines: {node: '>=4'}
peerDependencies:
eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
@ -4810,13 +4838,13 @@ packages:
doctrine: registry.nlark.com/doctrine/2.1.0
eslint: registry.npmmirror.com/eslint/8.6.0
eslint-import-resolver-node: registry.nlark.com/eslint-import-resolver-node/0.3.6
eslint-module-utils: registry.npmmirror.com/eslint-module-utils/2.7.1
eslint-module-utils: registry.npmmirror.com/eslint-module-utils/2.7.2
has: registry.nlark.com/has/1.0.3
is-core-module: registry.npmmirror.com/is-core-module/2.8.0
is-glob: registry.npmmirror.com/is-glob/4.0.3
minimatch: registry.nlark.com/minimatch/3.0.4
object.values: registry.npmmirror.com/object.values/1.1.5
resolve: registry.nlark.com/resolve/1.20.0
resolve: registry.npmmirror.com/resolve/1.20.0
tsconfig-paths: registry.npmmirror.com/tsconfig-paths/3.12.0
dev: true
@ -5192,7 +5220,7 @@ packages:
dev: true
registry.npmmirror.com/htmlparser2/7.2.0:
resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/htmlparser2/download/htmlparser2-7.2.0.tgz?cache=0&sync_timestamp=1636641904600&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fhtmlparser2%2Fdownload%2Fhtmlparser2-7.2.0.tgz}
resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/htmlparser2/download/htmlparser2-7.2.0.tgz?cache=0&sync_timestamp=1636641716463&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fhtmlparser2%2Fdownload%2Fhtmlparser2-7.2.0.tgz}
name: htmlparser2
version: 7.2.0
dependencies:
@ -5380,10 +5408,10 @@ packages:
version: 1.2.4
dev: true
registry.npmmirror.com/lint-staged/12.1.4:
resolution: {integrity: sha512-RgDz9nsFsE0/5eL9Vat0AvCuk0+j5mEuzBIVfrRH5FRtt5wibYe8zTjZs2nuqLFrLAGQGYnj8+HJxolcj08i/A==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/lint-staged/download/lint-staged-12.1.4.tgz}
registry.npmmirror.com/lint-staged/12.1.5:
resolution: {integrity: sha512-WyKb+0sNKDTd1LwwAfTBPp0XmdaKkAOEbg4oHE4Kq2+oQVchg/VAcjVQtSqZih1izNsTURjc2EkhG/syRQUXdA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/lint-staged/download/lint-staged-12.1.5.tgz}
name: lint-staged
version: 12.1.4
version: 12.1.5
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
hasBin: true
dependencies:
@ -5599,7 +5627,7 @@ packages:
version: 4.0.1
engines: {node: '>=8'}
dependencies:
path-key: registry.nlark.com/path-key/3.1.1
path-key: registry.npmmirror.com/path-key/3.1.1
dev: true
registry.npmmirror.com/object-inspect/1.12.0:
@ -5683,11 +5711,25 @@ packages:
lines-and-columns: registry.npmmirror.com/lines-and-columns/1.2.4
dev: true
registry.npmmirror.com/path-key/3.1.1:
resolution: {integrity: sha1-WB9q3mWMu6ZaDTOA3ndTKVBU83U=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/path-key/download/path-key-3.1.1.tgz}
name: path-key
version: 3.1.1
engines: {node: '>=8'}
dev: true
registry.npmmirror.com/picocolors/1.0.0:
resolution: {integrity: sha1-y1vcdP8/UYkiNur3nWi8RFZKuBw=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/picocolors/download/picocolors-1.0.0.tgz}
name: picocolors
version: 1.0.0
registry.npmmirror.com/picomatch/2.3.0:
resolution: {integrity: sha1-8fBh3o9qS/AiiS4tEoI0+5gwKXI=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/picomatch/download/picomatch-2.3.0.tgz}
name: picomatch
version: 2.3.0
engines: {node: '>=8.6'}
dev: true
registry.npmmirror.com/pinia/2.0.9_typescript@4.5.4+vue@3.2.26:
resolution: {integrity: sha512-iuYdxLJKQ07YPyOHYH05wNG9eKWqkP/4y4GE8+RqEYtz5fwHgPA5kr6zQbg/DoEJGnR2XCm1w1vdt6ppzL9ATg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/pinia/download/pinia-2.0.9.tgz}
id: registry.npmmirror.com/pinia/2.0.9
@ -5709,15 +5751,6 @@ packages:
vue-demi: registry.npmmirror.com/vue-demi/0.12.1_vue@3.2.26
dev: false
registry.npmmirror.com/pkg-dir/2.0.0:
resolution: {integrity: sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/pkg-dir/download/pkg-dir-2.0.0.tgz}
name: pkg-dir
version: 2.0.0
engines: {node: '>=4'}
dependencies:
find-up: registry.npmmirror.com/find-up/2.1.0
dev: true
registry.npmmirror.com/postcss/8.4.5:
resolution: {integrity: sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/postcss/download/postcss-8.4.5.tgz}
name: postcss
@ -5761,12 +5794,35 @@ packages:
engines: {node: '>=0.4.0'}
dev: true
registry.npmmirror.com/promise/7.3.1:
resolution: {integrity: sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/promise/download/promise-7.3.1.tgz}
name: promise
version: 7.3.1
dependencies:
asap: registry.nlark.com/asap/2.0.6
dev: true
registry.npmmirror.com/pug-error/2.0.0:
resolution: {integrity: sha1-XGIXPLCcNN4qLOBPF7it/sdNjKU=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/pug-error/download/pug-error-2.0.0.tgz}
name: pug-error
version: 2.0.0
dev: true
registry.npmmirror.com/pug/3.0.2:
resolution: {integrity: sha1-81xxBzQ0VOQ7wnrg/3bHMbeOpTU=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/pug/download/pug-3.0.2.tgz}
name: pug
version: 3.0.2
dependencies:
pug-code-gen: registry.nlark.com/pug-code-gen/3.0.2
pug-filters: registry.nlark.com/pug-filters/4.0.0
pug-lexer: registry.nlark.com/pug-lexer/5.0.1
pug-linker: registry.nlark.com/pug-linker/4.0.0
pug-load: registry.nlark.com/pug-load/3.0.0
pug-parser: registry.nlark.com/pug-parser/6.0.0
pug-runtime: registry.nlark.com/pug-runtime/3.0.1
pug-strip-comments: registry.nlark.com/pug-strip-comments/2.0.0
dev: true
registry.npmmirror.com/qs/6.10.2:
resolution: {integrity: sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/qs/download/qs-6.10.2.tgz}
name: qs
@ -5805,6 +5861,15 @@ packages:
version: 0.5.6
dev: true
registry.npmmirror.com/resolve/1.20.0:
resolution: {integrity: sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/resolve/download/resolve-1.20.0.tgz}
name: resolve
version: 1.20.0
dependencies:
is-core-module: registry.npmmirror.com/is-core-module/2.8.0
path-parse: registry.nlark.com/path-parse/1.0.7
dev: true
registry.npmmirror.com/reusify/1.0.4:
resolution: {integrity: sha1-kNo4Kx4SbvwCFG6QhFqI2xKSXXY=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/reusify/download/reusify-1.0.4.tgz}
name: reusify
@ -6421,30 +6486,32 @@ packages:
version: 5.0.0
dev: true
registry.npmmirror.com/vscode-pug-languageservice/0.30.1:
resolution: {integrity: sha512-ytco+lziRQNrpHpI8X+/rhYaX4KUWAnYZHd1f1epu2m+9WoIf9swbk8/slIOeyec1DPg4Y7AS8hTLcEfOfY71g==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vscode-pug-languageservice/download/vscode-pug-languageservice-0.30.1.tgz}
registry.npmmirror.com/vscode-pug-languageservice/0.30.2:
resolution: {integrity: sha512-YkrBodqSzNrtLaEIeMnRJAcnqCWysIiOdkzxF6XHuOc+wDvbZ1U4XgxoLvNNjQdzNQIEYKbsLW0ldq5TYphjiA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vscode-pug-languageservice/download/vscode-pug-languageservice-0.30.2.tgz}
name: vscode-pug-languageservice
version: 0.30.1
version: 0.30.2
dependencies:
'@volar/code-gen': registry.npmmirror.com/@volar/code-gen/0.30.1
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.1
'@volar/source-map': registry.npmmirror.com/@volar/source-map/0.30.1
'@volar/transforms': registry.npmmirror.com/@volar/transforms/0.30.1
'@volar/code-gen': registry.npmmirror.com/@volar/code-gen/0.30.2
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.2
'@volar/source-map': registry.npmmirror.com/@volar/source-map/0.30.2
'@volar/transforms': registry.npmmirror.com/@volar/transforms/0.30.2
pug-lexer: registry.nlark.com/pug-lexer/5.0.1
pug-parser: registry.nlark.com/pug-parser/6.0.0
vscode-languageserver: registry.npmmirror.com/vscode-languageserver/8.0.0-next.5
vscode-languageserver-textdocument: registry.npmmirror.com/vscode-languageserver-textdocument/1.0.3
vscode-languageserver-types: registry.npmmirror.com/vscode-languageserver-types/3.17.0-next.5
dev: true
registry.npmmirror.com/vscode-typescript-languageservice/0.30.1:
resolution: {integrity: sha512-7EBJiaLXThlrbm2K5VU+qWPR3z+RtmCFLWiZaNdJYO/E5UFBQiPmO8qXlxcB2x1N7zId2GZoogAbT15oexY2eQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vscode-typescript-languageservice/download/vscode-typescript-languageservice-0.30.1.tgz}
registry.npmmirror.com/vscode-typescript-languageservice/0.30.2:
resolution: {integrity: sha512-5l+gMfbHTZnJy7V7xdH78ai1ViR/scrIVQT5KFraDWLKTYHjGBkHDZ1E9fF+jbeyEizyy2ayldTQ7kCz8jWqVA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vscode-typescript-languageservice/download/vscode-typescript-languageservice-0.30.2.tgz}
name: vscode-typescript-languageservice
version: 0.30.1
version: 0.30.2
dependencies:
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.1
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.2
semver: registry.nlark.com/semver/7.3.5
upath: registry.nlark.com/upath/2.0.1
vscode-languageserver: registry.npmmirror.com/vscode-languageserver/8.0.0-next.5
vscode-languageserver-protocol: registry.npmmirror.com/vscode-languageserver-protocol/3.17.0-next.11
vscode-languageserver-textdocument: registry.npmmirror.com/vscode-languageserver-textdocument/1.0.3
vscode-nls: registry.npmmirror.com/vscode-nls/5.0.0
dev: true
registry.npmmirror.com/vscode-uri/2.1.2:
@ -6459,17 +6526,17 @@ packages:
version: 3.0.3
dev: true
registry.npmmirror.com/vscode-vue-languageservice/0.30.1:
resolution: {integrity: sha512-l9R5vXkrGY0N4hA2o9ZFBIKL44z7UbHc45YjOmnslGwYL15YXfb7T4quW8VYSWJNRbiFBTNnNLWORIDEcWazBA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vscode-vue-languageservice/download/vscode-vue-languageservice-0.30.1.tgz}
registry.npmmirror.com/vscode-vue-languageservice/0.30.2:
resolution: {integrity: sha512-P0g92JmnVkV2zrWhDbT2zxuOUp0X2kMM9VHlrT7ALZq8wAhPOW0B4nhhb9a6jKBh6qqFBDquNeQRvTEZp4NJMA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vscode-vue-languageservice/download/vscode-vue-languageservice-0.30.2.tgz}
name: vscode-vue-languageservice
version: 0.30.1
version: 0.30.2
dependencies:
'@volar/code-gen': registry.npmmirror.com/@volar/code-gen/0.30.1
'@volar/html2pug': registry.npmmirror.com/@volar/html2pug/0.30.1
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.1
'@volar/source-map': registry.npmmirror.com/@volar/source-map/0.30.1
'@volar/transforms': registry.npmmirror.com/@volar/transforms/0.30.1
'@volar/vue-code-gen': registry.npmmirror.com/@volar/vue-code-gen/0.30.1
'@volar/code-gen': registry.npmmirror.com/@volar/code-gen/0.30.2
'@volar/html2pug': registry.npmmirror.com/@volar/html2pug/0.30.2
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.2
'@volar/source-map': registry.npmmirror.com/@volar/source-map/0.30.2
'@volar/transforms': registry.npmmirror.com/@volar/transforms/0.30.2
'@volar/vue-code-gen': registry.npmmirror.com/@volar/vue-code-gen/0.30.2
'@vscode/emmet-helper': registry.npmmirror.com/@vscode/emmet-helper/2.8.3
'@vue/reactivity': registry.npmmirror.com/@vue/reactivity/3.2.26
'@vue/shared': registry.npmmirror.com/@vue/shared/3.2.26
@ -6479,9 +6546,10 @@ packages:
vscode-html-languageservice: registry.npmmirror.com/vscode-html-languageservice/4.2.1
vscode-json-languageservice: registry.npmmirror.com/vscode-json-languageservice/4.1.10
vscode-languageserver: registry.npmmirror.com/vscode-languageserver/8.0.0-next.5
vscode-languageserver-protocol: registry.npmmirror.com/vscode-languageserver-protocol/3.17.0-next.11
vscode-languageserver-textdocument: registry.npmmirror.com/vscode-languageserver-textdocument/1.0.3
vscode-pug-languageservice: registry.npmmirror.com/vscode-pug-languageservice/0.30.1
vscode-typescript-languageservice: registry.npmmirror.com/vscode-typescript-languageservice/0.30.1
vscode-pug-languageservice: registry.npmmirror.com/vscode-pug-languageservice/0.30.2
vscode-typescript-languageservice: registry.npmmirror.com/vscode-typescript-languageservice/0.30.2
dev: true
registry.npmmirror.com/vue-demi/0.12.1_vue@3.2.26:
@ -6535,18 +6603,18 @@ packages:
vue: registry.npmmirror.com/vue/3.2.26
dev: false
registry.npmmirror.com/vue-tsc/0.30.1_typescript@4.5.4:
resolution: {integrity: sha512-AVBPWF70LvuzAt6phaF3U8pg1WmjmZQBfZvkX4Ve9EHTPh4R2JiJnSjf3MQgnx03qF5w0PGkBJ90l12aaLZeKQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vue-tsc/download/vue-tsc-0.30.1.tgz}
id: registry.npmmirror.com/vue-tsc/0.30.1
registry.npmmirror.com/vue-tsc/0.30.2_typescript@4.5.4:
resolution: {integrity: sha512-A8KIk5KwQTbSdsrDxwJkFYLPqDJ1zM86w3X8cgpi6rveozKUGDMPt300awEz61sTuBM9fAfUhNRcsWbsJ1I+TQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vue-tsc/download/vue-tsc-0.30.2.tgz}
id: registry.npmmirror.com/vue-tsc/0.30.2
name: vue-tsc
version: 0.30.1
version: 0.30.2
hasBin: true
peerDependencies:
typescript: '*'
dependencies:
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.1
'@volar/shared': registry.npmmirror.com/@volar/shared/0.30.2
typescript: registry.npmmirror.com/typescript/4.5.4
vscode-vue-languageservice: registry.npmmirror.com/vscode-vue-languageservice/0.30.1
vscode-vue-languageservice: registry.npmmirror.com/vscode-vue-languageservice/0.30.2
dev: true
registry.npmmirror.com/vue/3.2.26:

View File

@ -1,5 +1,5 @@
<template>
<n-config-provider :theme-overrides="theme.naiveThemeOverrides" class="h-full">
<n-config-provider :theme="theme.naiveTheme" :theme-overrides="theme.naiveThemeOverrides" class="h-full">
<slot></slot>
</n-config-provider>
</template>

View File

@ -0,0 +1,48 @@
<template>
<div class="w-full text-14px">
<n-checkbox v-model:checked="checked">我已经仔细阅读并接受</n-checkbox>
<n-button :text="true" type="primary" @click="handleClickProtocol">用户协议</n-button>
<n-button :text="true" type="primary" @click="handleClickPolicy">隐私权政策</n-button>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { NCheckbox, NButton } from 'naive-ui';
interface Props {
/** 是否勾选 */
value?: boolean;
}
interface Emits {
(e: 'update:value', value: boolean): void;
/** 点击协议 */
(e: 'click-protocol'): void;
/** 点击隐私政策 */
(e: 'click-policy'): void;
}
const props = withDefaults(defineProps<Props>(), {
value: true
});
const emit = defineEmits<Emits>();
const checked = computed({
get() {
return props.value;
},
set(newValue: boolean) {
emit('update:value', newValue);
}
});
function handleClickProtocol() {
emit('click-protocol');
}
function handleClickPolicy() {
emit('click-policy');
}
</script>
<style scoped></style>

View File

@ -0,0 +1,3 @@
import LoginAgreement from './LoginAgreement/index.vue';
export { LoginAgreement };

View File

@ -0,0 +1,39 @@
<template>
<div class="text-18px hover:text-primary cursor-pointer" @click="handleSwitch">
<icon-mdi-moon-waning-crescent v-if="darkMode" />
<icon-mdi-white-balance-sunny v-else />
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
interface Props {
/** 暗黑模式 */
dark?: boolean;
}
interface Emits {
(e: 'update:dark', darkMode: boolean): void;
}
const props = withDefaults(defineProps<Props>(), {
dark: false
});
const emit = defineEmits<Emits>();
const darkMode = computed({
get() {
return props.dark;
},
set(newValue: boolean) {
emit('update:dark', newValue);
}
});
function handleSwitch() {
darkMode.value = !darkMode.value;
}
</script>
<style scoped></style>

View File

@ -1,3 +1,4 @@
import SystemLogo from './SystemLogo/index.vue';
import DarkModeSwitch from './DarkModeSwitch/index.vue';
export { SystemLogo };
export { SystemLogo, DarkModeSwitch };

View File

@ -0,0 +1,39 @@
<template>
<div>
<canvas ref="domRef" width="152" height="40" class="cursor-pointer" @click="getImgCode"></canvas>
</div>
</template>
<script setup lang="ts">
import { watch } from 'vue';
import { useImageVerify } from '@/hooks';
interface Props {
code?: string;
}
interface Emits {
(e: 'update:code', code: string): void;
}
const props = withDefaults(defineProps<Props>(), {
code: ''
});
const emit = defineEmits<Emits>();
const { domRef, imgCode, setImgCode, getImgCode } = useImageVerify();
watch(
() => props.code,
newValue => {
setImgCode(newValue);
}
);
watch(imgCode, newValue => {
emit('update:code', newValue);
});
defineExpose({ getImgCode });
</script>
<style scoped></style>

View File

@ -0,0 +1,3 @@
import ImageVerify from './ImageVerify/index.vue';
export { ImageVerify };

View File

@ -1 +1,3 @@
export * from './common';
export * from './custom';
export * from './business';

View File

@ -1 +1,3 @@
export * from './system';
export * from './router';
export * from './route';

View File

@ -0,0 +1,32 @@
import { unref, computed } from 'vue';
import { useRoute } from 'vue-router';
import { router } from '@/router';
import { useRouteStore } from '@/store';
/**
*
* @param inSetup - vue页面/setup里面调用axios里面无法使用useRouter和useRoute
*/
export function useRouteQuery(inSetup: boolean = true) {
const { getRouteName } = useRouteStore();
const route = getRouteInstance(inSetup);
/** 登录后跳转的地址 */
const loginRedirect = computed(() => {
let url: string | undefined;
if (route.name === getRouteName('login')) {
url = (route.query?.redirect as string) || '';
}
return url;
});
return {
loginRedirect
};
}
function getRouteInstance(inSetup: boolean = true) {
const route = inSetup ? useRoute() : unref(router.currentRoute);
return route;
}

View File

@ -0,0 +1,90 @@
import { unref } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import type { RouteLocationRaw } from 'vue-router';
import { router as globalRouter } from '@/router';
import { useRouteStore } from '@/store';
import { LoginModuleKey } from '@/interface';
/**
*
* @param inSetup - vue页面/setup里面调用axios里面无法使用useRouter和useRoute
*/
export function useRouterPush(inSetup: boolean = true) {
const { getRouteName } = useRouteStore();
const router = inSetup ? useRouter() : globalRouter;
const route = inSetup ? useRoute() : unref(globalRouter.currentRoute);
/**
*
* @param to -
* @param newTab - Tab标签打开
*/
function routerPush(to: RouteLocationRaw, newTab = false) {
if (newTab) {
const routerData = router.resolve(to);
window.open(routerData.href, '_blank');
} else {
router.push(to);
}
}
/** 返回上一级路由 */
function routerBack() {
router.go(-1);
}
/**
*
* @param newTab -
*/
function toHome(newTab = false) {
routerPush(getRouteName('root'), newTab);
}
/**
*
* @param loginModule -
* @param redirectUrl - (),undefined表示取当前地址为重定向地址
*/
function toLogin(loginModule?: LoginModuleKey, redirectUrl?: string) {
const module: LoginModuleKey = loginModule || 'pwd-login';
const routeLocation: RouteLocationRaw = {
name: getRouteName('login'),
params: { module }
};
const redirect = redirectUrl || route.fullPath;
Object.assign(routeLocation, { query: { redirect } });
routerPush(routeLocation);
}
/**
*
* @param module -
*/
function toLoginModule(module: LoginModuleKey) {
const { query } = route;
routerPush({ name: getRouteName('login'), params: { module }, query });
}
/**
*
* @param redirect -
*/
function toLoginRedirect(redirect?: string) {
if (redirect) {
routerPush(redirect);
} else {
toHome();
}
}
return {
routerPush,
routerBack,
toHome,
toLogin,
toLoginModule,
toLoginRedirect
};
}

View File

@ -1 +1,2 @@
export * from './service';
export * from './regexp';

View File

@ -0,0 +1,16 @@
/** 手机号码正则 */
export const REGEXP_PHONE =
/^[1](([3][0-9])|([4][0,1,4-9])|([5][0-3,5-9])|([6][2,5,6,7])|([7][0-8])|([8][0-9])|([9][0-3,5-9]))[0-9]{8}$/;
/** 邮箱正则 */
export const REGEXP_EMAIL = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
/** 密码正则(密码为8-18位数字/字符/符号的组合) */
export const REGEXP_PWD =
/^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){8,18}$/;
/** 6位数字验证码正则 */
export const REGEXP_CODE_SIX = /^\d{6}$/;
/** 4位数字验证码正则 */
export const REGEXP_CODE_FOUR = /^\d{4}$/;

View File

@ -0,0 +1,5 @@
import useCountDown from './useCountDown';
import useSmsCode from './useSmsCode';
import useImageVerify from './useImageVerify';
export { useCountDown, useSmsCode, useImageVerify };

View File

@ -0,0 +1,52 @@
import { ref, computed } from 'vue';
import { useBoolean } from '../common';
/**
*
* @param second - (s)
*/
export default function useCountDown(second: number) {
if (second <= 0 && second % 1 !== 0) {
throw Error('倒计时的时间应该为一个正整数!');
}
const { bool: isComplete, setTrue, setFalse } = useBoolean(false);
const counts = ref(0);
const isCounting = computed(() => Boolean(counts.value));
let intervalId: any;
/**
*
* @param updateSecond -
*/
function start(updateSecond: number = second) {
if (!counts.value) {
setFalse();
counts.value = updateSecond;
intervalId = setInterval(() => {
counts.value -= 1;
if (counts.value <= 0) {
clearInterval(intervalId);
setTrue();
}
}, 1000);
}
}
/**
*
*/
function stop() {
intervalId = clearInterval(intervalId);
counts.value = 0;
}
return {
counts,
isCounting,
start,
stop,
isComplete
};
}

View File

@ -0,0 +1,85 @@
import { ref, onMounted } from 'vue';
/**
*
* @param width -
* @param height -
*/
export default function useImageVerify(width = 152, height = 40) {
const domRef = ref<HTMLCanvasElement>();
const imgCode = ref('');
function setImgCode(code: string) {
imgCode.value = code;
}
function getImgCode() {
if (!domRef.value) return;
imgCode.value = draw(domRef.value, width, height);
}
onMounted(() => {
getImgCode();
});
return {
domRef,
imgCode,
setImgCode,
getImgCode
};
}
function randomNum(min: number, max: number) {
const num = Math.floor(Math.random() * (max - min) + min);
return num;
}
function randomColor(min: number, max: number) {
const r = randomNum(min, max);
const g = randomNum(min, max);
const b = randomNum(min, max);
return `rgb(${r},${g},${b})`;
}
function draw(dom: HTMLCanvasElement, width: number, height: number) {
let imgCode = '';
const NUMBER_STRING = '0123456789';
const ctx = dom.getContext('2d');
if (!ctx) return imgCode;
ctx.fillStyle = randomColor(180, 230);
ctx.fillRect(0, 0, width, height);
for (let i = 0; i < 4; i += 1) {
const text = NUMBER_STRING[randomNum(0, NUMBER_STRING.length)];
imgCode += text;
const fontSize = randomNum(18, 41);
const deg = randomNum(-30, 30);
ctx.font = `${fontSize}px Simhei`;
ctx.textBaseline = 'top';
ctx.fillStyle = randomColor(80, 150);
ctx.save();
ctx.translate(30 * i + 23, 15);
ctx.rotate((deg * Math.PI) / 180);
ctx.fillText(text, -15 + 5, -15);
ctx.restore();
}
for (let i = 0; i < 5; i += 1) {
ctx.beginPath();
ctx.moveTo(randomNum(0, width), randomNum(0, height));
ctx.lineTo(randomNum(0, width), randomNum(0, height));
ctx.strokeStyle = randomColor(180, 230);
ctx.closePath();
ctx.stroke();
}
for (let i = 0; i < 41; i += 1) {
ctx.beginPath();
ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = randomColor(150, 200);
ctx.fill();
}
return imgCode;
}

View File

@ -0,0 +1,59 @@
import { computed } from 'vue';
import { REGEXP_PHONE } from '@/config';
import { fetchSmsCode } from '@/service';
import { useLoading } from '../common';
import useCountDown from './useCountDown';
export default function useSmsCode() {
const { loading, startLoading, endLoading } = useLoading();
const { counts, start, isCounting } = useCountDown(60);
const initLabel = '获取验证码';
const countingLabel = (second: number) => `${second}秒后重新获取`;
const label = computed(() => {
let text = initLabel;
if (loading.value) {
text = '';
}
if (isCounting.value) {
text = countingLabel(counts.value);
}
return text;
});
/** 判断手机号码格式是否正确 */
function isPhoneValid(phone: string) {
let valid = true;
if (phone.trim() === '') {
window.$message?.error('手机号码不能为空!');
valid = false;
} else if (!REGEXP_PHONE.test(phone)) {
window.$message?.error('手机号码格式错误!');
valid = false;
}
return valid;
}
/**
*
* @param phone -
*/
async function getSmsCode(phone: string) {
const valid = isPhoneValid(phone);
if (!valid || loading.value) return;
startLoading();
const { data } = await fetchSmsCode(phone);
if (data) {
window.$message?.success('验证码发送成功!');
start();
}
endLoading();
}
return {
label,
start,
isCounting,
getSmsCode,
loading
};
}

View File

@ -1 +1,2 @@
export * from './common';
export * from './business';

View File

@ -2,5 +2,3 @@ import { EnumLoginModule } from '@/enum';
/** 登录模块 */
export type LoginModuleKey = keyof typeof EnumLoginModule;
export type LoginModuleRegexp = LoginModuleKey;

View File

@ -15,7 +15,7 @@ export async function handlePagePermission(
const permissions = to.meta.permissions || [];
const needLogin = Boolean(to.meta?.requiresAuth) || Boolean(permissions.length);
const hasPermission = !permissions.length || permissions.includes(auth.role);
const hasPermission = !permissions.length || permissions.includes(auth.userInfo.userRole);
if (!route.isAddedDynamicRoute) {
// 添加动态路由

View File

@ -1,4 +1,5 @@
// import { getLoginModuleRegExp } from '@/utils';
import { getLoginModuleRegExp } from '@/utils';
import type { LoginModuleKey } from '@/interface';
/** 固定的路由 */
const constantRoutes: AuthRoute.Route[] = [
@ -12,8 +13,14 @@ const constantRoutes: AuthRoute.Route[] = [
},
{
name: 'login',
path: '/login',
path: `/login/:module(${getLoginModuleRegExp()})?`,
component: 'blank',
props: route => {
const moduleType = (route.params.module as LoginModuleKey) || 'pwd-login';
return {
module: moduleType
};
},
meta: {
title: '登录',
single: true

View File

@ -1,15 +1,39 @@
import { mockRequest } from '../request';
import { userRoutesMiddleware } from '../middleware';
/**
*
* @param phone -
* @returns - boolean值表示是否发送成功
*/
export function fetchSmsCode(phone: string) {
return mockRequest.post<boolean>('/getSmsCode', { phone });
}
/**
*
* @param phone -
* @param pwdOrCode -
* @param type - 登录方式: pwd - ; sms -
*/
export function fetchLogin(phone: string, pwdOrCode: string, type: 'pwd' | 'sms') {
if (type === 'pwd') {
return mockRequest.post<ApiAuth.Token>('/loginByPwd', { phone, pwd: pwdOrCode });
}
return mockRequest.post<ApiAuth.Token>('/loginByCode', { phone, code: pwdOrCode });
}
/** 获取用户信息 */
export function fetchUserInfo() {
return mockRequest.get<ApiAuth.UserInfo>('/getUserInfo');
}
/**
*
* @param userId - id
* @description id查询到对应的角色类型
*/
export async function fetchUserRoutes(userId: string = 'soybean') {
const { data } = await mockRequest<ApiRoute.ResponseRoute>(
{ url: '/getUserRoutes', method: 'post', data: { userId } },
false
);
const { data } = await mockRequest.post<ApiRoute.Route>('/getUserRoutes', { userId });
return userRoutesMiddleware(data);
}

View File

@ -1,7 +1,7 @@
import type { RouteRecordRaw } from 'vue-router';
import { transformAuthRouteToVueRoute } from '@/utils';
export function userRoutesMiddleware(data: ApiRoute.ResponseRoute | null) {
export function userRoutesMiddleware(data: ApiRoute.Route | null) {
if (!data) return [];
const routes: RouteRecordRaw[] = data.routes.map(item => transformAuthRouteToVueRoute(item));

View File

@ -6,13 +6,6 @@ import CustomAxiosInstance from './instance';
type RequestMethod = 'get' | 'post' | 'put' | 'delete';
type RequestResultHook<T = any> = {
data: Ref<T | null>;
error: Ref<Service.RequestError | null>;
loading: Ref<boolean>;
network: Ref<boolean>;
};
interface RequestParam {
url: string;
method?: RequestMethod;
@ -20,9 +13,95 @@ interface RequestParam {
axiosConfig?: AxiosRequestConfig;
}
/**
*
* @param axiosConfig - axios配置
*/
export function createRequest(axiosConfig: AxiosRequestConfig) {
const customInstance = new CustomAxiosInstance(axiosConfig);
/**
* promise请求
* @param param -
* - url: 请求地址
* - method: 请求方法(get)
* - data: 请求的body的data
* - axiosConfig: axios配置
*/
async function asyncRequest<T = any>(param: RequestParam): Promise<Service.RequestResult<T>> {
const { url } = param;
const method = param.method || 'get';
const { instance } = customInstance;
const res = (await getRequestResponse(
instance,
method,
url,
param.data,
param.axiosConfig
)) as Service.RequestResult<T>;
return res;
}
/**
* get请求
* @param url -
* @param config - axios配置
*/
function get<T = any>(url: string, config?: AxiosRequestConfig) {
return asyncRequest<T>({ url, method: 'get', axiosConfig: config });
}
/**
* post请求
* @param url -
* @param data - body的data
* @param config - axios配置
*/
function post<T = any>(url: string, data?: any, config?: AxiosRequestConfig) {
return asyncRequest<T>({ url, method: 'post', data, axiosConfig: config });
}
/**
* put请求
* @param url -
* @param data - body的data
* @param config - axios配置
*/
function put<T = any>(url: string, data?: any, config?: AxiosRequestConfig) {
return asyncRequest<T>({ url, method: 'put', data, axiosConfig: config });
}
/**
* delete请求
* @param url -
* @param config - axios配置
*/
function handleDelete<T = any>(url: string, config: AxiosRequestConfig) {
return asyncRequest<T>({ url, method: 'delete', axiosConfig: config });
}
return {
get,
post,
put,
delete: handleDelete
};
}
type RequestResultHook<T = any> = {
data: Ref<T | null>;
error: Ref<Service.RequestError | null>;
loading: Ref<boolean>;
network: Ref<boolean>;
};
/**
* hooks请求
* @param axiosConfig - axios配置
*/
export function createHookRequest(axiosConfig: AxiosRequestConfig) {
const customInstance = new CustomAxiosInstance(axiosConfig);
/**
* hooks请求
* @param param -
@ -30,33 +109,8 @@ export function createRequest(axiosConfig: AxiosRequestConfig) {
* - method: 请求方法(get)
* - data: 请求的body的data
* - axiosConfig: axios配置
* @param hookMode - hook写法
*/
function request<T = any>(param: RequestParam, hookMode: true): RequestResultHook<T>;
function request<T = any>(param: RequestParam, hookMode: false): Promise<Service.RequestResult<T>>;
function request<T = any>(
param: RequestParam,
hookMode: boolean
): RequestResultHook<T> | Promise<Service.RequestResult<T>> {
const { url } = param;
const method = param.method || 'get';
const { instance } = customInstance;
if (hookMode) {
return useRequest(instance, method, url, param.data, param.axiosConfig);
}
return asyncRequest(instance, method, url, param.data, param.axiosConfig);
}
return request;
}
function useRequest<T = any>(
instance: AxiosInstance,
method: RequestMethod,
url: string,
bodyData?: any,
config?: AxiosRequestConfig
): RequestResultHook<T> {
function useRequest<T = any>(param: RequestParam): RequestResultHook<T> {
const { loading, startLoading, endLoading } = useLoading();
const { bool: network, setBool: setNetwork } = useBoolean(window.navigator.onLine);
@ -72,7 +126,11 @@ function useRequest<T = any>(
setNetwork(window.navigator.onLine);
}
getRequestResponse(instance, method, url, bodyData, config).then(handleRequestResult);
const { url } = param;
const method = param.method || 'get';
const { instance } = customInstance;
getRequestResponse(instance, method, url, param.data, param.axiosConfig).then(handleRequestResult);
return {
data,
@ -80,18 +138,51 @@ function useRequest<T = any>(
loading,
network
};
}
}
async function asyncRequest<T = any>(
instance: AxiosInstance,
method: RequestMethod,
url: string,
bodyData?: any,
config?: AxiosRequestConfig
): Promise<Service.RequestResult<T>> {
const res = (await getRequestResponse(instance, method, url, bodyData, config)) as Service.RequestResult<T>;
/**
* get请求
* @param url -
* @param config - axios配置
*/
function get<T = any>(url: string, config?: AxiosRequestConfig) {
return useRequest<T>({ url, method: 'get', axiosConfig: config });
}
return res;
/**
* post请求
* @param url -
* @param data - body的data
* @param config - axios配置
*/
function post<T = any>(url: string, data?: any, config?: AxiosRequestConfig) {
return useRequest<T>({ url, method: 'post', data, axiosConfig: config });
}
/**
* put请求
* @param url -
* @param data - body的data
* @param config - axios配置
*/
function put<T = any>(url: string, data?: any, config?: AxiosRequestConfig) {
return useRequest<T>({ url, method: 'put', data, axiosConfig: config });
}
/**
* delete请求
* @param url -
* @param config - axios配置
*/
function handleDelete<T = any>(url: string, config: AxiosRequestConfig) {
return useRequest<T>({ url, method: 'delete', axiosConfig: config });
}
return {
get,
post,
put,
delete: handleDelete
};
}
async function getRequestResponse(

View File

@ -1,7 +1,11 @@
import { ref, computed, reactive } from 'vue';
import { ref, computed, reactive, unref } from 'vue';
import type { Ref, ComputedRef } from 'vue';
import { defineStore } from 'pinia';
import { getUserInfo, getToken } from '@/utils';
import { router as globalRouter } from '@/router';
import { useRouterPush, useRouteQuery } from '@/composables';
import { useLoading } from '@/hooks';
import { fetchLogin, fetchUserInfo } from '@/service';
import { getUserInfo, getToken, setUserInfo, setToken, setRefreshToken, clearAuthStorage } from '@/utils';
interface AuthStore {
/** 用户信息 */
@ -10,21 +14,94 @@ interface AuthStore {
token: Ref<string>;
/** 是否登录 */
isLogin: ComputedRef<boolean>;
/** 用户角色 */
role: Ref<Auth.RoleType>;
/**
* authStore
* ()
*/
resetAuthStore(pushRoute: boolean): void;
/** 登录的加载状态 */
loginLoding: Ref<boolean>;
/**
*
* @param phone -
* @param pwdOrCode -
* @param type - 登录方式: pwd - ; sms -
*/
login(phone: string, pwdOrCode: string, type: 'pwd' | 'sms'): void;
}
export const useAuthStore = defineStore('auth-store', () => {
const { toLogin, toLoginRedirect } = useRouterPush(false);
const { loginRedirect } = useRouteQuery(false);
const { loading: loginLoding, startLoading: startLoginLoading, endLoading: endLoginLoading } = useLoading();
const userInfo: Auth.UserInfo = reactive(getUserInfo());
function handleSetUserInfo(data: Auth.UserInfo) {
Object.assign(userInfo, data);
}
const token = ref(getToken());
function handleSetToken(data: string) {
token.value = data;
}
const isLogin = computed(() => Boolean(token.value));
const role = ref<Auth.RoleType>('super');
function resetAuthStore(pushRoute: boolean = true) {
const auth = useAuthStore();
const route = unref(globalRouter.currentRoute);
clearAuthStorage();
auth.$reset();
if (pushRoute && route.meta.requiresAuth) {
toLogin();
}
}
async function login(phone: string, pwdOrCode: string, type: 'pwd' | 'sms') {
startLoginLoading();
const { data } = await fetchLogin(phone, pwdOrCode, type);
if (data) {
await loginByToken(data);
}
endLoginLoading();
}
async function loginByToken(backendToken: ApiAuth.Token) {
// 1.先把token存储到缓存中
const { token, refreshToken } = backendToken;
setToken(token);
setRefreshToken(refreshToken);
// 2.获取用户信息
const { data } = await fetchUserInfo();
if (data) {
// 成功后把用户信息存储到缓存中
setUserInfo(data);
handleSetToken(token);
handleSetUserInfo(data);
// 3. 跳转登录后的地址
toLoginRedirect(loginRedirect.value);
// 4.登录成功弹出欢迎提示
window.$notification?.success({
title: '登录成功!',
content: `欢迎回来,${userInfo.userName}!`,
duration: 3000
});
} else {
// 不成功则重置状态
resetAuthStore(false);
}
}
const authStore: AuthStore = {
userInfo,
token,
isLogin,
role
resetAuthStore,
loginLoding,
login
};
return authStore;

View File

@ -26,7 +26,7 @@ interface RouteStore {
*
* @description getRouteName getRoutePath 使 getRouteName
*/
getRoutePath(key: AuthRoute.RouteKey): AuthRoute.RoutePath<''> | undefined;
getRoutePath(key: AuthRoute.RouteKey): AuthRoute.RoutePath | undefined;
/** 获取路由路径 */
getRouteTitle(key: AuthRoute.RouteKey): string | undefined;
}

View File

@ -37,3 +37,27 @@ export function getThemeColors(colors: [ColorType, string][]) {
return themeColor;
}
/** windicss 暗黑模式 */
export function handleWindicssDarkMode() {
const DARK_CLASS = 'dark';
function getHtmlElement() {
return document.querySelector('html');
}
function addDarkClass() {
const html = getHtmlElement();
if (html) {
html.classList.add(DARK_CLASS);
}
}
function removeDarkClass() {
const html = getHtmlElement();
if (html) {
html.classList.remove(DARK_CLASS);
}
}
return {
addDarkClass,
removeDarkClass
};
}

View File

@ -1,11 +1,12 @@
import { ref, computed } from 'vue';
import { ref, computed, watch } from 'vue';
import type { Ref, ComputedRef } from 'vue';
import { defineStore } from 'pinia';
import { useThemeVars } from 'naive-ui';
import type { GlobalThemeOverrides } from 'naive-ui';
import { useThemeVars, darkTheme, useOsTheme } from 'naive-ui';
import type { GlobalThemeOverrides, GlobalTheme } from 'naive-ui';
import { kebabCase } from 'lodash-es';
import { useBoolean } from '@/hooks';
import { getColorPalette } from '@/utils';
import { getThemeColors } from './helpers';
import { getThemeColors, handleWindicssDarkMode } from './helpers';
interface OtherColor {
/** 信息 */
@ -18,19 +19,31 @@ interface OtherColor {
error: string;
}
type BuiltInGlobalTheme = Omit<Required<GlobalTheme>, 'InternalSelectMenu' | 'InternalSelection'>;
interface ThemeStore {
/** 暗黑模式 */
darkMode: Ref<boolean>;
/** 设置暗黑模式 */
setDarkMode(dark: boolean): void;
/** 切换/关闭 暗黑模式 */
toggleDarkMode(dark: boolean): void;
/** 主题颜色 */
themeColor: Ref<string>;
/** 其他颜色 */
otherColor: ComputedRef<OtherColor>;
/** naiveUI的主题配置 */
naiveThemeOverrides: ComputedRef<GlobalThemeOverrides>;
/** naive-ui暗黑主题 */
naiveTheme: ComputedRef<BuiltInGlobalTheme | undefined>;
}
type ThemeVarsKeys = keyof Exclude<GlobalThemeOverrides['common'], undefined>;
export const useThemeStore = defineStore('theme-store', () => {
const themeVars = useThemeVars();
const { bool: darkMode, setBool: setDarkMode, toggle: toggleDarkMode } = useBoolean();
const { addDarkClass, removeDarkClass } = handleWindicssDarkMode();
const themeColor = ref('#1890ff');
const otherColor = computed<OtherColor>(() => ({
@ -62,6 +75,12 @@ export const useThemeStore = defineStore('theme-store', () => {
};
});
/** naive-ui暗黑主题 */
const naiveTheme = computed(() => (darkMode.value ? darkTheme : undefined));
/** 操作系统暗黑主题 */
const osTheme = useOsTheme();
/** 添加css vars至html */
function addThemeCssVarsToHtml() {
if (document.documentElement.style.cssText) return;
@ -82,10 +101,40 @@ export const useThemeStore = defineStore('theme-store', () => {
init();
// 监听操作系统主题模式
watch(
osTheme,
newValue => {
const isDark = newValue === 'dark';
if (isDark) {
setDarkMode(true);
} else {
setDarkMode(false);
}
},
{ immediate: true }
);
// 监听主题的暗黑模式
watch(
() => darkMode.value,
newValue => {
if (newValue) {
addDarkClass();
} else {
removeDarkClass();
}
},
{ immediate: true }
);
const themeStore: ThemeStore = {
darkMode,
setDarkMode,
toggleDarkMode,
themeColor,
otherColor,
naiveThemeOverrides
naiveThemeOverrides,
naiveTheme
};
return themeStore;

10
src/typings/api/auth.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
/** 后端返回的用户权益相关类型 */
declare namespace ApiAuth {
/** 返回的token和刷新token */
interface Token {
token: string;
refreshToken: string;
}
/** 返回的用户信息 */
type UserInfo = Auth.UserInfo;
}

View File

@ -1,7 +1,7 @@
/** 后端返回的路由相关类型 */
declare namespace ApiRoute {
/** 后端返回的路由数据类型 */
interface ResponseRoute {
interface Route {
/** 动态路由 */
routes: AuthRoute.Route[];
/** 路由首页对应的key */

View File

@ -1,15 +1,5 @@
/** 用户相关模块 */
declare namespace Auth {
/** 用户信息 */
interface UserInfo {
/** 用户id */
userId: string;
/** 用户名 */
userName: string;
/** 用户手机号 */
userPhone: string;
}
/**
*
* - super:
@ -18,4 +8,16 @@ declare namespace Auth {
* - visitor: 游客
*/
type RoleType = 'super' | 'admin' | 'test' | 'visitor';
/** 用户信息 */
interface UserInfo {
/** 用户id */
userId: string;
/** 用户名 */
userName: string;
/** 用户手机号 */
userPhone: string;
/** 用户角色类型 */
userRole: RoleType;
}
}

View File

@ -24,7 +24,7 @@ declare namespace AuthRoute {
: `/${Key}`;
/** 路由路径 */
type RoutePath<Key extends string = string> =
type RoutePath<Key extends string = '' | LoginPath> =
| '/'
| Exclude<KeyToPath<RouteKey>, '/root' | '/redirect'>
| Key
@ -65,8 +65,11 @@ declare namespace AuthRoute {
order?: number;
};
/** 登录路由路径 */
type LoginPath = `/login/:module(${string})?`;
/** 单个路由的类型结构(后端返回此类型结构的路由) */
interface Route<T extends string = ''> {
interface Route<T extends string = '' | LoginPath> {
/** 路由名称(路由唯一标识) */
name: RouteKey;
/** 路由路径 */
@ -85,5 +88,7 @@ declare namespace AuthRoute {
children?: Route[];
/** 路由描述 */
meta: RouteMeta;
/** 属性 */
props?: boolean | Record<string, any> | ((to: any) => Record<string, any>);
}
}

View File

@ -12,6 +12,4 @@ declare namespace Util {
type UnionToTuple<T, U = T> = [T] extends [never]
? []
: [LastInUnion<T>, ...UnionToTuple<Exclude<U, LastInUnion<T>>>];
type Inter = UnionToTuple<'1' | '2'>;
}

View File

@ -36,7 +36,8 @@ export function getUserInfo() {
const emptyInfo: Auth.UserInfo = {
userId: '',
userName: '',
userPhone: ''
userPhone: '',
userRole: 'visitor'
};
const userInfo: Auth.UserInfo = getLocal<Auth.UserInfo>(EnumStorageKey['user-info']) || emptyInfo;
return userInfo;

View File

@ -1,4 +1,5 @@
export * from './typeof';
export * from './console';
export * from './color';
export * from './number';
export * from './design-pattern';

View File

@ -0,0 +1,23 @@
/**
*
* @param num - (0-10)
*/
export function getHanByNumber(num: number) {
const HAN_STR = '零一二三四五六七八九十';
return HAN_STR.charAt(num);
}
/**
*
* @param seconds -
*/
export function transformToTimeCountDown(seconds: number) {
const SECONDS_A_MINUTE = 60;
function fillZero(num: number) {
return num.toString().padStart(2, '0');
}
const minuteNum = Math.floor(seconds / SECONDS_A_MINUTE);
const minute = fillZero(minuteNum);
const second = fillZero(seconds - minuteNum * SECONDS_A_MINUTE);
return `${minute}: ${second}`;
}

1
src/utils/form/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './rule';

73
src/utils/form/rule.ts Normal file
View File

@ -0,0 +1,73 @@
import { Ref } from 'vue';
import type { FormItemRule } from 'naive-ui';
import { REGEXP_PHONE, REGEXP_PWD, REGEXP_CODE_SIX, REGEXP_EMAIL } from '@/config';
/** 表单规则 */
interface CustomFormRules {
/** 手机号码 */
phone: FormItemRule[];
/** 密码 */
pwd: FormItemRule[];
/** 验证码 */
code: FormItemRule[];
/** 邮箱 */
email: FormItemRule[];
}
/** 表单规则 */
export const formRules: CustomFormRules = {
phone: [
{ required: true, message: '请输入手机号码' },
{ pattern: REGEXP_PHONE, message: '手机号码格式错误', trigger: 'input' }
],
pwd: [
{ required: true, message: '请输入密码' },
{ pattern: REGEXP_PWD, message: '密码为8-18位数字/字符/符号至少2种组合', trigger: 'input' }
],
code: [
{ required: true, message: '请输入验证码' },
{ pattern: REGEXP_CODE_SIX, message: '验证码格式错误', trigger: 'input' }
],
email: [{ pattern: REGEXP_EMAIL, message: '邮箱格式错误', trigger: 'blur' }]
};
/** 获取确认密码的表单规则 */
export function getConfirmPwdRule(pwd: Ref<string>) {
const confirmPwdRule: FormItemRule[] = [
{ required: true, message: '请输入确认密码' },
{
validator: (rule, value) => {
if (!isBlankString(value) && value !== pwd.value) {
return Promise.reject(rule.message);
}
return Promise.resolve();
},
message: '输入的值与密码不一致',
trigger: 'input'
}
];
return confirmPwdRule;
}
/** 获取图片验证码的表单规则 */
export function getImgCodeRule(imgCode: Ref<string>) {
const imgCodeRule: FormItemRule[] = [
{ required: true, message: '请输入验证码' },
{
validator: (rule, value) => {
if (!isBlankString(value) && value !== imgCode.value) {
return Promise.reject(rule.message);
}
return Promise.resolve();
},
message: '验证码不正确',
trigger: 'blur'
}
];
return imgCodeRule;
}
/** 是否为空字符串 */
function isBlankString(str: string) {
return str.trim() === '';
}

View File

@ -3,3 +3,4 @@ export * from './storage';
export * from './service';
export * from './auth';
export * from './router';
export * from './form';

View File

@ -40,12 +40,18 @@ export function transformAuthRouteToVueRoute(item: AuthRoute.Route) {
consoleError('路由组件解析失败: ', item);
}
}
if (hasProps(item) && !isSingleRoute(item)) {
(itemRoute as any).props = item.props;
}
if (isSingleRoute(item)) {
itemRoute.children = [
{
path: '',
name: item.name,
component: getViewComponent(item.name)
component: getViewComponent(item.name),
props: hasProps(item) ? item.props : undefined
}
];
} else if (hasChildren(item)) {
@ -67,6 +73,10 @@ function hasChildren(item: AuthRoute.Route) {
return Boolean(item.children && item.children.length);
}
function hasProps(item: AuthRoute.Route) {
return Boolean(item.props);
}
function isSingleRoute(item: AuthRoute.Route) {
return Boolean(item.meta.single);
}

View File

@ -0,0 +1,61 @@
<template>
<n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
<n-form-item path="phone">
<n-input v-model:value="model.phone" placeholder="手机号码" />
</n-form-item>
<n-form-item path="code">
<div class="flex-y-center w-full">
<n-input v-model:value="model.code" placeholder="验证码" />
<div class="w-18px"></div>
<n-button size="large" :disabled="isCounting" :loading="smsLoading" @click="handleSmsCode">
{{ label }}
</n-button>
</div>
</n-form-item>
<n-space :vertical="true" size="large">
<n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">确定</n-button>
<n-button size="large" :block="true" :round="true" @click="toLoginModule('pwd-login')">返回</n-button>
</n-space>
</n-form>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { NForm, NFormItem, NInput, NSpace, NButton } from 'naive-ui';
import type { FormInst } from 'naive-ui';
import { useRouterPush } from '@/composables';
import { useSmsCode } from '@/hooks';
import { formRules } from '@/utils';
const { toLoginModule } = useRouterPush();
const { label, isCounting, loading: smsLoading, getSmsCode } = useSmsCode();
const formRef = ref<(HTMLElement & FormInst) | null>(null);
const model = reactive({
phone: '',
code: '',
imgCode: ''
});
const rules = {
phone: formRules.phone,
code: formRules.code
};
function handleSmsCode() {
getSmsCode(model.phone);
}
function handleSubmit(e: MouseEvent) {
if (!formRef.value) return;
e.preventDefault();
formRef.value.validate(errors => {
if (!errors) {
window.$message?.success('验证成功');
} else {
window.$message?.error('验证失败');
}
});
}
</script>
<style scoped></style>

View File

@ -0,0 +1,81 @@
<template>
<n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
<n-form-item path="phone">
<n-input v-model:value="model.phone" placeholder="手机号码" />
</n-form-item>
<n-form-item path="code">
<div class="flex-y-center w-full">
<n-input v-model:value="model.code" placeholder="验证码" />
<div class="w-18px"></div>
<n-button size="large" :disabled="isCounting" :loading="smsLoading" @click="handleSmsCode">
{{ label }}
</n-button>
</div>
</n-form-item>
<n-form-item path="imgCode">
<n-input v-model:value="model.imgCode" placeholder="验证码,点击图片刷新" />
<div class="pl-8px">
<image-verify v-model:code="imgCode" />
</div>
</n-form-item>
<n-space :vertical="true" size="large">
<n-button
type="primary"
size="large"
:block="true"
:round="true"
:loading="auth.loginLoding"
@click="handleSubmit"
>
确定
</n-button>
<n-button size="large" :block="true" :round="true" @click="toLoginModule('pwd-login')">返回</n-button>
</n-space>
</n-form>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { NForm, NFormItem, NInput, NSpace, NButton } from 'naive-ui';
import type { FormInst } from 'naive-ui';
import { ImageVerify } from '@/components';
import { useAuthStore } from '@/store';
import { useRouterPush } from '@/composables';
import { useSmsCode } from '@/hooks';
import { formRules, getImgCodeRule } from '@/utils';
const auth = useAuthStore();
const { login } = useAuthStore();
const { toLoginModule } = useRouterPush();
const { label, isCounting, loading: smsLoading, getSmsCode } = useSmsCode();
const formRef = ref<(HTMLElement & FormInst) | null>(null);
const model = reactive({
phone: '',
code: '',
imgCode: ''
});
const imgCode = ref('');
const rules = {
phone: formRules.phone,
code: formRules.code,
imgCode: getImgCodeRule(imgCode)
};
function handleSmsCode() {
getSmsCode(model.phone);
}
function handleSubmit(e: MouseEvent) {
if (!formRef.value) return;
e.preventDefault();
formRef.value.validate(errors => {
if (!errors) {
const { phone, code } = model;
login(phone, code, 'sms');
}
});
}
</script>
<style scoped></style>

View File

@ -1,5 +1,5 @@
<template>
<div class="absolute-lt wh-full overflow-hidden">
<div class="absolute-lt z-1 wh-full overflow-hidden">
<div class="absolute -right-300px -top-900px">
<corner-top :start-color="lightColor" :end-color="darkColor" />
</div>

View File

@ -0,0 +1,15 @@
<template>
<n-space :vertical="true">
<n-divider class="!mb-0 text-14px text-[#666]">其他登录方式</n-divider>
<div class="flex-center">
<n-button :text="true">
<icon-mdi-wechat class="text-22px text-[#888] hover:text-[#52BF5E]" />
</n-button>
</div>
</n-space>
</template>
<script lang="ts" setup>
import { NSpace, NDivider, NButton } from 'naive-ui';
</script>
<style scoped></style>

View File

@ -0,0 +1,3 @@
import OtherLogin from './OtherLogin.vue';
export { OtherLogin };

View File

@ -0,0 +1,75 @@
<template>
<n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
<n-form-item path="phone">
<n-input v-model:value="model.phone" placeholder="请输入手机号码" />
</n-form-item>
<n-form-item path="pwd">
<n-input v-model:value="model.pwd" type="password" show-password-on="click" placeholder="请输入密码" />
</n-form-item>
<n-space :vertical="true" :size="24">
<div class="flex-y-center justify-between">
<n-checkbox v-model:checked="rememberMe">记住我</n-checkbox>
<n-button :text="true">忘记密码</n-button>
</div>
<n-button
type="primary"
size="large"
:block="true"
:round="true"
:loading="auth.loginLoding"
@click="handleSubmit"
>
确定
</n-button>
<div class="flex-y-center justify-between">
<n-button class="flex-1" :block="true" @click="toLoginModule('code-login')">
{{ EnumLoginModule['code-login'] }}
</n-button>
<div class="w-12px"></div>
<n-button class="flex-1" :block="true" @click="toLoginModule('register')">
{{ EnumLoginModule.register }}
</n-button>
</div>
</n-space>
<other-login />
</n-form>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { NForm, NFormItem, NInput, NSpace, NCheckbox, NButton } from 'naive-ui';
import type { FormInst, FormRules } from 'naive-ui';
import { EnumLoginModule } from '@/enum';
import { useAuthStore } from '@/store';
import { useRouterPush } from '@/composables';
import { formRules } from '@/utils';
import { OtherLogin } from './components';
const auth = useAuthStore();
const { login } = useAuthStore();
const { toLoginModule } = useRouterPush();
const formRef = ref<(HTMLElement & FormInst) | null>(null);
const model = reactive({
phone: '',
pwd: ''
});
const rules: FormRules = {
phone: formRules.phone,
pwd: formRules.pwd
};
const rememberMe = ref(false);
function handleSubmit(e: MouseEvent) {
if (!formRef.value) return;
e.preventDefault();
formRef.value.validate(errors => {
if (!errors) {
const { phone, pwd } = model;
login(phone, pwd, 'pwd');
}
});
}
</script>
<style scoped></style>

View File

@ -0,0 +1,75 @@
<template>
<n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
<n-form-item path="phone">
<n-input v-model:value="model.phone" placeholder="手机号码" />
</n-form-item>
<n-form-item path="code">
<div class="flex-y-center w-full">
<n-input v-model:value="model.code" placeholder="验证码" />
<div class="w-18px"></div>
<n-button size="large" :disabled="isCounting" :loading="smsLoading" @click="handleSmsCode">
{{ label }}
</n-button>
</div>
</n-form-item>
<n-form-item path="pwd">
<n-input v-model:value="model.pwd" placeholder="密码" />
</n-form-item>
<n-form-item path="confirmPwd">
<n-input v-model:value="model.confirmPwd" placeholder="确认密码" />
</n-form-item>
<n-space :vertical="true" size="large">
<login-agreement v-model:value="agreement" />
<n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">确定</n-button>
<n-button size="large" :block="true" :round="true" @click="toLoginModule('pwd-login')">返回</n-button>
</n-space>
</n-form>
</template>
<script lang="ts" setup>
import { reactive, ref, toRefs } from 'vue';
import { NForm, NFormItem, NInput, NSpace, NButton } from 'naive-ui';
import type { FormInst, FormRules } from 'naive-ui';
import { LoginAgreement } from '@/components';
import { useRouterPush } from '@/composables';
import { useSmsCode } from '@/hooks';
import { formRules, getConfirmPwdRule } from '@/utils';
const { toLoginModule } = useRouterPush();
const { label, isCounting, loading: smsLoading, start } = useSmsCode();
const formRef = ref<(HTMLElement & FormInst) | null>(null);
const model = reactive({
phone: '',
code: '',
pwd: '',
confirmPwd: ''
});
const rules: FormRules = {
phone: formRules.phone,
code: formRules.code,
pwd: formRules.pwd,
confirmPwd: getConfirmPwdRule(toRefs(model).pwd)
};
const agreement = ref(false);
function handleSmsCode() {
start();
}
function handleSubmit(e: MouseEvent) {
if (!formRef.value) return;
e.preventDefault();
formRef.value.validate(errors => {
if (!errors) {
if (!agreement.value) return;
window.$message?.success('验证成功');
} else {
window.$message?.error('验证失败');
}
});
}
</script>
<style scoped></style>

View File

@ -0,0 +1,71 @@
<template>
<n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
<n-form-item path="phone">
<n-input v-model:value="model.phone" placeholder="手机号码" />
</n-form-item>
<n-form-item path="code">
<div class="flex-y-center w-full">
<n-input v-model:value="model.code" placeholder="验证码" />
<div class="w-18px"></div>
<n-button size="large" :disabled="isCounting" :loading="smsLoading" @click="handleSmsCode">
{{ label }}
</n-button>
</div>
</n-form-item>
<n-form-item path="pwd">
<n-input v-model:value="model.pwd" placeholder="密码" />
</n-form-item>
<n-form-item path="confirmPwd">
<n-input v-model:value="model.confirmPwd" placeholder="确认密码" />
</n-form-item>
<n-space :vertical="true" size="large">
<n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">确定</n-button>
<n-button size="large" :block="true" :round="true" @click="toLoginModule('pwd-login')">返回</n-button>
</n-space>
</n-form>
</template>
<script lang="ts" setup>
import { reactive, ref, toRefs } from 'vue';
import { NForm, NFormItem, NInput, NSpace, NButton, useMessage } from 'naive-ui';
import type { FormInst, FormRules } from 'naive-ui';
import { useRouterPush } from '@/composables';
import { useSmsCode } from '@/hooks';
import { formRules, getConfirmPwdRule } from '@/utils';
const message = useMessage();
const { toLoginModule } = useRouterPush();
const { label, isCounting, loading: smsLoading, start } = useSmsCode();
const formRef = ref<(HTMLElement & FormInst) | null>(null);
const model = reactive({
phone: '',
code: '',
pwd: '',
confirmPwd: ''
});
const rules: FormRules = {
phone: formRules.phone,
code: formRules.code,
pwd: formRules.pwd,
confirmPwd: getConfirmPwdRule(toRefs(model).pwd)
};
function handleSmsCode() {
start();
}
function handleSubmit(e: MouseEvent) {
if (!formRef.value) return;
e.preventDefault();
formRef.value.validate(errors => {
if (!errors) {
message.success('验证成功');
} else {
message.error('验证失败');
}
});
}
</script>
<style scoped></style>

View File

@ -1,3 +1,8 @@
import LoginBg from './LoginBg/index.vue';
import PwdLogin from './PwdLogin/index.vue';
import CodeLogin from './CodeLogin/index.vue';
import Register from './Register/index.vue';
import ResetPwd from './ResetPwd/index.vue';
import BindWechat from './BindWechat/index.vue';
export { LoginBg };
export { LoginBg, PwdLogin, CodeLogin, Register, ResetPwd, BindWechat };

View File

@ -1,6 +1,11 @@
<template>
<div class="relative flex-center wh-full" :style="{ backgroundColor: bgColor }">
<n-card :bordered="false" size="large" class="z-20 !w-auto rounded-20px shadow-sm">
<dark-mode-switch
:dark="theme.darkMode"
class="absolute left-48px top-24px z-3 text-20px"
@update:dark="setDarkMode"
/>
<n-card :bordered="false" size="large" class="z-4 !w-auto rounded-20px shadow-sm">
<div class="w-360px">
<header class="flex-y-center justify-between">
<div class="w-70px h-70px rounded-35px overflow-hidden">
@ -9,30 +14,70 @@
<n-gradient-text type="primary" :size="28">{{ title }}</n-gradient-text>
</header>
<main class="pt-24px">
<h3 class="text-18px text-primary font-medium">登录</h3>
<h3 class="text-18px text-primary font-medium">{{ activeModule.label }}</h3>
<div class="pt-24px">
<transition>
<component :is="activeModule.component" />
</transition>
</div>
</main>
</div>
</n-card>
<login-bg :theme-color="theme.themeColor" />
<login-bg :theme-color="bgThemeColor" />
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import type { Component } from 'vue';
import { NCard, NGradientText } from 'naive-ui';
import { SystemLogo } from '@/components';
import { EnumLoginModule } from '@/enum';
import { SystemLogo, DarkModeSwitch } from '@/components';
import { useThemeStore } from '@/store';
import { useAppInfo } from '@/composables';
import { mixColor } from '@/utils';
import { LoginBg } from './components';
import { getColorPalette, mixColor } from '@/utils';
import type { LoginModuleKey } from '@/interface';
import { LoginBg, PwdLogin, CodeLogin, Register, ResetPwd, BindWechat } from './components';
interface Props {
/** 登录模块分类 */
module: LoginModuleKey;
}
interface LoginModule {
key: LoginModuleKey;
label: EnumLoginModule;
component: Component;
}
const props = defineProps<Props>();
const theme = useThemeStore();
const { setDarkMode } = useThemeStore();
const { title } = useAppInfo();
const modules: LoginModule[] = [
{ key: 'pwd-login', label: EnumLoginModule['pwd-login'], component: PwdLogin },
{ key: 'code-login', label: EnumLoginModule['code-login'], component: CodeLogin },
{ key: 'register', label: EnumLoginModule.register, component: Register },
{ key: 'reset-pwd', label: EnumLoginModule['reset-pwd'], component: ResetPwd },
{ key: 'bind-wechat', label: EnumLoginModule['bind-wechat'], component: BindWechat }
];
const activeModule = computed(() => {
const active: LoginModule = { ...modules[0] };
const findItem = modules.find(item => item.key === props.module);
if (findItem) {
Object.assign(active, findItem);
}
return active;
});
const bgThemeColor = computed(() => (theme.darkMode ? getColorPalette(theme.themeColor, 7) : theme.themeColor));
const bgColor = computed(() => {
const COLOR_WHITE = '#ffffff';
const darkMode = false;
const ratio = darkMode ? 0.6 : 0.2;
const ratio = theme.darkMode ? 0.5 : 0.2;
return mixColor(COLOR_WHITE, theme.themeColor, ratio);
});
</script>