修改登陆api

This commit is contained in:
Kunagisa 2025-06-04 23:50:46 +08:00
parent 4f5e971cb6
commit 60667511b4
4 changed files with 349 additions and 218 deletions

View File

@ -5,7 +5,6 @@
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<script src="https://v-cn.vaptcha.com/v3.js"></script>
<title>红色警戒3数据分析中心</title>
</head>
<body>

View File

@ -50,39 +50,45 @@ import { loginSuccess } from '../utils/jwt';
// }
// )
export const userLogin = async (username, password, server, token) => {
// 获取验证码
export const getCaptcha = async () => {
try {
const response = await axiosInstance.get('/captcha');
return response.data;
} catch (error) {
throw error;
}
};
// 用户登录
export const userLogin = async (username, password, token, captcha) => {
try {
// console.log('登录请求参数:', { username, password, server, token }); // 保留此调试日志以备将来使用,或按需移除
const response = await axiosInstance.post('/user/login', {
username,
password,
server,
token
token,
captcha
});
if (response.data.access_token) {
loginSuccess(response.data.access_token, username); // 使用 username 作为 userId
loginSuccess(response.data.access_token);
}
return response.data;
} catch (error) {
// 错误将由响应拦截器统一处理和记录,这里可以直接抛出
throw error;
}
};
export const userRegister = async (qq_code, password, server, token) => {
// 用户注册
export const userRegister = async (qq_code, password, token, captcha) => {
try {
const requestData = {
const response = await axiosInstance.post('/user/register', {
qq_code,
password,
server,
token
};
// console.log('注册请求参数:', requestData); // 保留此调试日志以备将来使用,或按需移除
const response = await axiosInstance.post('/user/register', requestData);
// console.log('注册响应数据:', response.data); // 保留此调试日志以备将来使用,或按需移除
token,
captcha
});
return response.data;
} catch (error) {
throw error;

View File

@ -1,7 +1,7 @@
<template>
<div class="login-form">
<div>登陆</div>
<form class="login-form-container" @submit.prevent = "handleLogin">
<form class="login-form-container" @submit.prevent="handleLogin">
<div class="input-container">
<label for="username">QQ号</label>
<input
@ -26,43 +26,29 @@
/>
<span class="error-message" v-if="passwordError">{{ passwordError }}</span>
</div>
<div class="input-container">
<!-- 人机验证 -->
<div id="VAPTCHAContainer" style="width: 100%; height: 36px;">
<div class="VAPTCHA-init-main">
<div class="VAPTCHA-init-loading">
<a href="/" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48px"
height="60px" viewBox="0 0 24 30"
style="enable-background: new 0 0 50 50; width: 14px; height: 14px; vertical-align: middle"
xml:space="preserve">
<rect x="0" y="9.22656" width="4" height="12.5469" fill="#CCCCCC">
<animate attributeName="height" attributeType="XML" values="5;21;5" begin="0s" dur="0.6s"
repeatCount="indefinite"></animate>
<animate attributeName="y" attributeType="XML" values="13; 5; 13" begin="0s" dur="0.6s"
repeatCount="indefinite"></animate>
</rect>
<rect x="10" y="5.22656" width="4" height="20.5469" fill="#CCCCCC">
<animate attributeName="height" attributeType="XML" values="5;21;5" begin="0.15s" dur="0.6s"
repeatCount="indefinite"></animate>
<animate attributeName="y" attributeType="XML" values="13; 5; 13" begin="0.15s" dur="0.6s"
repeatCount="indefinite"></animate>
</rect>
<rect x="20" y="8.77344" width="4" height="13.4531" fill="#CCCCCC">
<animate attributeName="height" attributeType="XML" values="5;21;5" begin="0.3s" dur="0.6s"
repeatCount="indefinite"></animate>
<animate attributeName="y" attributeType="XML" values="13; 5; 13" begin="0.3s" dur="0.6s"
repeatCount="indefinite"></animate>
</rect>
</svg>
</a>
<span class="VAPTCHA-text">Vaptcha Initializing...</span>
</div>
</div>
<div class="input-container captcha-container">
<label for="captcha">验证码</label>
<div class="captcha-wrapper">
<input
type="text"
id="captcha"
v-model="captcha"
placeholder="请输入验证码"
@blur="validateCaptcha"
:class="{ 'error': captchaError }"
/>
<img
v-if="captchaImage"
:src="captchaImage"
alt="验证码"
class="captcha-image"
@click="refreshCaptcha"
/>
</div>
<span class="error-message" v-if="captchaError">{{ captchaError }}</span>
</div>
<div class="login-button">
<button type="submit" :disabled="!isFormValid" >登录</button>
<button type="submit" :disabled="!isFormValid">登录</button>
</div>
<div class="register-link">
<a @click.prevent="$emit('register')">注册账号</a>
@ -72,20 +58,21 @@
</template>
<script setup>
import {ref, onMounted, computed} from 'vue'
import { ref, onMounted, computed } from 'vue'
import { useRouter } from 'vue-router'
import { userLogin } from '../api/login'
import { userLogin, getCaptcha } from '../api/login'
const router = useRouter()
const VAPTCHAObj = ref(null)
const username = ref('')
const password = ref('')
const captcha = ref('')
const captchaImage = ref('')
const captchaToken = ref('')
//
const usernameError = ref('')
const passwordError = ref('')
const isVaptchaVerified = ref(false)
const captchaError = ref('')
const validateUsername = () => {
if (!username.value) {
@ -121,40 +108,43 @@ const validatePassword = () => {
return true
}
const validateCaptcha = () => {
if (!captcha.value) {
captchaError.value = '请输入验证码'
return false
}
if (captcha.value.length !== 4) {
captchaError.value = '验证码长度不正确'
return false
}
captchaError.value = ''
return true
}
const isFormValid = computed(() => {
return !usernameError.value &&
!passwordError.value &&
!captchaError.value &&
username.value &&
password.value &&
VAPTCHAObj.value && //
isVaptchaVerified.value //
captcha.value
})
const refreshCaptcha = async () => {
try {
const response = await getCaptcha()
// 使APIimgbase64
captchaImage.value = `data:image/png;base64,${response.img}`
captchaToken.value = response.token
captcha.value = '' //
} catch (error) {
console.error('获取验证码失败:', error)
alert('获取验证码失败,请刷新页面重试')
}
}
onMounted(() => {
window.vaptcha({
vid: '6828264bdc0ff12924d9bfe3',
mode: 'click',
scene: 1,
container: '#VAPTCHAContainer',
area: 'auto'
}).then(function (obj) {
VAPTCHAObj.value = obj
obj.render()
//
obj.listen('pass', function () {
isVaptchaVerified.value = true
})
//
obj.listen('fail', function () {
isVaptchaVerified.value = false
})
//
obj.listen('close', function () {
isVaptchaVerified.value = false
})
})
refreshCaptcha()
})
const handleLogin = async () => {
@ -163,58 +153,37 @@ const handleLogin = async () => {
if (!validateForm()) {
return
}
//
if (!VAPTCHAObj.value) {
alert('请完成人机验证')
return
}
const serverToken = await VAPTCHAObj.value.getServerToken()
console.log('获取到的 serverToken:', serverToken)
if (!serverToken || !serverToken.token || !serverToken.server) {
alert('获取验证token失败请重新验证')
return
}
const registerData = {
username: username.value,
password: password.value,
server: serverToken.server,
token: serverToken.token
}
// API
const response = await userLogin(
registerData.username,
registerData.password,
registerData.server,
registerData.token
username.value,
password.value,
captchaToken.value,
captcha.value
)
//
if (response.access_token) {
// token ID (QQ)
localStorage.setItem('access_token', response.access_token)
localStorage.setItem('user_id', username.value) // username.value QQ
localStorage.setItem('user_id', username.value)
router.push('/')
} else {
throw new Error('登录失败:未获取到访问令牌')
}
} catch (error) {
console.error('登录失败:', error)
if (error.response && error.response.status === 401) {
alert('错误的用户名或密码,或未注册')
alert('错误的用户名或密码')
} else {
alert(error.response?.data?.message || error.message || '登录失败,请稍后重试')
}
//
refreshCaptcha()
}
}
const validateForm = () => {
if (!username.value || !password.value) {
alert('请输入用户名和密码')
return false
}
return true
return validateUsername() && validatePassword() && validateCaptcha()
}
</script>
@ -351,5 +320,98 @@ const validateForm = () => {
.input-container input.error {
border-color: #f56c6c;
}
.captcha-container {
margin-bottom: 16px;
width: 100%;
}
.captcha-wrapper {
display: flex;
gap: 8px;
align-items: center;
width: 100%;
position: relative;
}
.captcha-wrapper input {
flex: 1;
height: 40px;
border: 1px solid #d0d7de;
border-radius: 6px;
padding: 0 12px;
font-size: 15px;
background: #f7fbfd;
transition: border 0.2s;
width: calc(100% - 120px); /* 减去验证码图片的宽度和间距 */
}
.captcha-wrapper input:focus {
border: 1.5px solid #409eff;
outline: none;
background: #fff;
}
.captcha-image {
width: 110px;
height: 40px;
border-radius: 6px;
cursor: pointer;
object-fit: cover;
border: 1px solid #d0d7de;
transition: all 0.3s;
}
.captcha-image:hover {
opacity: 0.8;
}
/* 移动端适配 */
@media screen and (max-width: 480px) {
.login-form {
width: 90%;
max-width: 340px;
padding: 24px 20px 20px 20px;
}
.captcha-wrapper {
gap: 6px;
}
.captcha-wrapper input {
width: calc(100% - 100px);
}
.captcha-image {
width: 90px;
}
.input-container input,
.captcha-wrapper input {
height: 38px;
font-size: 14px;
}
.login-button button {
height: 40px;
font-size: 15px;
}
}
/* 超小屏幕适配 */
@media screen and (max-width: 320px) {
.login-form {
padding: 20px 16px 16px 16px;
}
.captcha-wrapper input {
width: calc(100% - 90px);
padding: 0 8px;
}
.captcha-image {
width: 80px;
}
}
</style>

View File

@ -20,40 +20,26 @@
@blur="validateConfirmPassword" :class="{ 'error': confirmPasswordError }" />
<span class="error-message" v-if="confirmPasswordError">{{ confirmPasswordError }}</span>
</div>
<div class="input-container">
<!-- 人机验证 -->
<div id="VAPTCHAContainer" style="width: 100%; height: 36px;">
<div class="VAPTCHA-init-main">
<div class="VAPTCHA-init-loading">
<a href="/" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48px"
height="60px" viewBox="0 0 24 30"
style="enable-background: new 0 0 50 50; width: 14px; height: 14px; vertical-align: middle"
xml:space="preserve">
<rect x="0" y="9.22656" width="4" height="12.5469" fill="#CCCCCC">
<animate attributeName="height" attributeType="XML" values="5;21;5" begin="0s" dur="0.6s"
repeatCount="indefinite"></animate>
<animate attributeName="y" attributeType="XML" values="13; 5; 13" begin="0s" dur="0.6s"
repeatCount="indefinite"></animate>
</rect>
<rect x="10" y="5.22656" width="4" height="20.5469" fill="#CCCCCC">
<animate attributeName="height" attributeType="XML" values="5;21;5" begin="0.15s" dur="0.6s"
repeatCount="indefinite"></animate>
<animate attributeName="y" attributeType="XML" values="13; 5; 13" begin="0.15s" dur="0.6s"
repeatCount="indefinite"></animate>
</rect>
<rect x="20" y="8.77344" width="4" height="13.4531" fill="#CCCCCC">
<animate attributeName="height" attributeType="XML" values="5;21;5" begin="0.3s" dur="0.6s"
repeatCount="indefinite"></animate>
<animate attributeName="y" attributeType="XML" values="13; 5; 13" begin="0.3s" dur="0.6s"
repeatCount="indefinite"></animate>
</rect>
</svg>
</a>
<span class="VAPTCHA-text">Vaptcha Initializing...</span>
</div>
</div>
<div class="input-container captcha-container">
<label for="captcha">验证码</label>
<div class="captcha-wrapper">
<input
type="text"
id="captcha"
v-model="captcha"
placeholder="请输入验证码"
@blur="validateCaptcha"
:class="{ 'error': captchaError }"
/>
<img
v-if="captchaImage"
:src="captchaImage"
alt="验证码"
class="captcha-image"
@click="refreshCaptcha"
/>
</div>
<span class="error-message" v-if="captchaError">{{ captchaError }}</span>
</div>
<div class="login-button">
<button type="submit" :disabled="!isFormValid">注册</button>
@ -68,13 +54,15 @@
<script setup>
import { ref, onMounted, computed } from 'vue'
import { useRouter } from 'vue-router'
import { userRegister } from "@/api/login.js";
import { userRegister, getCaptcha } from "@/api/login.js";
const router = useRouter()
const VAPTCHAObj = ref(null)
const username = ref('')
const password = ref('')
const confirmPassword = ref('')
const captcha = ref('')
const captchaImage = ref('')
const captchaToken = ref('')
// emit
const emit = defineEmits(['login'])
@ -83,8 +71,7 @@ const emit = defineEmits(['login'])
const usernameError = ref('')
const passwordError = ref('')
const confirmPasswordError = ref('')
const captchaError = ref('')
//
const validateUsername = () => {
@ -126,92 +113,76 @@ const validateConfirmPassword = () => {
confirmPasswordError.value = ''
return true
}
const isVaptchaVerified = ref(false)
const validateCaptcha = () => {
if (!captcha.value) {
captchaError.value = '请输入验证码'
return false
}
if (captcha.value.length !== 4) {
captchaError.value = '验证码长度不正确'
return false
}
captchaError.value = ''
return true
}
//
const isFormValid = computed(() => {
return !usernameError.value &&
!passwordError.value &&
!confirmPasswordError.value &&
!captchaError.value &&
username.value &&
password.value &&
confirmPassword.value &&
VAPTCHAObj.value && //
isVaptchaVerified.value //
captcha.value
})
const refreshCaptcha = async () => {
try {
const response = await getCaptcha()
captchaImage.value = `data:image/png;base64,${response.img}`
captchaToken.value = response.token
captcha.value = '' //
} catch (error) {
console.error('获取验证码失败:', error)
alert('获取验证码失败,请刷新页面重试')
}
}
//
onMounted(() => {
window.vaptcha({
vid: '6828264bdc0ff12924d9bfe3',
mode: 'click',
scene: 2,
container: '#VAPTCHAContainer',
area: 'auto'
}).then(function (obj) {
VAPTCHAObj.value = obj
obj.render()
//
obj.listen('pass', function () {
isVaptchaVerified.value = true
console.log('验证通过')
})
//
obj.listen('fail', function () {
isVaptchaVerified.value = false
console.log('验证失败')
})
//
obj.listen('close', function () {
isVaptchaVerified.value = false
console.log('验证关闭')
})
})
refreshCaptcha()
})
const handleRegister = async () => {
//
if (!validateUsername() || !validatePassword() || !validateConfirmPassword()) {
return
}
//
if (!isVaptchaVerified.value || !VAPTCHAObj.value) {
alert('请完成人机验证')
return
}
try {
//
if (!validateUsername() || !validatePassword() || !validateConfirmPassword() || !validateCaptcha()) {
return
}
// token
const serverToken = await VAPTCHAObj.value.getServerToken()
console.log('获取到的 serverToken:', serverToken)
//
await userRegister(
username.value,
password.value,
captchaToken.value,
captcha.value
)
if (!serverToken || !serverToken.token || !serverToken.server) {
alert('获取验证token失败请重新验证')
return
alert('注册成功')
//
emit('login')
} catch (error) {
console.error('注册失败:', error)
if (error.response && error.response.status === 400) {
alert('该QQ号已被注册')
} else {
alert(error.response?.data?.message || error.message || '注册失败,请稍后重试')
}
//
refreshCaptcha()
}
//
const registerData = {
qq_code: username.value,
password: password.value,
server: serverToken.server,
token: serverToken.token
}
console.log('注册请求参数:', registerData)
//
await userRegister(
registerData.qq_code,
registerData.password,
registerData.server,
registerData.token
)
alert('注册成功')
//
emit('login')
}
</script>
@ -355,4 +326,97 @@ const handleRegister = async () => {
color: #666;
vertical-align: middle;
}
.captcha-container {
margin-bottom: 16px;
width: 100%;
}
.captcha-wrapper {
display: flex;
gap: 8px;
align-items: center;
width: 100%;
position: relative;
}
.captcha-wrapper input {
flex: 1;
height: 40px;
border: 1px solid #d0d7de;
border-radius: 6px;
padding: 0 12px;
font-size: 15px;
background: #f7fbfd;
transition: border 0.2s;
width: calc(100% - 120px); /* 减去验证码图片的宽度和间距 */
}
.captcha-wrapper input:focus {
border: 1.5px solid #409eff;
outline: none;
background: #fff;
}
.captcha-image {
width: 110px;
height: 40px;
border-radius: 6px;
cursor: pointer;
object-fit: cover;
border: 1px solid #d0d7de;
transition: all 0.3s;
}
.captcha-image:hover {
opacity: 0.8;
}
/* 移动端适配 */
@media screen and (max-width: 480px) {
.login-form {
width: 90%;
max-width: 340px;
padding: 24px 20px 20px 20px;
}
.captcha-wrapper {
gap: 6px;
}
.captcha-wrapper input {
width: calc(100% - 100px);
}
.captcha-image {
width: 90px;
}
.input-container input,
.captcha-wrapper input {
height: 38px;
font-size: 14px;
}
.login-button button {
height: 40px;
font-size: 15px;
}
}
/* 超小屏幕适配 */
@media screen and (max-width: 320px) {
.login-form {
padding: 20px 16px 16px 16px;
}
.captcha-wrapper input {
width: calc(100% - 90px);
padding: 0 8px;
}
.captcha-image {
width: 80px;
}
}
</style>