Files
VibeCoding/WebRTCControllerWeb/src/components/LoginPanel.vue
tongtongstudio 6eb2c7321a feat(controlled): 实现设备激活与安全认证流程
- 添加API客户端、加密存储和provision/token激活逻辑
- WebSocket改用Bearer令牌认证,移除REGISTER请求
- 设备ID改为服务端下发,支持令牌刷新和强制下线处理
- 新增deviceSecret加密存储和accessToken自动刷新
- 更新设备ID获取方式为出厂SN,添加安全存储依赖
2026-08-01 15:13:12 +08:00

104 lines
3.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { ref } from 'vue';
import { store, login, register, connectSignaling } from '../store/controllerStore';
const username = ref('');
const password = ref('');
const showRegister = ref(false);
const regUsername = ref('');
const regPassword = ref('');
const regBusy = ref(false);
const regError = ref('');
async function onLogin() {
const ok = await login(username.value.trim(), password.value);
if (ok) {
await connectSignaling();
}
}
async function onRegister() {
regError.value = '';
regBusy.value = true;
try {
await register({ username: regUsername.value.trim(), password: regPassword.value });
// 注册成功后直接登录并连接。
username.value = regUsername.value.trim();
password.value = regPassword.value;
showRegister.value = false;
await onLogin();
} catch (e) {
regError.value = e.message || '注册失败';
} finally {
regBusy.value = false;
}
}
</script>
<template>
<div class="login-mask">
<div class="login-card">
<div class="login-brand">WebRTC 网页远程控制</div>
<div class="login-sub">登录后才能连接信令服务器并发起远程控制</div>
<form v-if="!showRegister" class="login-form" @submit.prevent="onLogin">
<label>用户名
<input v-model="username" type="text" autocomplete="username" placeholder="请输入用户名" />
</label>
<label>密码
<input v-model="password" type="password" autocomplete="current-password" placeholder="请输入密码" />
</label>
<p v-if="store.loginError" class="login-err">{{ store.loginError }}</p>
<button class="btn primary" type="submit" :disabled="store.loginBusy">
{{ store.loginBusy ? '登录中…' : '登录' }}
</button>
<button class="btn link" type="button" @click="showRegister = true">没有账号注册</button>
</form>
<form v-else class="login-form" @submit.prevent="onRegister">
<label>用户名
<input v-model="regUsername" type="text" autocomplete="username" placeholder="设置用户名" />
</label>
<label>密码
<input v-model="regPassword" type="password" autocomplete="new-password" placeholder="设置密码至少12位含大小写/数字/特殊字符)" />
</label>
<p v-if="regError" class="login-err">{{ regError }}</p>
<button class="btn primary" type="submit" :disabled="regBusy">
{{ regBusy ? '注册中…' : '注册并登录' }}
</button>
<button class="btn link" type="button" @click="showRegister = false">返回登录</button>
</form>
</div>
</div>
</template>
<style scoped>
.login-mask {
position: fixed; inset: 0;
display: flex; align-items: center; justify-content: center;
background: radial-gradient(circle at 30% 20%, #1e293b, #0f172a 70%);
}
.login-card {
width: 360px; padding: 32px;
background: #1e293b; border: 1px solid #334155; border-radius: 16px;
box-shadow: 0 20px 60px rgba(0,0,0,.45); color: #e2e8f0;
}
.login-brand { font-size: 20px; font-weight: 700; color: #38bdf8; }
.login-sub { margin: 8px 0 20px; font-size: 13px; color: #94a3b8; }
.login-form { display: flex; flex-direction: column; gap: 14px; }
.login-form label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: #cbd5e1; }
.login-form input {
padding: 10px 12px; border-radius: 8px; border: 1px solid #334155;
background: #0f172a; color: #e2e8f0; font-size: 14px;
}
.login-form input:focus { outline: none; border-color: #38bdf8; }
.login-err { color: #f87171; font-size: 13px; margin: 0; }
.btn.primary {
padding: 11px; border: none; border-radius: 8px; cursor: pointer;
background: #38bdf8; color: #0f172a; font-weight: 600; font-size: 14px;
}
.btn.primary:disabled { opacity: .6; cursor: default; }
.btn.link { background: none; border: none; color: #94a3b8; cursor: pointer; font-size: 13px; }
.btn.link:hover { color: #38bdf8; }
</style>