优化玩家服务中心登录页

This commit is contained in:
Stev_Wang
2026-01-05 16:59:39 +08:00
parent 468d24c3bb
commit 7b8e6c7f7e
5 changed files with 882 additions and 64 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -9,6 +9,8 @@
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@remixicon/vue": "^4.8.0", "@remixicon/vue": "^4.8.0",
"@vicons/ionicons5": "^0.13.0",
"animate.css": "^4.1.1",
"axios": "^1.13.2", "axios": "^1.13.2",
"naive-ui": "^2.43.2", "naive-ui": "^2.43.2",
"pinia": "^3.0.4", "pinia": "^3.0.4",
@@ -19,6 +21,7 @@
"@types/node": "^24.10.1", "@types/node": "^24.10.1",
"@vitejs/plugin-vue": "^6.0.1", "@vitejs/plugin-vue": "^6.0.1",
"@vue/tsconfig": "^0.8.1", "@vue/tsconfig": "^0.8.1",
"less": "^4.5.1",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"vite": "^7.2.4", "vite": "^7.2.4",
"vue-tsc": "^3.1.4" "vue-tsc": "^3.1.4"
@@ -910,6 +913,12 @@
"undici-types": "~7.16.0" "undici-types": "~7.16.0"
} }
}, },
"node_modules/@vicons/ionicons5": {
"version": "0.13.0",
"resolved": "https://registry.npmmirror.com/@vicons/ionicons5/-/ionicons5-0.13.0.tgz",
"integrity": "sha512-zvZKBPjEXKN7AXNo2Na2uy+nvuv6SP4KAMQxpKL2vfHMj0fSvuw7JZcOPCjQC3e7ayssKnaoFVAhbYcW6v41qQ==",
"license": "MIT"
},
"node_modules/@vitejs/plugin-vue": { "node_modules/@vitejs/plugin-vue": {
"version": "6.0.3", "version": "6.0.3",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.3.tgz", "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.3.tgz",
@@ -1131,6 +1140,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/animate.css": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/animate.css/-/animate.css-4.1.1.tgz",
"integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==",
"license": "MIT"
},
"node_modules/async-validator": { "node_modules/async-validator": {
"version": "4.2.5", "version": "4.2.5",
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz", "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
@@ -1279,6 +1294,20 @@
"url": "https://github.com/fb55/entities?sponsor=1" "url": "https://github.com/fb55/entities?sponsor=1"
} }
}, },
"node_modules/errno": {
"version": "0.1.8",
"resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz",
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"prr": "~1.0.1"
},
"bin": {
"errno": "cli.js"
}
},
"node_modules/es-define-property": { "node_modules/es-define-property": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -1505,6 +1534,14 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC",
"optional": true
},
"node_modules/has-symbols": { "node_modules/has-symbols": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -1559,6 +1596,34 @@
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/image-size": {
"version": "0.5.5",
"resolved": "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz",
"integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
"dev": true,
"license": "MIT",
"optional": true,
"bin": {
"image-size": "bin/image-size.js"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-what": { "node_modules/is-what": {
"version": "5.5.0", "version": "5.5.0",
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-5.5.0.tgz", "resolved": "https://registry.npmmirror.com/is-what/-/is-what-5.5.0.tgz",
@@ -1571,6 +1636,54 @@
"url": "https://github.com/sponsors/mesqueeb" "url": "https://github.com/sponsors/mesqueeb"
} }
}, },
"node_modules/less": {
"version": "4.5.1",
"resolved": "https://registry.npmmirror.com/less/-/less-4.5.1.tgz",
"integrity": "sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==",
"dev": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"copy-anything": "^2.0.1",
"parse-node-version": "^1.0.1",
"tslib": "^2.3.0"
},
"bin": {
"lessc": "bin/lessc"
},
"engines": {
"node": ">=14"
},
"optionalDependencies": {
"errno": "^0.1.1",
"graceful-fs": "^4.1.2",
"image-size": "~0.5.0",
"make-dir": "^2.1.0",
"mime": "^1.4.1",
"needle": "^3.1.0",
"source-map": "~0.6.0"
}
},
"node_modules/less/node_modules/copy-anything": {
"version": "2.0.6",
"resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz",
"integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-what": "^3.14.1"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/less/node_modules/is-what": {
"version": "3.14.1",
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz",
"integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
"dev": true,
"license": "MIT"
},
"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",
@@ -1592,6 +1705,21 @@
"@jridgewell/sourcemap-codec": "^1.5.5" "@jridgewell/sourcemap-codec": "^1.5.5"
} }
}, },
"node_modules/make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"pify": "^4.0.1",
"semver": "^5.6.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/math-intrinsics": { "node_modules/math-intrinsics": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -1601,6 +1729,20 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"dev": true,
"license": "MIT",
"optional": true,
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": { "node_modules/mime-db": {
"version": "1.52.0", "version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
@@ -1683,6 +1825,34 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
} }
}, },
"node_modules/needle": {
"version": "3.3.1",
"resolved": "https://registry.npmmirror.com/needle/-/needle-3.3.1.tgz",
"integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"iconv-lite": "^0.6.3",
"sax": "^1.2.4"
},
"bin": {
"needle": "bin/needle"
},
"engines": {
"node": ">= 4.4.x"
}
},
"node_modules/parse-node-version": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/path-browserify": { "node_modules/path-browserify": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz", "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz",
@@ -1715,6 +1885,17 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/pify": {
"version": "4.0.1",
"resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=6"
}
},
"node_modules/pinia": { "node_modules/pinia": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.4.tgz", "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.4.tgz",
@@ -1770,6 +1951,14 @@
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/prr": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz",
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/rfdc": { "node_modules/rfdc": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
@@ -1818,12 +2007,50 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/sax": {
"version": "1.4.3",
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.3.tgz",
"integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
"dev": true,
"license": "BlueOak-1.0.0",
"optional": true
},
"node_modules/seemly": { "node_modules/seemly": {
"version": "0.3.10", "version": "0.3.10",
"resolved": "https://registry.npmmirror.com/seemly/-/seemly-0.3.10.tgz", "resolved": "https://registry.npmmirror.com/seemly/-/seemly-0.3.10.tgz",
"integrity": "sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==", "integrity": "sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"license": "ISC",
"optional": true,
"bin": {
"semver": "bin/semver"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1877,6 +2104,13 @@
"integrity": "sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==", "integrity": "sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.9.3", "version": "5.9.3",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",

View File

@@ -10,6 +10,8 @@
}, },
"dependencies": { "dependencies": {
"@remixicon/vue": "^4.8.0", "@remixicon/vue": "^4.8.0",
"@vicons/ionicons5": "^0.13.0",
"animate.css": "^4.1.1",
"axios": "^1.13.2", "axios": "^1.13.2",
"naive-ui": "^2.43.2", "naive-ui": "^2.43.2",
"pinia": "^3.0.4", "pinia": "^3.0.4",
@@ -20,6 +22,7 @@
"@types/node": "^24.10.1", "@types/node": "^24.10.1",
"@vitejs/plugin-vue": "^6.0.1", "@vitejs/plugin-vue": "^6.0.1",
"@vue/tsconfig": "^0.8.1", "@vue/tsconfig": "^0.8.1",
"less": "^4.5.1",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"vite": "^7.2.4", "vite": "^7.2.4",
"vue-tsc": "^3.1.4" "vue-tsc": "^3.1.4"

View File

@@ -2,6 +2,7 @@ import { createApp } from 'vue'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import router from './router' import router from './router'
import App from './App.vue' import App from './App.vue'
import 'animate.css'
const app = createApp(App) const app = createApp(App)

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="login-container"> <div class="view-account">
<!-- 维护公告遮罩 --> <!-- 维护公告遮罩 -->
<div v-if="!playerServiceEnabled" class="maintenance-overlay"> <div v-if="!playerServiceEnabled" class="maintenance-overlay">
<n-card title="系统维护公告" style="width: 500px; text-align: center;"> <n-card title="系统维护公告" style="width: 500px; text-align: center;">
@@ -13,45 +13,141 @@
</n-card> </n-card>
</div> </div>
<!-- 登录表单 --> <div class="view-account-header"></div>
<n-card v-else title="玩家登录" style="width: 400px;"> <div class="view-account-background">
<n-form ref="formRef" :model="formValue" :rules="rules" size="large"> <div class="line line-1"></div>
<n-form-item path="username" label="用户名"> <div class="line line-2"></div>
<n-input v-model:value="formValue.username" placeholder="请输入用户名" /> <div class="line line-3"></div>
</n-form-item> <div class="square square-1"></div>
<n-form-item path="password" label="密码"> <div class="square square-2"></div>
<n-input <div class="triangle"></div>
v-model:value="formValue.password" <div class="wave wave-1"></div>
type="password" <div class="wave wave-2"></div>
show-password-on="click" <div class="wave wave-3"></div>
placeholder="请输入密码" </div>
/> <div class="view-account-container animate__animated animate__fadeInDown">
</n-form-item> <div class="view-account-top">
<n-form-item v-if="captchaEnabled" path="captchaCode" label="验证码"> <div class="view-account-top-logo">
<n-space style="width: 100%"> <img src="/assets/account-logo-D9-eMHsk.png" alt="梦幻西游" />
</div>
<div class="view-account-top-desc">梦幻西游玩家服务中心</div>
</div>
<div class="view-account-form">
<h2 class="view-account-title">游戏账号登录</h2>
<div class="login-welcome">欢迎回来请登录您的游戏账号</div>
<n-form
ref="formRef"
label-placement="left"
size="large"
:model="formValue"
:rules="rules"
class="login-form"
>
<n-form-item path="username" class="username-item">
<n-input <n-input
v-model:value="formValue.captchaCode" v-model:value="formValue.username"
placeholder="请输入验证码" placeholder="请输入游戏账号"
@keyup.enter="handleLogin" class="login-input"
/> >
<div class="captcha-image" @click="handleRefreshCaptcha" v-html="captchaSvg"></div> <template #prefix>
</n-space> <n-icon size="18" color="#808695">
</n-form-item> <PersonOutline />
<n-form-item> </n-icon>
<n-button type="primary" block @click="handleLogin" :loading="loading"> </template>
登录 </n-input>
</n-button> </n-form-item>
</n-form-item> <n-form-item path="password" class="password-item">
</n-form> <n-input
</n-card> v-model:value="formValue.password"
type="password"
showPasswordOn="click"
placeholder="请输入密码"
class="login-input"
>
<template #prefix>
<n-icon size="18" color="#808695">
<LockClosedOutline />
</n-icon>
</template>
</n-input>
</n-form-item>
<n-form-item v-if="captchaEnabled" path="captchaCode" class="captcha-item">
<n-space style="width: 100%">
<n-input
v-model:value="formValue.captchaCode"
placeholder="请输入验证码"
class="login-input"
>
<template #prefix>
<n-icon size="18" color="#808695">
<ShieldOutline />
</n-icon>
</template>
</n-input>
<div class="captcha-image" @click="handleRefreshCaptcha" v-html="captchaSvg"></div>
</n-space>
</n-form-item>
<n-form-item class="default-color remember-forgot">
<div class="flex-between-wrapper">
<div class="left">
<n-checkbox v-model:checked="autoLogin">自动登录</n-checkbox>
</div>
<div class="right">
<a href="javascript:" class="forgot-link">忘记密码</a>
</div>
</div>
</n-form-item>
<n-form-item>
<n-button
type="primary"
@click="handleLogin"
size="large"
:loading="loading"
block
class="login-button"
>
登录
</n-button>
</n-form-item>
<n-form-item class="default-color other-item">
<div class="flex view-account-other">
<div class="flex-initial other-text">
<span>其它登录方式</span>
</div>
<!-- <div class="social-login">
<a href="javascript:" class="social-icon">
<n-icon size="24" color="#909399">
<LogoGithub />
</n-icon>
</a>
<a href="javascript:" class="social-icon">
<n-icon size="24" color="#909399">
<LogoFacebook />
</n-icon>
</a>
<a href="javascript:" class="social-icon">
<n-icon size="24" color="#909399">
<LogoWechat />
</n-icon>
</a>
</div> -->
<div class="flex-initial" style="margin-left: auto">
<a href="javascript:" class="register-link">注册账号</a>
</div>
</div>
</n-form-item>
</n-form>
</div>
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { NCard, NForm, NFormItem, NInput, NButton, NSpace, NIcon, useMessage } from 'naive-ui' import { NCard, NForm, NFormItem, NInput, NButton, NSpace, NIcon, NCheckbox, useMessage } from 'naive-ui'
import { RiAlertLine } from '@remixicon/vue' import { RiAlertLine } from '@remixicon/vue'
import { PersonOutline, LockClosedOutline, ShieldOutline } from '@vicons/ionicons5'
import { usePlayerStore } from '@/stores/player' import { usePlayerStore } from '@/stores/player'
import { generateCaptcha, checkCaptchaEnabled, checkPlayerServiceStatus } from '@/api/player' import { generateCaptcha, checkCaptchaEnabled, checkPlayerServiceStatus } from '@/api/player'
@@ -66,6 +162,7 @@ const captchaSvg = ref('')
const captchaId = ref('') const captchaId = ref('')
const playerServiceEnabled = ref(true) const playerServiceEnabled = ref(true)
const playerServiceCloseMsg = ref('玩家服务中心系统维护中') const playerServiceCloseMsg = ref('玩家服务中心系统维护中')
const autoLogin = ref(true)
const formValue = ref({ const formValue = ref({
username: '', username: '',
@@ -110,7 +207,7 @@ const handleLogin = async () => {
) )
if (success) { if (success) {
message.success('登录成功') message.success('登录成功,即将进入系统')
router.push('/player/dashboard') router.push('/player/dashboard')
} else { } else {
message.error('登录失败,请检查用户名和密码') message.error('登录失败,请检查用户名和密码')
@@ -177,49 +274,532 @@ const handleRefreshStatus = async () => {
onMounted(() => { onMounted(() => {
checkCaptchaStatus() checkCaptchaStatus()
checkServiceStatus() checkServiceStatus()
setTimeout(() => {
const usernameInput = document.querySelector('input[placeholder="请输入用户名"]')
if (usernameInput) {
(usernameInput as HTMLElement).focus()
}
}, 500)
}) })
</script> </script>
<style scoped> <style lang="less" scoped>
.login-container { .view-account {
display: flex; display: flex;
justify-content: center; flex-direction: column;
align-items: center;
height: 100vh; height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); overflow: auto;
background-color: #f0f2f5;
background: linear-gradient(140deg, #e8f1fa, #c2d9ec, #a1c3e0, #80aed3);
position: relative;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MCIgaGVpZ2h0PSI2MCI+CiAgPGNpcmNsZSBjeD0iMTAiIGN5PSIxMCIgcj0iMiIgZmlsbD0icmdiYSg0NSwgMTQwLCAyNDAsIDAuMSkiIC8+Cjwvc3ZnPg==');
opacity: 0.6;
z-index: 0;
}
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj4KICA8cmVjdCB4PSI1MCIgeT0iNTAiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgdHJhbnNmb3JtPSJyb3RhdGUoNDUgNTUgNTUpIiBmaWxsPSJyZ2JhKDQ1LCAxNDAsIDI0MCwgMC4wNSkiIC8+Cjwvc3ZnPg==');
opacity: 0.8;
z-index: 0;
}
&-container {
padding: 32px 40px 20px;
max-width: 580px;
min-width: 460px;
margin: 0 auto;
background-color: #fff;
border-radius: 12px;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
margin-top: 10vh;
position: relative;
backdrop-filter: blur(10px);
background: rgba(255, 255, 255, 0.95);
border: 1px solid rgba(255, 255, 255, 0.18);
transition: all 0.3s ease;
z-index: 1;
&:hover {
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
transform: translateY(-5px);
}
}
&-title {
text-align: center;
font-size: 22px;
font-weight: 500;
color: #333;
margin-bottom: 8px;
position: relative;
&::after {
content: '';
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
width: 40px;
height: 2px;
background: linear-gradient(to right, #2d8cf0, #0081ff);
border-radius: 2px;
}
}
.login-welcome {
text-align: center;
font-size: 14px;
color: #606266;
margin-bottom: 30px;
margin-top: 20px;
}
&-top {
padding: 10px 0;
text-align: center;
&-logo {
margin-bottom: 8px;
display: flex;
justify-content: center;
img {
height: 60px;
}
}
&-desc {
font-size: 14px;
color: #606266;
}
}
&-other {
width: 100%;
display: flex;
align-items: center;
}
.default-color {
color: #515a6e;
:deep(.ant-checkbox-wrapper) {
color: #515a6e;
}
}
.login-button {
margin-top: 10px;
height: 42px;
font-size: 16px;
border-radius: 4px;
transition: all 0.3s;
position: relative;
overflow: hidden;
&:hover {
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
}
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 5px;
height: 5px;
background: rgba(255, 255, 255, 0.5);
opacity: 0;
border-radius: 100%;
transform: scale(1, 1) translate(-50%);
transform-origin: 50% 50%;
}
&:focus:not(:active)::after {
animation: ripple 1s ease-out;
}
@keyframes ripple {
0% {
transform: scale(0, 0);
opacity: 0.5;
}
20% {
transform: scale(25, 25);
opacity: 0.3;
}
100% {
opacity: 0;
transform: scale(40, 40);
}
}
}
.remember-forgot {
margin-bottom: 5px;
.flex-between-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.right {
text-align: right;
}
}
.forgot-link {
color: #606266;
transition: all 0.2s;
&:hover {
color: #2d8cf0;
}
}
.social-login {
display: flex;
margin-left: 16px;
}
.social-icon {
display: flex;
justify-content: center;
align-items: center;
width: 36px;
height: 36px;
border-radius: 50%;
margin-right: 12px;
transition: all 0.3s;
background-color: rgba(144, 147, 153, 0.1);
&:hover {
background-color: rgba(45, 140, 240, 0.2);
transform: scale(1.1);
:deep(svg) {
color: #2d8cf0 !important;
}
}
}
.register-link {
color: #2d8cf0;
transition: all 0.3s;
&:hover {
color: #57a3f3;
text-decoration: underline;
}
}
.login-form {
:deep(.n-form-item-feedback-wrapper) {
min-height: 18px;
}
:deep(.n-input) {
border-radius: 4px;
}
padding: 0;
}
.login-input {
:deep(.n-input__input-el) {
padding-left: 5px;
}
:deep(.n-input-wrapper) {
transition: all 0.3s ease;
}
&:hover {
:deep(.n-input-wrapper) {
box-shadow: 0 0 0 1px rgba(45, 140, 240, 0.2);
}
}
}
.username-item, .password-item, .captcha-item {
margin-bottom: 24px;
}
.captcha-image {
width: 120px;
height: 40px;
cursor: pointer;
border: 1px solid #dcdfe6;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
transition: all 0.3s;
}
.captcha-image:hover {
border-color: #409eff;
box-shadow: 0 0 0 1px rgba(64, 158, 255, 0.2);
}
.captcha-image :deep(svg) {
width: 100%;
height: 100%;
}
.other-text {
padding-left: 5px;
}
.other-item {
margin-bottom: 0;
}
.maintenance-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
} }
.maintenance-overlay { @media (min-width: 768px) {
position: fixed; .view-account {
top: 0; background-image: url('../../assets/images/login.svg'),
left: 0; radial-gradient(circle at 10% 20%, rgba(100, 149, 237, 0.25) 0%, rgba(65, 105, 225, 0.2) 40%, rgba(30, 144, 255, 0.1) 90%);
right: 0; background-repeat: no-repeat;
bottom: 0; background-position: 50%;
background: rgba(0, 0, 0, 0.7); background-size: cover;
display: flex; position: relative;
justify-content: center;
align-items: center; &::before {
z-index: 1000; content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(45, 140, 240, 0.1), rgba(45, 140, 240, 0.05));
backdrop-filter: blur(10px);
z-index: 0;
}
&::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8ZGVmcz4KICA8cGF0dGVybiBpZD0icGF0dGVybiIgeD0iMCIgeT0iMCIgd2lkdGg9IjYwIiBoZWlnaHQ9IjYwIiBwYXR0ZXJuVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBwYXR0ZXJuVHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgIDxjaXJjbGUgY3g9IjMwIiBjeT0iMzAiIHI9IjEuNSIgZmlsbD0icmdiYSg0NSwgMTQwLCAyNDAsIDAuMikiIC8+CiAgPC9wYXR0ZXJuPgo8L2RlZnM+CjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjcGF0dGVybikiIC8+Cjwvc3ZnPg==');
opacity: 0.3;
z-index: 0;
pointer-events: none;
}
&-container {
margin-top: 15vh;
z-index: 1;
position: relative;
}
}
} }
.captcha-image { @media (max-height: 650px) {
width: 120px; .view-account-container {
height: 40px; margin-top: 5vh;
cursor: pointer; }
border: 1px solid #dcdfe6;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
} }
.captcha-image:hover { .view-account-background {
border-color: #409eff; position: absolute;
}
.captcha-image :deep(svg) {
width: 100%; width: 100%;
height: 100%; height: 100%;
top: 0;
left: 0;
overflow: hidden;
pointer-events: none;
z-index: 0;
.line {
position: absolute;
background: linear-gradient(90deg, rgba(45, 140, 240, 0.2), rgba(0, 129, 255, 0.1));
&-1 {
width: 300px;
height: 2px;
top: 15%;
right: 5%;
transform: rotate(-30deg);
animation: pulse 8s ease-in-out infinite;
}
&-2 {
width: 200px;
height: 2px;
bottom: 20%;
left: 10%;
transform: rotate(45deg);
animation: pulse 6s ease-in-out infinite 1s;
}
&-3 {
width: 150px;
height: 2px;
top: 40%;
left: 5%;
transform: rotate(-15deg);
animation: pulse 7s ease-in-out infinite 2s;
}
}
.square {
position: absolute;
&-1 {
width: 80px;
height: 80px;
top: 10%;
left: 15%;
background: linear-gradient(45deg, rgba(45, 140, 240, 0.15), rgba(0, 129, 255, 0.05));
transform: rotate(30deg);
animation: rotate 15s linear infinite;
}
&-2 {
width: 60px;
height: 60px;
bottom: 15%;
right: 10%;
border: 2px solid rgba(45, 140, 240, 0.1);
background: transparent;
animation: rotate 12s linear infinite reverse;
}
}
.triangle {
position: absolute;
bottom: 30%;
right: 20%;
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-bottom: 80px solid rgba(45, 140, 240, 0.08);
animation: float 10s ease-in-out infinite;
}
@keyframes pulse {
0% {
opacity: 0.3;
}
50% {
opacity: 0.6;
}
100% {
opacity: 0.3;
}
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.wave {
position: absolute;
opacity: 0.3;
transform-origin: bottom left;
&-1 {
bottom: 0;
left: 0;
width: 100%;
height: 120px;
background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNDQwIDMyMCIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+PHBhdGggZmlsbD0icmdiYSg0NSwgMTQwLCAyNDAsIDAuMikiIGQ9Ik0wLDMyMEMwLDI0MCA0MCwxNjAgODAsMTYwQzEyMCwxNjAgMTYwLDI0MCAyMDAsMjQwQzI0MCwyNDAgMjgwLDE2MCAzMjAsMTYwQzM2MCwxNjAgNDAwLDI0MCA0NDAsMjQwQzQ4MCwyNDAgNTIwLDE2MCA1NjAsMTYwQzYwMCwxNjAgNjQwLDI0MCA2ODAsMjQwQzcyMCwyNDAgNzYwLDE2MCA4MDAsMTYwQzg0MCwxNjAgODgwLDI0MCA5MjAsMjQwQzk2MCwyNDAgMTAwMCwxNjAgMTA0MCwxNjBDMTA4MCwxNjAgMTEyMCwyNDAgMTE2MCwyNDBDMTIwMCwyNDAgMTI0MCwxNjAgMTI4MCwxNjBDMTMyMCwxNjAgMTM2MCwyNDAgMTQwMCwyNDBDMTQ0MCwyNDAgMTQ0MCwxNjAgMTQ0MCwxNjBMMTQ0MCwzMjBMMCwzMjBaIj48L3BhdGg+PC9zdmc+');
background-size: 100% 120px;
animation: wave-left-to-right 15s ease-in-out infinite;
transform: rotate(-2deg);
}
&-2 {
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNDQwIDMyMCIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+PHBhdGggZmlsbD0icmdiYSg0NSwgMTQwLCAyNDAsIDAuMTUpIiBkPSJNMCwzMjBDMCwyNDAgNjAsMTgwIDEyMCwxODBDMTgwLDE4MCAyNDAsMjQwIDMwMCwyNDBDMzYwLDI0MCA0MjAsMTgwIDQ4MCwxODBDNTQwLDE4MCA2MDAsMjQwIDY2MCwyNDBDNzIwLDI0MCA3ODAsMTgwIDg0MCwxODBDOTAwLDE4MCA5NjAsMjQwIDEwMjAsMjQwQzEwODAsMjQwIDExNDAsMTgwIDEyMDAsMTgwQzEyNjAsMTgwIDEzMjAsMjQwIDEzODAsMjQwQzE0NDAsMjQwIDE0NDAsMTgwIDE0NDAsMTgwTDE0NDAsMzIwTDAsMzIwWiI+PC9wYXRoPjwvc3ZnPg==');
background-size: 100% 100px;
animation: wave-left-to-right 18s ease-in-out infinite;
animation-delay: -5s;
transform: rotate(-1deg);
}
&-3 {
bottom: 0;
left: 0;
width: 100%;
height: 80px;
background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNDQwIDMyMCIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+PHBhdGggZmlsbD0icmdiYSg0NSwgMTQwLCAyNDAsIDAuMSkiIGQ9Ik0wLDMyMEMwLDI2MCAzMCwyMDAgNjAsMjAwQzkwLDIwMCAxMjAsMjYwIDE1MCwyNjBDMTgwLDI2MCAyMTAsMjAwIDI0MCwyMDBDMjcwLDIwMCAzMDAsMjYwIDMzMCwyNjBDMzYwLDI2MCAzOTAsMjAwIDQyMCwyMDBDNDUwLDIwMCA0ODAsMjYwIDUxMCwyNjBDNTQwLDI2MCA1NzAsMjAwIDYwMCwyMDBDNjMwLDIwMCA2NjAsMjYwIDY5MCwyNjBDNzIwLDI2MCA3NTAsMjAwIDc4MCwyMDBDODEwLDIwMCA4NDAsMjYwIDg3MCwyNjBDOTAwLDI2MCA5MzAsMjAwIDk2MCwyMDBDOTkwLDIwMCAxMDIwLDI2MCAxMDUwLDI2MEMxMDgwLDI2MCAxMTEwLDIwMCAxMTQwLDIwMEMxMTcwLDIwMCAxMjAwLDI2MCAxMjMwLDI2MEMxMjYwLDI2MCAxMjkwLDIwMCAxMzIwLDIwMEMxMzUwLDIwMCAxMzgwLDI2MCAxNDEwLDI2MEMxNDQwLDI2MCAxNDQwLDIwMCAxNDQwLDIwMEwxNDQwLDMyMEwwLDMyMFoiPjwvcGF0aD48L3N2Zz4=');
background-size: 100% 80px;
animation: wave-left-to-right 20s ease-in-out infinite;
animation-delay: -2s;
}
}
@keyframes wave-left-to-right {
0% {
background-position-x: 0;
background-position-y: 100%;
}
50% {
background-position-x: 720px;
background-position-y: 50%;
}
100% {
background-position-x: 1440px;
background-position-y: 0%;
}
}
@keyframes float {
0% {
transform: translateY(0);
}
50% {
transform: translateY(-15px);
}
100% {
transform: translateY(0);
}
}
} }
</style> </style>