feat: 优化登录页面布局

This commit is contained in:
duanshuwen
2025-09-23 20:50:43 +08:00
parent 089a86c66b
commit d9b59db465
5 changed files with 938 additions and 301 deletions

View File

@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta <meta
http-equiv="Content-Security-Policy" http-equiv="Content-Security-Policy"
content="default-src 'self' 'unsafe-inline';" content="default-src *;img-src 'self' data: base64; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'"
/> />
<title>Vite + Vue</title> <title>Vite + Vue</title>
</head> </head>

709
package-lock.json generated
View File

@@ -13,6 +13,7 @@
"pinia": "^3.0.3", "pinia": "^3.0.3",
"tailwindcss": "^4.1.13", "tailwindcss": "^4.1.13",
"vue": "^3.5.21", "vue": "^3.5.21",
"vue-qrcode": "^2.2.2",
"vue-router": "^4.5.1" "vue-router": "^4.5.1"
}, },
"devDependencies": { "devDependencies": {
@@ -24,6 +25,7 @@
"nodemon": "^3.1.10", "nodemon": "^3.1.10",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"sass": "^1.93.1",
"vite": "^7.1.6" "vite": "^7.1.6"
} }
}, },
@@ -1678,6 +1680,338 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
} }
}, },
"node_modules/@parcel/watcher": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.1",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-freebsd-x64": "2.5.1",
"@parcel/watcher-linux-arm-glibc": "2.5.1",
"@parcel/watcher-linux-arm-musl": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-ia32": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher/node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
"license": "Apache-2.0",
"optional": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/@parcel/watcher/node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/@pkgjs/parseargs": { "node_modules/@pkgjs/parseargs": {
"version": "0.11.0", "version": "0.11.0",
"resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -2853,7 +3187,6 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@@ -3772,6 +4105,16 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6"
}
},
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001743", "version": "1.0.30001743",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz",
@@ -4247,6 +4590,16 @@
} }
} }
}, },
"node_modules/decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/decompress-response": { "node_modules/decompress-response": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz", "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz",
@@ -4363,6 +4716,13 @@
"license": "MIT", "license": "MIT",
"optional": true "optional": true
}, },
"node_modules/dijkstrajs": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
"license": "MIT",
"peer": true
},
"node_modules/dir-compare": { "node_modules/dir-compare": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmmirror.com/dir-compare/-/dir-compare-4.2.0.tgz", "resolved": "https://registry.npmmirror.com/dir-compare/-/dir-compare-4.2.0.tgz",
@@ -4902,7 +5262,6 @@
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/encoding": { "node_modules/encoding": {
@@ -5328,6 +5687,20 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"license": "MIT",
"peer": true,
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/for-each": { "node_modules/for-each": {
"version": "0.3.5", "version": "0.3.5",
"resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz", "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz",
@@ -5564,7 +5937,6 @@
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC", "license": "ISC",
"engines": { "engines": {
"node": "6.* || 8.* || >= 10.*" "node": "6.* || 8.* || >= 10.*"
@@ -5999,6 +6371,13 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/immutable": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz",
"integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==",
"dev": true,
"license": "MIT"
},
"node_modules/imurmurhash": { "node_modules/imurmurhash": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz",
@@ -6268,7 +6647,6 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@@ -6954,6 +7332,19 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"license": "MIT",
"peer": true,
"dependencies": {
"p-locate": "^4.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
@@ -7208,6 +7599,35 @@
"node": ">= 0.10.0" "node": ">= 0.10.0"
} }
}, },
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/micromatch/node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/mime": { "node_modules/mime": {
"version": "2.6.0", "version": "2.6.0",
"resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz", "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz",
@@ -7869,6 +8289,35 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"license": "MIT",
"peer": true,
"dependencies": {
"p-limit": "^2.2.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/p-locate/node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"license": "MIT",
"peer": true,
"dependencies": {
"p-try": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-map": { "node_modules/p-map": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz",
@@ -7885,6 +8334,16 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6"
}
},
"node_modules/package-json-from-dist": { "node_modules/package-json-from-dist": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
@@ -7906,6 +8365,16 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=8"
}
},
"node_modules/path-is-absolute": { "node_modules/path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -8086,6 +8555,16 @@
"node": ">=10.4.0" "node": ">=10.4.0"
} }
}, },
"node_modules/pngjs": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/possible-typed-array-names": { "node_modules/possible-typed-array-names": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
@@ -8230,6 +8709,131 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/qrcode": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
"license": "MIT",
"peer": true,
"dependencies": {
"dijkstrajs": "^1.0.1",
"pngjs": "^5.0.0",
"yargs": "^15.3.1"
},
"bin": {
"qrcode": "bin/qrcode"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/qrcode/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"peer": true,
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/qrcode/node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"license": "ISC",
"peer": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"node_modules/qrcode/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/qrcode/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT",
"peer": true
},
"node_modules/qrcode/node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"license": "MIT",
"peer": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"license": "ISC",
"peer": true
},
"node_modules/qrcode/node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"license": "MIT",
"peer": true,
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"license": "ISC",
"peer": true,
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/quick-lru": { "node_modules/quick-lru": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-5.1.1.tgz", "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-5.1.1.tgz",
@@ -8360,12 +8964,18 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"license": "ISC",
"peer": true
},
"node_modules/resedit": { "node_modules/resedit": {
"version": "1.7.2", "version": "1.7.2",
"resolved": "https://registry.npmmirror.com/resedit/-/resedit-1.7.2.tgz", "resolved": "https://registry.npmmirror.com/resedit/-/resedit-1.7.2.tgz",
@@ -8626,6 +9236,57 @@
"truncate-utf8-bytes": "^1.0.0" "truncate-utf8-bytes": "^1.0.0"
} }
}, },
"node_modules/sass": {
"version": "1.93.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.93.1.tgz",
"integrity": "sha512-wLAeLB7IksO2u+cCfhHqcy7/2ZUMPp/X2oV6+LjmweTqgjhOKrkaE/Q1wljxtco5EcOcupZ4c981X0gpk5Tiag==",
"dev": true,
"license": "MIT",
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.0.2",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/sass/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/sass/node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/sax": { "node_modules/sax": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz", "resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz",
@@ -8668,6 +9329,13 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"license": "ISC",
"peer": true
},
"node_modules/set-function-length": { "node_modules/set-function-length": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -9105,7 +9773,6 @@
"version": "4.2.3", "version": "4.2.3",
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"emoji-regex": "^8.0.0", "emoji-regex": "^8.0.0",
@@ -9214,7 +9881,6 @@
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ansi-regex": "^5.0.1" "ansi-regex": "^5.0.1"
@@ -9529,6 +10195,12 @@
"utf8-byte-length": "^1.0.1" "utf8-byte-length": "^1.0.1"
} }
}, },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/type-fest": { "node_modules/type-fest": {
"version": "0.13.1", "version": "0.13.1",
"resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.13.1.tgz", "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.13.1.tgz",
@@ -9882,6 +10554,22 @@
} }
} }
}, },
"node_modules/vue-qrcode": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/vue-qrcode/-/vue-qrcode-2.2.2.tgz",
"integrity": "sha512-SbrXq/mSb1g2tbDyXPe9gy9KiMYsvxWKRErlpij1BqiFoHwQckheZV63CTw6yRLLUVG2RXAVlX+APkpdCK7SQQ==",
"license": "MIT",
"dependencies": {
"tslib": "^2.6.2"
},
"funding": {
"url": "https://opencollective.com/rxts"
},
"peerDependencies": {
"qrcode": "^1.0.0",
"vue": "^2.7.0 || ^3.0.0"
}
},
"node_modules/vue-router": { "node_modules/vue-router": {
"version": "4.5.1", "version": "4.5.1",
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz", "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz",
@@ -9993,6 +10681,13 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/which-module": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
"license": "ISC",
"peer": true
},
"node_modules/which-typed-array": { "node_modules/which-typed-array": {
"version": "1.1.19", "version": "1.1.19",
"resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.19.tgz", "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.19.tgz",

View File

@@ -50,6 +50,7 @@
"pinia": "^3.0.3", "pinia": "^3.0.3",
"tailwindcss": "^4.1.13", "tailwindcss": "^4.1.13",
"vue": "^3.5.21", "vue": "^3.5.21",
"vue-qrcode": "^2.2.2",
"vue-router": "^4.5.1" "vue-router": "^4.5.1"
}, },
"devDependencies": { "devDependencies": {
@@ -61,6 +62,7 @@
"nodemon": "^3.1.10", "nodemon": "^3.1.10",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"sass": "^1.93.1",
"vite": "^7.1.6" "vite": "^7.1.6"
} }
} }

