forked from gzt/A8000
1 changed files with 0 additions and 297 deletions
@ -1,297 +0,0 @@ |
|||||
<template> |
|
||||
<div class="login-container"> |
|
||||
<aside class="sidebar"> |
|
||||
<ul> |
|
||||
<li |
|
||||
v-for="user in userList" |
|
||||
:key="user.id" |
|
||||
:class="[ |
|
||||
'user', |
|
||||
{ selected: user.account === selectedUser?.account }, |
|
||||
]" |
|
||||
@click="selectUser(user)" |
|
||||
> |
|
||||
{{ user.account }} |
|
||||
</li> |
|
||||
</ul> |
|
||||
</aside> |
|
||||
<main class="main-content"> |
|
||||
<div class="pin-container"> |
|
||||
<p :class="['pin-prompt', { shake: isShaking }]">{{ loginStatus }}</p> |
|
||||
<div class="pin-indicators"> |
|
||||
<span |
|
||||
v-for="n in maxPinLength" |
|
||||
:key="n" |
|
||||
:class="['pin-dot', { filled: n <= pin.length }]" |
|
||||
></span> |
|
||||
</div> |
|
||||
<div class="pin-keypad"> |
|
||||
<div |
|
||||
v-for="n in 9" |
|
||||
:key="n" |
|
||||
class="key" |
|
||||
@click="inputPin(n.toString())" |
|
||||
> |
|
||||
{{ n }} |
|
||||
</div> |
|
||||
<div class="key" @click="clearPin">重输</div> |
|
||||
<div class="key" @click="inputPin('0')">0</div> |
|
||||
<div class="key" @click="submitPin">确定</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</main> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script setup lang="ts"> |
|
||||
import { onMounted, onUnmounted, ref, watch } from 'vue' |
|
||||
import { login, getUserList, isBoardParamInited } from '@/services' |
|
||||
import type { User } from '@/types/Index' |
|
||||
import { useRouter } from 'vue-router' |
|
||||
import { getServerInfo } from '@/utils/getServerInfo' |
|
||||
import { createWebSocket, DeviceContextStateMessage } from '@/websocket/socket' |
|
||||
import { ElMessage } from 'element-plus' |
|
||||
|
|
||||
const router = useRouter() |
|
||||
|
|
||||
const stateUrl = getServerInfo('/api/v1/app/ws/state') |
|
||||
const wsState = createWebSocket(stateUrl.wsUrl) |
|
||||
|
|
||||
//用户列表 |
|
||||
const userList = ref<User[]>([]) |
|
||||
//获取用户列表 |
|
||||
const getUserListData = async () => { |
|
||||
const res = await getUserList() |
|
||||
userList.value = res.data |
|
||||
} |
|
||||
|
|
||||
const handleDeviceContextState = (data: DeviceContextStateMessage['data']) => { |
|
||||
if (data.loginFlag) { |
|
||||
sessionStorage.setItem('token', JSON.stringify(data.loginUser)) |
|
||||
router.push('/index') |
|
||||
} |
|
||||
} |
|
||||
onMounted(() => { |
|
||||
wsState.subscribe<DeviceContextStateMessage>( |
|
||||
'DeviceContext', |
|
||||
handleDeviceContextState, |
|
||||
) |
|
||||
wsState.connect() |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
getUserListData() |
|
||||
}, 500) |
|
||||
}) |
|
||||
onUnmounted(() => { |
|
||||
wsState.unsubscribe<DeviceContextStateMessage>( |
|
||||
'DeviceContext', |
|
||||
handleDeviceContextState, |
|
||||
) |
|
||||
}) |
|
||||
//选中的用户 |
|
||||
const selectedUser = ref<User | null>(null) |
|
||||
// PIN 相关 |
|
||||
const pin = ref('') |
|
||||
const maxPinLength = 4 |
|
||||
const loginStatus = ref('请输入您的pin码') |
|
||||
const isShaking = ref(false) // 控制抖动效果的触发 |
|
||||
// 切换角色时清空PIN |
|
||||
const selectUser = (user: User) => { |
|
||||
selectedUser.value = user |
|
||||
pin.value = '' |
|
||||
loginStatus.value = '请输入您的pin码' |
|
||||
} |
|
||||
|
|
||||
// 输入PIN码 |
|
||||
const inputPin = (key: string) => { |
|
||||
if (selectedUser.value === null) { |
|
||||
loginStatus.value = '请选择用户' |
|
||||
return |
|
||||
} |
|
||||
if (pin.value.length >= maxPinLength) { |
|
||||
return |
|
||||
} |
|
||||
pin.value += key |
|
||||
if (pin.value.length === maxPinLength) { |
|
||||
return |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 重置PIN码 |
|
||||
const clearPin = () => { |
|
||||
pin.value = '' |
|
||||
loginStatus.value = '请输入您的pin码' |
|
||||
} |
|
||||
|
|
||||
// 点击确认验证PIN码 |
|
||||
// 模拟后端请求验证PIN |
|
||||
const submitPin = async () => { |
|
||||
if (selectedUser.value === null) { |
|
||||
loginStatus.value = '请选择用户' |
|
||||
return |
|
||||
} |
|
||||
if (pin.value.length !== maxPinLength) { |
|
||||
loginStatus.value = '请输入密码' |
|
||||
return |
|
||||
} |
|
||||
let resData = await isBoardParamInited() |
|
||||
if (!resData.data) { |
|
||||
//设备正在初始化 |
|
||||
ElMessage.warning('设备正在初始化,请稍候...') |
|
||||
return |
|
||||
} |
|
||||
const params = { |
|
||||
id: selectedUser.value?.id, |
|
||||
password: pin.value, |
|
||||
} |
|
||||
const res = await login(params) |
|
||||
if (res.success) { |
|
||||
loginStatus.value = '登录成功' |
|
||||
sessionStorage.setItem('token', JSON.stringify(res.data)) |
|
||||
await router.push('/index') |
|
||||
} else { |
|
||||
loginStatus.value = res.info |
|
||||
pin.value = '' |
|
||||
} |
|
||||
} |
|
||||
// 监听 loginStatus 变化并触发抖动效果 |
|
||||
watch(loginStatus, () => { |
|
||||
isShaking.value = true |
|
||||
setTimeout(() => { |
|
||||
isShaking.value = false |
|
||||
}, 500) // 动画持续时间为0.5秒 |
|
||||
}) |
|
||||
</script> |
|
||||
<style scoped lang="less"> |
|
||||
.login-container { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
width: 100vw; |
|
||||
height: 100vh; |
|
||||
background: linear-gradient(to bottom, #a1d0f5, #bce0fe); // 垂直线性渐变 |
|
||||
padding: 15px; // 减小padding |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.sidebar { |
|
||||
width: 220px; |
|
||||
height: 80vh; |
|
||||
background-color: #e1f0fb; |
|
||||
border-radius: 20px; |
|
||||
padding: 20px 10px; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15); |
|
||||
margin-right: 20px; |
|
||||
overflow: auto; |
|
||||
|
|
||||
ul { |
|
||||
list-style: none; |
|
||||
padding: 0; |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
.user { |
|
||||
padding: 12px 0; |
|
||||
font-size: 24px; |
|
||||
color: #666; |
|
||||
cursor: pointer; |
|
||||
transition: all 0.3s; |
|
||||
border-radius: 10px; |
|
||||
margin: 10px 0; |
|
||||
|
|
||||
&:hover { |
|
||||
background-color: #b3d9f9; |
|
||||
color: #fff; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.selected { |
|
||||
background-color: #53b6f3; |
|
||||
color: white; |
|
||||
font-weight: bold; |
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.main-content { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
|
|
||||
.pin-container { |
|
||||
background-color: #fff; |
|
||||
border-radius: 20px; |
|
||||
padding: 30px 40px; |
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2); |
|
||||
text-align: center; |
|
||||
|
|
||||
.pin-prompt { |
|
||||
font-size: 28px; |
|
||||
color: #333; |
|
||||
margin-bottom: 20px; |
|
||||
font-weight: bold; |
|
||||
|
|
||||
&.shake { |
|
||||
animation: shake 0.5s ease; |
|
||||
color: #d9534f; // 抖动时变色 |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.pin-indicators { |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
margin: 15px 0; |
|
||||
|
|
||||
.pin-dot { |
|
||||
width: 25px; |
|
||||
height: 25px; |
|
||||
background-color: #d5e6f2; |
|
||||
border-radius: 50%; |
|
||||
margin: 0 10px; |
|
||||
transition: background-color 0.3s; |
|
||||
|
|
||||
&.filled { |
|
||||
background-color: #53b6f3; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.pin-keypad { |
|
||||
display: grid; |
|
||||
grid-template-columns: repeat(3, 1fr); |
|
||||
gap: 20px; |
|
||||
margin-top: 30px; |
|
||||
|
|
||||
.key { |
|
||||
width: 90px; |
|
||||
height: 90px; |
|
||||
background-color: #f0f8ff; |
|
||||
border-radius: 15px; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
font-size: 32px; |
|
||||
color: #333; |
|
||||
cursor: pointer; |
|
||||
transition: all 0.3s; |
|
||||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15); |
|
||||
|
|
||||
&:hover { |
|
||||
background-color: #53b6f3; |
|
||||
color: #fff; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.login-status { |
|
||||
margin-top: 20px; |
|
||||
font-size: 22px; |
|
||||
color: #d9534f; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
</style> |
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue