增加登录验证码功能
This commit is contained in:
@@ -5,3 +5,5 @@ export const login = (data) => request.post('/auth/login', data)
|
||||
export const register = (data) => request.post('/auth/register', data)
|
||||
|
||||
export const getCurrentUser = () => request.get('/auth/info')
|
||||
|
||||
export const getCaptcha = () => request.get('/captcha')
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
</div>
|
||||
<template v-else>
|
||||
<div v-for="msg in currentMessages" :key="msg.id" class="message-wrapper" :class="{ self: msg.isSelf }">
|
||||
<el-popover placement="left" :width="200" trigger="hover">
|
||||
<el-popover :placement="msg.isSelf ? 'left' : 'right'" :width="240" trigger="hover">
|
||||
<template #reference>
|
||||
<el-avatar
|
||||
:size="30"
|
||||
@@ -104,9 +104,17 @@
|
||||
<span class="user-info-label">昵称:</span>
|
||||
<span class="user-info-value">{{ msg.isSelf ? (currentUserNickname || currentUserName) : (msg.fromNickname || '未知') }}</span>
|
||||
</div>
|
||||
<div class="user-info-row signature-row">
|
||||
<div class="user-info-row">
|
||||
<span class="user-info-label">电话:</span>
|
||||
<span class="user-info-value">{{ msg.isSelf ? (userStore.phone || '暂无') : (msg.fromPhone || '暂无') }}</span>
|
||||
</div>
|
||||
<div class="user-info-row">
|
||||
<span class="user-info-label">邮箱:</span>
|
||||
<span class="user-info-value">{{ msg.isSelf ? (userStore.email || '暂无') : (msg.fromEmail || '暂无') }}</span>
|
||||
</div>
|
||||
<div class="user-info-row">
|
||||
<span class="user-info-label">签名:</span>
|
||||
<div class="user-info-signature">
|
||||
<div class="user-info-signature-box">
|
||||
{{ msg.isSelf ? (userStore.signature || '暂无') : (msg.fromSignature || '暂无') }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -753,12 +761,23 @@ onUnmounted(() => { if (unsubscribeWs) unsubscribeWs() })
|
||||
.uploading-mini { display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; color: #409eff; font-size: 12px; background: #ecf5ff; border-radius: 4px; }
|
||||
.uploading-mini .el-icon { font-size: 14px; }
|
||||
|
||||
.user-info-popover { display: flex; flex-direction: column; gap: 8px; padding: 8px 0; }
|
||||
.user-info-row { display: flex; gap: 8px; font-size: 12px; line-height: 1.5; }
|
||||
.user-info-label { font-weight: 500; min-width: 50px; color: #303133; }
|
||||
.user-info-popover { display: flex; flex-direction: column; gap: 8px; padding: 4px 0; }
|
||||
.user-info-row { display: flex; align-items: flex-start; gap: 8px; font-size: 12px; line-height: 1.5; }
|
||||
.user-info-label { font-weight: 500; min-width: 50px; color: #303133; flex-shrink: 0; padding-top: 6px; }
|
||||
.user-info-value { color: #606266; word-break: break-all; flex: 1; }
|
||||
.signature-row { flex-direction: column; align-items: flex-start; }
|
||||
.user-info-signature { color: #606266; font-size: 12px; line-height: 1.4; word-break: break-all; margin-top: 4px; }
|
||||
.user-info-signature-box {
|
||||
flex: 1;
|
||||
padding: 6px 10px;
|
||||
background: #f5f7fa;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
color: #606266;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
word-break: break-all;
|
||||
max-height: 80px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.chat-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #909399; gap: 12px; }
|
||||
.chat-empty p { margin: 0; font-size: 14px; }
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<el-upload
|
||||
drag
|
||||
multiple
|
||||
:limit="11"
|
||||
:limit="31"
|
||||
:auto-upload="false"
|
||||
:on-change="handleChange"
|
||||
:on-exceed="handleExceed"
|
||||
@@ -17,7 +17,7 @@
|
||||
:show-file-list="false"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><UploadFilled /></el-icon>
|
||||
<div class="el-upload__text">拖拽文件到此处,或<em>点击选择</em>(最多10个)</div>
|
||||
<div class="el-upload__text">拖拽文件到此处,或<em>点击选择</em>(最多30个)</div>
|
||||
</el-upload>
|
||||
|
||||
<div class="storage-info">
|
||||
@@ -98,16 +98,16 @@ const isOverSizeLimit = computed(() => {
|
||||
})
|
||||
|
||||
const handleChange = (file, list) => {
|
||||
if (list.length > 10) {
|
||||
ElMessage.warning('最多一次上传10个文件')
|
||||
fileList.value = list.slice(0, 10)
|
||||
if (list.length > 30) {
|
||||
ElMessage.warning('最多一次上传30个文件')
|
||||
fileList.value = list.slice(0, 30)
|
||||
return
|
||||
}
|
||||
fileList.value = list
|
||||
}
|
||||
|
||||
const handleExceed = () => {
|
||||
ElMessage.warning('最多一次上传10个文件')
|
||||
ElMessage.warning('最多一次上传30个文件')
|
||||
}
|
||||
|
||||
const removeFile = (index) => {
|
||||
|
||||
@@ -19,48 +19,90 @@
|
||||
<template #prefix><el-icon><Lock /></el-icon></template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="captcha">
|
||||
<div class="captcha-row">
|
||||
<el-input
|
||||
v-model="form.captcha"
|
||||
placeholder="请输入验证码"
|
||||
clearable
|
||||
@keyup.enter="handleLogin"
|
||||
>
|
||||
<template #prefix><el-icon><Key /></el-icon></template>
|
||||
</el-input>
|
||||
<img
|
||||
:src="captchaImage"
|
||||
class="captcha-img"
|
||||
@click="refreshCaptcha"
|
||||
title="点击刷新验证码"
|
||||
/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
:disabled="!isFormComplete"
|
||||
native-type="submit"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-icon><Right /></el-icon>
|
||||
<span style="margin-left: 4px">登录</span>
|
||||
<template v-if="loading">
|
||||
<span>登录中...</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-icon><Right /></el-icon>
|
||||
<span style="margin-left: 4px">登录</span>
|
||||
</template>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { User, Lock, Right } from '@element-plus/icons-vue'
|
||||
import { login } from '@/api/auth'
|
||||
import { User, Lock, Right, Key } from '@element-plus/icons-vue'
|
||||
import { login, getCaptcha } from '@/api/auth'
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
const formRef = ref(null)
|
||||
const loading = ref(false)
|
||||
const captchaImage = ref('')
|
||||
|
||||
const form = reactive({ username: '', password: '' })
|
||||
const form = reactive({ username: '', password: '', captcha: '' })
|
||||
|
||||
// 表单是否填写完整
|
||||
const isFormComplete = computed(() => {
|
||||
return form.username.trim() && form.password.trim() && form.captcha.trim()
|
||||
})
|
||||
|
||||
const rules = {
|
||||
username: [{ required: true, message: '请输入账号', trigger: 'blur' }],
|
||||
password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
|
||||
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
||||
captcha: [{ required: true, message: '请输入验证码', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const refreshCaptcha = async () => {
|
||||
try {
|
||||
const res = await getCaptcha()
|
||||
captchaImage.value = res.data.image
|
||||
} catch (e) {
|
||||
console.error('获取验证码失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogin = async () => {
|
||||
const valid = await formRef.value.validate().catch(() => false)
|
||||
if (!valid || loading.value) return
|
||||
if (!isFormComplete.value || loading.value) return
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await login(form)
|
||||
// res.data = { token, user }
|
||||
ElMessage.success('登录成功,正在跳转...')
|
||||
emit('success', res.data)
|
||||
} catch (e) {
|
||||
// 登录失败刷新验证码
|
||||
refreshCaptcha()
|
||||
form.captcha = ''
|
||||
if (!e.response) {
|
||||
ElMessage.error('后端服务不可用,请稍后重试')
|
||||
} else if (e.response.status >= 500) {
|
||||
@@ -72,13 +114,36 @@ const handleLogin = async () => {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
refreshCaptcha()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-tips {
|
||||
text-align: center;
|
||||
color: #c0c4cc;
|
||||
font-size: 12px;
|
||||
margin-top: -8px;
|
||||
.captcha-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.captcha-row .el-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.captcha-img {
|
||||
width: 140px;
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dcdfe6;
|
||||
object-fit: fill;
|
||||
background: #fff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.captcha-img:hover {
|
||||
border-color: #409eff;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user