后台登陆验证码
This commit is contained in:
@@ -20,14 +20,30 @@ export interface AdminLoginData {
|
||||
tokens: AdminTokenPair
|
||||
}
|
||||
|
||||
export interface AdminCaptcha {
|
||||
captcha_id: string
|
||||
image: string
|
||||
expires_in: number
|
||||
}
|
||||
|
||||
interface ApiResponse<T> {
|
||||
code: string
|
||||
message: string
|
||||
data: T
|
||||
}
|
||||
|
||||
export async function loginAdmin(username: string, password: string) {
|
||||
const { data } = await apiClient.post<ApiResponse<AdminLoginData>>('/admin/auth/login', { username, password })
|
||||
export async function fetchAdminCaptcha() {
|
||||
const { data } = await apiClient.get<ApiResponse<AdminCaptcha>>('/admin/auth/captcha')
|
||||
return data.data
|
||||
}
|
||||
|
||||
export async function loginAdmin(username: string, password: string, captchaId: string, captchaCode: string) {
|
||||
const { data } = await apiClient.post<ApiResponse<AdminLoginData>>('/admin/auth/login', {
|
||||
username,
|
||||
password,
|
||||
captcha_id: captchaId,
|
||||
captcha_code: captchaCode,
|
||||
})
|
||||
return data.data
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ export const useAdminSessionStore = defineStore('adminSession', {
|
||||
nickname: '',
|
||||
}),
|
||||
actions: {
|
||||
async login(username: string, password: string) {
|
||||
const result = await loginAdmin(username, password)
|
||||
async login(username: string, password: string, captchaId: string, captchaCode: string) {
|
||||
const result = await loginAdmin(username, password, captchaId, captchaCode)
|
||||
this.applySession(result.admin, result.tokens.access_token, result.tokens.refresh_token)
|
||||
return result
|
||||
},
|
||||
|
||||
@@ -199,6 +199,37 @@ a {
|
||||
color: #52616f;
|
||||
}
|
||||
|
||||
.admin-captcha-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 132px;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.captcha-image-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 44px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.captcha-image-button:disabled {
|
||||
cursor: wait;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.captcha-image-button img {
|
||||
display: block;
|
||||
width: 132px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.page {
|
||||
max-width: 1120px;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { fetchAdminCaptcha, type AdminCaptcha } from '@/api/adminAuth'
|
||||
import { useAdminSessionStore } from '@/stores/adminSession'
|
||||
|
||||
const router = useRouter()
|
||||
const adminSession = useAdminSessionStore()
|
||||
const loading = ref(false)
|
||||
const captchaLoading = ref(false)
|
||||
const captcha = ref<AdminCaptcha | null>(null)
|
||||
const form = reactive({
|
||||
username: 'admin',
|
||||
password: 'admin123456',
|
||||
captchaCode: '',
|
||||
})
|
||||
|
||||
onMounted(loadCaptcha)
|
||||
|
||||
async function loadCaptcha() {
|
||||
captchaLoading.value = true
|
||||
try {
|
||||
captcha.value = await fetchAdminCaptcha()
|
||||
form.captchaCode = ''
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '验证码加载失败'))
|
||||
} finally {
|
||||
captchaLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogin() {
|
||||
loading.value = true
|
||||
try {
|
||||
await adminSession.login(form.username, form.password)
|
||||
await adminSession.login(form.username, form.password, captcha.value?.captcha_id || '', form.captchaCode)
|
||||
ElMessage.success('后台登录成功')
|
||||
await router.push('/admin/dashboard')
|
||||
} catch (error) {
|
||||
ElMessage.error(readError(error, '后台登录失败'))
|
||||
await loadCaptcha()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -56,6 +75,15 @@ function readError(error: unknown, fallback: string) {
|
||||
<el-form-item label="密码">
|
||||
<el-input v-model="form.password" type="password" show-password placeholder="请输入管理员密码" @keyup.enter="handleLogin" />
|
||||
</el-form-item>
|
||||
<el-form-item label="验证码">
|
||||
<div class="admin-captcha-row">
|
||||
<el-input v-model="form.captchaCode" maxlength="4" placeholder="请输入验证码" @keyup.enter="handleLogin" />
|
||||
<button class="captcha-image-button" type="button" :disabled="captchaLoading" @click="loadCaptcha">
|
||||
<img v-if="captcha" :src="captcha.image" alt="验证码" />
|
||||
<span v-else>刷新</span>
|
||||
</button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-button class="full-control" type="primary" :loading="loading" @click="handleLogin">登录后台</el-button>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user