View File

@@ -23,3 +23,15 @@ body {
width: 100%; width: 100%;
min-height: 100vh; min-height: 100vh;
} }
.fz-14 {
font-size: 14px;
}
.fz-22 {
font-size: 22px;
}
.mt-20 {
margin-top: 20px;
}

View File

@@ -1,363 +1,291 @@
<template> <template>
<div <div
class="min-h-screen bg-gray-50 flex items-center justify-center p-4" class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4"
> >
<div class="w-full max-w-md"> <div class="w-full max-w-md">
<!-- 登录卡片 --> <!-- 登录卡片 -->
<div class="bg-white rounded-lg shadow-sm border p-8 w-full max-w-md"> <div class="bg-white rounded-lg shadow-xl p-8">
<!-- 头部区域 --> <!-- Logo和标题 -->
<div class="text-center mb-8"> <div class="text-center mb-8">
<!-- Logo --> <h1 class="text-2xl font-bold text-gray-900 mb-2">
<div class="mb-6"> China Fellou Plus
<div class="w-16 h-16 bg-blue-600 rounded-xl flex items-center justify-center mx-auto"> </h1>
<span class="text-white font-bold text-2xl">F</span> <div class="text-sm text-gray-600">
</div> 您的隐私对我们很重要我们确保您的数据是安全和保密的
</div> </div>
<!-- 标题 -->
<h1 class="text-2xl font-bold text-gray-900 mb-2">欢迎回来</h1>
<p class="text-gray-600">登录您的账户</p>
</div> </div>
<!-- 登录方式切换 --> <div class="login flex items-start justify-center">
<div class="flex mb-8 bg-gray-50 rounded-lg p-1"> <!-- 二维码登录 -->
<button <div class="left text-center">
@click="loginType = 'code'" <div class="bg-gray-100 rounded-lg p-8 mb-4">
:class="[ <div class="fz-22">手机扫码登录</div>
'flex-1 py-2 px-4 rounded-md text-sm font-medium transition-all', <div
loginType === 'code' class="w-48 h-48 bg-white rounded-lg mx-auto flex items-center justify-center"
? 'bg-white text-blue-600 shadow-sm' >
: 'text-gray-600 hover:text-gray-700' <div class="qr text-center mt-20">
]" <vue-qrcode
> value="https://www.1stg.me"
验证码登录 width="195"
</button> margin="3"
<button />
@click="loginType = 'password'"
:class="[
'flex-1 py-2 px-4 rounded-md text-sm font-medium transition-all',
loginType === 'password'
? 'bg-white text-blue-600 shadow-sm'
: 'text-gray-600 hover:text-gray-700'
]"
>
密码登录
</button>
</div>
<!-- 登录表单 --> <!-- 二维码失效 -->
<el-form <div class="qrcode-error">
ref="loginFormRef" <p>二维码已失效</p>
:model="loginForm" <el-button type="info" round @click="refreshQrCode"
:rules="loginRules" >刷新</el-button
@submit.prevent="handleLogin" >
class="space-y-3" </div>
> </div>
<!-- 邮箱输入框 --> </div>
<el-form-item prop="email"> </div>
<el-input <p class="fz-14">
v-model="loginForm.email" 打开<el-text type="success">微信</el-text>扫一扫登录
type="email" </p>
placeholder="请输入邮箱" </div>
size="default"
class="w-full"
:prefix-icon="User"
@blur="validateField('email')"
/>
</el-form-item>
<!-- 验证码登录 --> <!-- 账号密码登录 -->
<div v-if="loginType === 'code'"> <div class="right text-center">
<el-form-item prop="verifyCode"> <div class="fz-22">密码登录</div>
<div class="flex gap-2">
<el-form
class="mt-20"
ref="loginFormRef"
:model="loginForm"
:rules="loginRules"
@keyup.enter="handleLogin"
>
<!-- 用户名/邮箱/手机号 -->
<el-form-item prop="username">
<el-input <el-input
v-model="loginForm.verifyCode" v-model="loginForm.username"
placeholder="请输入验证码" placeholder="请输入手机号"
size="default" size="large"
class="flex-1" clearable
:prefix-icon="Timer"
maxlength="4"
@blur="validateField('verifyCode')"
/>
<el-button
type="primary"
size="default"
:disabled="!isEmailValid || countdown > 0"
@click="sendVerifyCode"
class="min-w-[80px]"
> >
{{ countdown > 0 ? `${countdown}s` : "获取" }} <template #prefix>
<el-icon><User /></el-icon>
</template>
</el-input>
</el-form-item>
<!-- 密码 -->
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
placeholder="请输入密码"
size="large"
show-password
clearable
>
<template #prefix>
<el-icon><Lock /></el-icon>
</template>
</el-input>
</el-form-item>
<!-- 图形验证码 -->
<el-form-item v-if="showCaptcha" prop="captcha">
<div class="flex space-x-2">
<el-input
v-model="loginForm.captcha"
placeholder="请输入验证码"
size="large"
clearable
>
<template #prefix>
<el-icon><Key /></el-icon>
</template>
</el-input>
<div
class="w-24 h-10 bg-gray-200 rounded flex items-center justify-center cursor-pointer"
@click="refreshCaptcha"
>
<span class="text-sm font-mono">{{ captchaCode }}</span>
</div>
</div>
</el-form-item>
<!-- 记住我和忘记密码 -->
<div class="flex items-center justify-between">
<el-checkbox v-model="loginForm.remember"> 记住我 </el-checkbox>
<el-button
type="text"
size="small"
@click="handleForgotPassword"
>
忘记密码
</el-button> </el-button>
</div> </div>
</el-form-item>
</div>
<!-- 密码登录 --> <!-- 登录按钮 -->
<div v-if="loginType === 'password'"> <el-form-item>
<el-form-item prop="password"> <el-button
<el-input type="primary"
v-model="loginForm.password" size="large"
:type="showPassword ? 'text' : 'password'" class="w-full"
placeholder="请输入密码" :loading="loading"
size="default" @click="handleLogin"
class="w-full" >
:prefix-icon="Lock" 登录
@blur="validateField('password')" </el-button>
> </el-form-item>
<template #suffix> </el-form>
<el-icon
@click="showPassword = !showPassword"
class="cursor-pointer text-gray-400 hover:text-gray-600"
>
<View v-if="showPassword" />
<Hide v-else />
</el-icon>
</template>
</el-input>
</el-form-item>
<!-- 忘记密码 --> <!-- 用户协议 -->
<div class="text-right mb-2"> <div class="text-center fz-14">
<el-link 登录即表示您同意
type="primary" <el-link type="primary" :underline="false">用户协议</el-link>
:underline="false"
@click="handleForgotPassword" <el-link type="primary" :underline="false">隐私政策</el-link>
class="text-xs"
>
忘记密码
</el-link>
</div> </div>
</div> </div>
</div>
<!-- 协议同意 -->
<el-form-item prop="agreed">
<el-checkbox
v-model="loginForm.agreed"
@change="validateField('agreed')"
class="text-xs"
>
<span class="text-gray-600">
我已阅读并同意
<el-link
type="primary"
:underline="false"
@click="handlePrivacyPolicy"
class="text-xs"
>隐私政策</el-link
>
<el-link
type="primary"
:underline="false"
@click="handleServiceTerms"
class="text-xs"
>服务条款</el-link
>
</span>
</el-checkbox>
</el-form-item>
<!-- 登录按钮 -->
<el-form-item>
<el-button
type="primary"
size="default"
:loading="loading"
@click="handleLogin"
class="w-full"
>
{{ loading ? "登录中..." : "登录" }}
</el-button>
</el-form-item>
</el-form>
</div> </div>
<!-- Copyright -->
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, reactive, computed, onMounted } from "vue"; import { ref, reactive, onMounted } from "vue";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { User, Lock, Timer, View, Hide } from "@element-plus/icons-vue"; import { User, Lock, Key } from "@element-plus/icons-vue";
import VueQrcode from "vue-qrcode";
// 登录类型 // 登录表单
const loginType = ref("code");
// 表单引用
const loginFormRef = ref(); const loginFormRef = ref();
// 表单数据
const loginForm = reactive({ const loginForm = reactive({
email: "", username: "",
password: "", password: "",
verifyCode: "", captcha: "",
agreed: false, remember: false,
}); });
// 表单验证规则 // 登录规则(必填验证)
const loginRules = reactive({ const loginRules = {
email: [ username: [
{ required: true, message: "请输入邮箱", trigger: "blur" }, { required: true, message: "请输入用户名/邮箱/手机号", trigger: "blur" },
{ type: "email", message: "请输入正确的邮箱格式", trigger: "blur" },
], ],
password: [ password: [
{ required: true, message: "请输入密码", trigger: "blur" }, { required: true, message: "请输入密码", trigger: "blur" },
{ min: 6, message: "密码长度不能少于6位", trigger: "blur" }, { min: 6, message: "密码长度不能少于6位", trigger: "blur" },
], ],
verifyCode: [ captcha: [{ required: true, message: "请输入验证码", trigger: "blur" }],
{ required: true, message: "请输入验证码", trigger: "blur" }, };
{ len: 4, message: "验证码必须为4位", trigger: "blur" },
],
agreed: [
{
validator: (rule, value, callback) => {
if (!value) {
callback(new Error("请同意隐私政策和服务条款"));
} else {
callback();
}
},
trigger: "change",
},
],
});
// 状态管理 // 加载状态
const loading = ref(false); const loading = ref(false);
const showPassword = ref(false);
const countdown = ref(0);
// 计算属性 // 验证码相关
const isEmailValid = computed(() => { const showCaptcha = ref(false);
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const captchaCode = ref("8K9M");
return emailRegex.test(loginForm.email); const loginAttempts = ref(0);
});
// 验证码倒计时 // 生成二维码
const startCountdown = () => { const qrCodeUrl = ref("");
countdown.value = 60;
const timer = setInterval(() => {
countdown.value--;
if (countdown.value <= 0) {
clearInterval(timer);
}
}, 1000);
};
// 发送验证码 // 刷新验证码
const sendVerifyCode = async () => { const refreshCaptcha = () => {
if (!isEmailValid.value) { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
ElMessage.error("请先输入正确的邮箱地址"); let code = "";
return; for (let i = 0; i < 4; i++) {
code += chars.charAt(Math.floor(Math.random() * chars.length));
} }
captchaCode.value = code;
try {
// 这里调用发送验证码的API
ElMessage.success("验证码已发送到您的邮箱");
startCountdown();
} catch (error) {
ElMessage.error("验证码发送失败,请重试");
}
};
// 验证单个字段
const validateField = (field) => {
loginFormRef.value?.validateField(field);
}; };
// 处理登录 // 处理登录
const handleLogin = async () => { const handleLogin = async () => {
if (!loginFormRef.value) return;
try { try {
const valid = await loginFormRef.value.validate(); await loginFormRef.value.validate();
if (!valid) return;
loading.value = true; loading.value = true;
// 根据登录类型构建请求数据
const loginData = {
email: loginForm.email,
agreed: loginForm.agreed,
};
if (loginType.value === "password") {
loginData.password = loginForm.password;
} else {
loginData.verifyCode = loginForm.verifyCode;
}
// 这里调用登录API
console.log("登录数据:", loginData);
// 模拟登录请求 // 模拟登录请求
await new Promise((resolve) => setTimeout(resolve, 2000)); setTimeout(() => {
loading.value = false;
loginAttempts.value++;
ElMessage.success("登录成功"); // 模拟登录失败,显示验证码
if (loginAttempts.value >= 1) {
// 登录成功后的跳转逻辑 showCaptcha.value = true;
// router.push('/home') ElMessage.error("用户名或密码错误");
refreshCaptcha();
} else {
ElMessage.success("登录成功");
}
}, 1500);
} catch (error) { } catch (error) {
console.error("登录失败:", error); console.error("验证失败:", error);
ElMessage.error("登录失败,请重试");
} finally {
loading.value = false;
} }
}; };
// 忘记密码 // 处理忘记密码
const handleForgotPassword = () => { const handleForgotPassword = () => {
ElMessage.info("忘记密码功能开发中"); ElMessage.info("忘记密码功能开发中...");
}; };
// 隐私政策 // 页面加载时聚焦到用户名输入框
const handlePrivacyPolicy = () => {
ElMessage.info("隐私政策页面开发中");
};
// 服务条款
const handleServiceTerms = () => {
ElMessage.info("服务条款页面开发中");
};
// 页面加载时自动聚焦到邮箱输入框
onMounted(() => { onMounted(() => {
// 可以在这里添加自动聚焦逻辑 const usernameInput = document.querySelector(
'input[placeholder="请输入用户名/邮箱/手机号"]'
);
if (usernameInput) {
usernameInput.focus();
}
}); });
</script> </script>
<style scoped> <style scoped lang="scss">
/* Element Plus 样式覆盖 */ .login {
:deep(.el-input__wrapper) { margin: 40px auto;
}
.left {
padding-right: 60px;
border-right: 1px solid #f2f2f2;
}
.right {
padding-left: 60px;
}
.qr {
width: 195px;
height: 195px;
border: 1px solid #e5e8ec;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 0 0 1px #e5e7eb; box-shadow: none;
transition: all 0.2s; background-color: #fff;
position: relative;
} }
:deep(.el-input__wrapper:hover) { .qrcode-error {
box-shadow: 0 0 0 1px #d1d5db; margin: 14px;
} width: 167px;
height: 167px;
background: hsla(0, 0%, 100%, 0.95);
font-family: PingFang SC;
font-weight: 400;
letter-spacing: 0;
position: absolute;
left: 0;
top: 0;
z-index: 1001;
:deep(.el-input.is-focus .el-input__wrapper) { p {
box-shadow: 0 0 0 2px #3b82f6; font-family: PingFang SC;
} font-size: 16px;
font-weight: 500;
:deep(.el-button--primary) { line-height: 16px;
border-radius: 8px; letter-spacing: 0;
font-weight: 500; color: #111;
} margin-top: 38px;
margin-bottom: 20px;
:deep(.el-checkbox__label) { }
font-size: 14px;
}
/* 自定义渐变按钮样式 */
.el-button--primary.bg-gradient-to-r {
background: linear-gradient(to right, #9333ea, #2563eb) !important;
border: none !important;
}
.el-button--primary.bg-gradient-to-r:hover {
background: linear-gradient(to right, #7c3aed, #1d4ed8) !important;
} }
</style> </style>