登录模块审核

This commit is contained in:
Shen Wei
2026-04-30 18:40:55 +08:00
parent 4030a91100
commit 57600598ac
34 changed files with 2544 additions and 2431 deletions

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1280">
<title>Fonrey 登录管理 · 静态原型</title>
<title>Fonrey 登录管理 · Tenant 识别Story 1</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<script>
@@ -12,36 +12,17 @@
extend: {
colors: {
primary: {
50: '#F0FDFA',
100: '#CCFBF1',
200: '#99F6E4',
500: '#14B8A6',
600: '#0F766E',
700: '#115E59',
800: '#134E4A'
50: '#F0FDFA', 100: '#CCFBF1', 200: '#99F6E4',
500: '#14B8A6', 600: '#0F766E', 700: '#115E59', 800: '#134E4A'
},
neutral: {
50: '#F8FAFC',
100: '#F1F5F9',
200: '#E2E8F0',
300: '#CBD5E1',
400: '#94A3B8',
500: '#64748B',
600: '#475569',
700: '#334155',
800: '#1E293B',
900: '#0F172A'
50: '#F8FAFC', 100: '#F1F5F9', 200: '#E2E8F0', 300: '#CBD5E1',
400: '#94A3B8', 500: '#64748B', 600: '#475569', 700: '#334155',
800: '#1E293B', 900: '#0F172A'
},
success: { 50: '#F0FDF4', 600: '#16A34A' },
warning: { 50: '#FFFBEB', 600: '#D97706' },
danger: { 50: '#FEF2F2', 600: '#DC2626' },
info: { 50: '#EFF6FF', 600: '#2563EB' }
},
boxShadow: {
xs: '0 1px 2px rgba(15,23,42,0.04)'
},
fontFamily: {
sans: ['Inter', 'PingFang SC', 'Microsoft YaHei', 'sans-serif']
danger: { 50: '#FEF2F2', 600: '#DC2626' }
}
}
}
@@ -49,12 +30,9 @@
</script>
<style>
[x-cloak] { display: none !important; }
.tabular-nums { font-variant-numeric: tabular-nums; }
.captcha-track { background: linear-gradient(90deg, #E2E8F0 0%, #F1F5F9 100%); }
.captcha-success { background: linear-gradient(90deg, #F0FDF4 0%, #16A34A 100%); }
</style>
</head>
<body class="min-h-screen bg-neutral-50 font-sans text-sm text-neutral-700 antialiased" x-data="loginPrototype()">
<body class="min-h-screen bg-neutral-50 font-sans text-sm text-neutral-700 antialiased" x-data="tenantVerifyPage()">
<div class="fixed inset-0 -z-10">
<div class="absolute inset-y-0 left-0 w-[56%] bg-gradient-to-br from-primary-800 via-primary-700 to-primary-600"></div>
<div class="absolute -top-16 -left-24 w-96 h-96 rounded-full bg-white/10 blur-2xl"></div>
@@ -69,369 +47,126 @@
<span class="text-base font-semibold">Fonrey 房睿</span>
</div>
<h1 class="mt-8 text-4xl font-semibold leading-tight">面向经纪业务的<br>高密度工作台</h1>
<h1 class="mt-8 text-4xl font-semibold leading-tight">欢迎使用 Fonrey 房睿</h1>
<p class="mt-4 text-primary-100 text-base leading-7 max-w-xl">
多租户隔离、角色权限控制、房客源高频操作一致体验
本页面原型覆盖 Tenant 识别、账号密码登录、验证码验证、锁定与会话过期等 P0 场景
首次启动客户端时,请先输入 12 位公司识别码完成租户识别
识别成功后将自动进入该租户的登录页面
</p>
</div>
<div class="grid grid-cols-2 gap-4 max-w-2xl">
<div class="rounded-lg border border-white/20 bg-white/10 p-4">
<div class="text-xs text-primary-100">多租户识别</div>
<div class="mt-1 text-xl font-semibold tabular-nums">12位 Tenant ID</div>
<div class="text-xs text-primary-100">识别码格式</div>
<div class="mt-1 text-xl font-semibold">12 位纯数字</div>
</div>
<div class="rounded-lg border border-white/20 bg-white/10 p-4">
<div class="text-xs text-primary-100">安全策略</div>
<div class="mt-1 text-xl font-semibold tabular-nums">5次失败锁定30分钟</div>
<div class="mt-1 text-xl font-semibold">公共接口限流保护</div>
</div>
</div>
</section>
<section class="col-span-5 px-10 py-10 flex items-center justify-center">
<div class="w-full max-w-md rounded-xl bg-white border border-neutral-200 shadow-lg p-6">
<h2 class="text-xl font-semibold text-neutral-800">Tenant 识别</h2>
<p class="mt-2 text-sm text-neutral-500">请输入您公司的专属识别码以继续</p>
<template x-if="view === 'tenant'">
<div x-cloak>
<h2 class="text-xl font-semibold text-neutral-800">欢迎使用 Fonrey 房睿</h2>
<p class="mt-2 text-sm text-neutral-500">请输入您公司的专属识别码,以进入对应租户登录页</p>
<div class="mt-6 space-y-1.5">
<label for="tenant-code" class="block text-sm font-medium text-neutral-700">公司识别码Tenant Code<span class="text-danger-600">*</span></label>
<input
id="tenant-code"
type="text"
inputmode="numeric"
maxlength="12"
:disabled="loading"
x-model="tenantCode"
@input="sanitizeTenantCode"
placeholder="请输入12位数字识别码"
class="block w-full px-3 py-2 text-sm rounded-md border border-neutral-300 placeholder:text-neutral-400 focus:outline-none focus:border-primary-600 focus:ring-2 focus:ring-primary-600/20 disabled:bg-neutral-100 disabled:text-neutral-400"
aria-describedby="tenant-help tenant-error"
>
<p id="tenant-help" class="text-xs text-neutral-500">支持粘贴,系统将自动去除前后空格并过滤非数字字符</p>
<div class="min-h-[22px]">
<p id="tenant-error" x-show="errorText" x-text="errorText" class="text-xs text-danger-600"></p>
</div>
</div>
<div class="mt-6 space-y-1.5">
<label for="tenant-id" class="block text-sm font-medium text-neutral-700">公司识别码Tenant ID<span class="text-danger-600">*</span></label>
<input
id="tenant-id"
type="text"
inputmode="numeric"
maxlength="12"
:disabled="tenantLoading"
x-model="tenantId"
@input="sanitizeTenantId"
placeholder="请输入12位数字识别码"
class="block w-full px-3 py-2 text-sm rounded-md border border-neutral-300 placeholder:text-neutral-400 focus:outline-none focus:border-primary-600 focus:ring-2 focus:ring-primary-600/20 disabled:bg-neutral-100 disabled:text-neutral-400"
aria-describedby="tenant-help tenant-error"
>
<p id="tenant-help" class="text-xs text-neutral-500">支持粘贴,系统将自动去除空格与非数字字符</p>
<div class="min-h-[22px]">
<p id="tenant-error" x-show="tenantError" x-text="tenantError" class="text-xs text-danger-600"></p>
</div>
</div>
<button
type="button"
@click="submitTenantCode"
:disabled="loading"
class="mt-1 inline-flex w-full items-center justify-center gap-1.5 px-4 py-2 text-sm font-medium rounded-md bg-primary-600 text-white hover:bg-primary-700 active:bg-primary-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-600/40 disabled:opacity-50 disabled:cursor-not-allowed"
>
<svg x-show="loading" class="w-4 h-4 animate-spin" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M16.75 7.25a6.5 6.5 0 1 0 0 9.5"/></svg>
<span x-text="loading ? '识别中…' : '确认'"></span>
</button>
<button
type="button"
@click="submitTenant"
:disabled="tenantLoading"
class="mt-1 inline-flex w-full items-center justify-center gap-1.5 px-4 py-2 text-sm font-medium rounded-md bg-primary-600 text-white hover:bg-primary-700 active:bg-primary-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-600/40 disabled:opacity-50 disabled:cursor-not-allowed"
>
<svg x-show="tenantLoading" class="w-4 h-4 animate-spin" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M16.75 7.25a6.5 6.5 0 1 0 0 9.5"/></svg>
<span x-text="tenantLoading ? '识别中…' : '确认'"></span>
</button>
<template x-if="tenantNetworkError">
<div class="mt-3 rounded-md border border-warning-600/30 bg-warning-50 px-3 py-2 text-xs text-warning-600 flex items-center justify-between">
<span>网络连接失败,请检查网络后重试</span>
<button @click="tenantNetworkError=false" class="text-primary-600 hover:underline">重试</button>
</div>
</template>
<p class="mt-4 text-xs text-neutral-500">不知道识别码?请联系您公司的系统管理员</p>
<template x-if="networkError">
<div class="mt-3 rounded-md border border-warning-600/30 bg-warning-50 px-3 py-2 text-xs text-warning-600 flex items-center justify-between gap-2">
<span>网络连接失败,请检查网络后重试</span>
<button type="button" @click="networkError=false" class="text-primary-600 hover:underline">重试</button>
</div>
</template>
<template x-if="view === 'login'">
<div x-cloak>
<div class="flex items-center justify-between mb-4">
<div class="min-w-0">
<p class="text-xs text-neutral-500">正在登录</p>
<p class="text-sm font-semibold text-neutral-800 truncate" x-text="tenantName"></p>
</div>
<button type="button" @click="openSwitchModal = true" class="text-sm text-primary-600 hover:text-primary-700 hover:underline underline-offset-2">切换公司</button>
</div>
<template x-if="sessionExpiredNotice">
<div class="mb-3 rounded-md border border-warning-600/30 bg-warning-50 px-3 py-2 text-xs text-warning-600 flex items-start justify-between gap-2">
<span>登录已过期,请重新登录</span>
<button @click="sessionExpiredNotice=false" class="text-neutral-500 hover:text-neutral-700" aria-label="关闭会话过期提示"></button>
</div>
</template>
<h2 class="text-xl font-semibold text-neutral-800">账号登录</h2>
<p class="mt-1 text-sm text-neutral-500">请输入用户名和密码,并完成行为验证</p>
<form class="mt-5 space-y-4" @submit.prevent="submitLogin">
<div class="space-y-1">
<label for="username" class="block text-sm font-medium text-neutral-700">用户名<span class="text-danger-600">*</span></label>
<input id="username" type="text" x-model.trim="username" placeholder="请输入用户名"
class="block w-full px-3 py-2 text-sm rounded-md border border-neutral-300 placeholder:text-neutral-400 focus:outline-none focus:border-primary-600 focus:ring-2 focus:ring-primary-600/20"
:disabled="loginLoading || accountState==='locked' || accountState==='disabled'">
</div>
<div class="space-y-1">
<label for="password" class="block text-sm font-medium text-neutral-700">密码<span class="text-danger-600">*</span></label>
<div class="relative">
<input id="password" :type="passwordVisible ? 'text' : 'password'" x-model="password" placeholder="请输入密码"
class="block w-full px-3 py-2 pr-10 text-sm rounded-md border border-neutral-300 placeholder:text-neutral-400 focus:outline-none focus:border-primary-600 focus:ring-2 focus:ring-primary-600/20"
:disabled="loginLoading || accountState==='locked' || accountState==='disabled'">
<button type="button" @click="passwordVisible=!passwordVisible" class="absolute right-2 top-1/2 -translate-y-1/2 p-1 text-neutral-500 hover:text-neutral-700" aria-label="显示或隐藏密码">
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.644C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.009 9.963 7.178.07.207.07.431 0 .638C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.009-9.964-7.178Z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/></svg>
</button>
</div>
</div>
<div class="space-y-2">
<div class="flex items-center justify-between">
<label class="block text-sm font-medium text-neutral-700">行为验证<span class="text-danger-600">*</span></label>
<button type="button" @click="refreshCaptcha" class="inline-flex items-center gap-1 text-xs text-neutral-500 hover:text-neutral-700" aria-label="刷新验证码">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182"/></svg>
刷新
</button>
</div>
<div class="rounded-lg border border-neutral-200 p-3">
<div class="relative h-24 rounded-md overflow-hidden bg-gradient-to-r from-neutral-100 to-neutral-200">
<div class="absolute inset-0 bg-[radial-gradient(circle_at_20%_30%,rgba(15,118,110,0.18),transparent_45%),radial-gradient(circle_at_80%_70%,rgba(37,99,235,0.15),transparent_45%)]"></div>
<div class="absolute top-7 h-10 w-9 rounded bg-neutral-300/90 border border-neutral-400/70" :style="`left:${captchaTarget}%`"></div>
<div class="absolute top-7 h-10 w-9 rounded border border-primary-600/70 bg-primary-100/90 transition-all" :style="`left: calc(${sliderValue}% - 18px)`"></div>
</div>
<div class="mt-2 rounded-md p-2 border"
:class="captchaState==='pass' ? 'captcha-success border-success-600/30' : 'captcha-track border-neutral-200'">
<input type="range" min="0" max="100" step="1" x-model="sliderValue" @change="verifyCaptcha" @input="captchaState='idle'"
:disabled="captchaState==='pass' || loginLoading || accountState==='locked' || accountState==='disabled'"
class="w-full accent-primary-600">
</div>
<p class="mt-1 text-xs"
:class="captchaState==='pass' ? 'text-success-600' : (captchaState==='fail' ? 'text-danger-600' : 'text-neutral-500')"
x-text="captchaState==='pass' ? '验证通过' : (captchaState==='fail' ? '验证失败,请重试' : '拖动滑块完成拼图')"></p>
</div>
</div>
<template x-if="loginError">
<div class="rounded-md border border-danger-600/30 bg-danger-50 px-3 py-2 text-xs text-danger-600" x-text="loginError"></div>
</template>
<button type="submit"
class="inline-flex w-full items-center justify-center gap-1.5 px-4 py-2 text-sm font-medium rounded-md bg-primary-600 text-white hover:bg-primary-700 active:bg-primary-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-600/40 disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="!canSubmit || loginLoading || accountState==='locked' || accountState==='disabled'">
<svg x-show="loginLoading" class="w-4 h-4 animate-spin" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M16.75 7.25a6.5 6.5 0 1 0 0 9.5"/></svg>
<span x-text="loginLoading ? '登录中…' : '登录'"></span>
</button>
</form>
<div class="mt-3 flex items-center justify-between text-xs">
<a href="javascript:void(0)" class="text-primary-600 hover:text-primary-700 hover:underline underline-offset-2">忘记用户名</a>
<a href="javascript:void(0)" class="text-primary-600 hover:text-primary-700 hover:underline underline-offset-2">忘记密码</a>
</div>
<div class="mt-4 border-t border-neutral-200 pt-4 space-y-2">
<button type="button" disabled class="w-full px-3 py-2 rounded-md border border-neutral-300 bg-neutral-100 text-neutral-400 cursor-not-allowed text-sm">手机验证码登录(即将开放)</button>
<button type="button" disabled class="w-full px-3 py-2 rounded-md border border-neutral-300 bg-neutral-100 text-neutral-400 cursor-not-allowed text-sm">微信扫码登录(即将开放)</button>
</div>
<details class="mt-4 rounded-md border border-neutral-200 bg-neutral-50 p-2">
<summary class="cursor-pointer text-xs text-neutral-500">原型状态切换(仅评审演示)</summary>
<div class="mt-2 grid grid-cols-2 gap-2 text-xs">
<button @click="simulateInvalidCredential" class="px-2 py-1.5 rounded border border-neutral-300 bg-white hover:bg-neutral-100">模拟账号密码错误</button>
<button @click="simulateLock" class="px-2 py-1.5 rounded border border-neutral-300 bg-white hover:bg-neutral-100">模拟账号锁定</button>
<button @click="simulateDisabled" class="px-2 py-1.5 rounded border border-neutral-300 bg-white hover:bg-neutral-100">模拟账号停用</button>
<button @click="sessionExpiredNotice=true" class="px-2 py-1.5 rounded border border-neutral-300 bg-white hover:bg-neutral-100">模拟会话过期</button>
<button @click="resetLoginState" class="col-span-2 px-2 py-1.5 rounded border border-neutral-300 bg-white hover:bg-neutral-100">重置状态</button>
</div>
</details>
</div>
<template x-if="successText">
<div class="mt-3 rounded-md border border-success-600/30 bg-success-50 px-3 py-2 text-xs text-success-600" x-text="successText"></div>
</template>
<p class="mt-4 text-xs text-neutral-500">不知道识别码请联系您公司的Tenant Admin租户管理员</p>
</div>
</section>
</main>
<div x-show="openSwitchModal" x-cloak class="fixed inset-0 z-50" @keydown.escape.window="openSwitchModal=false">
<div class="absolute inset-0 bg-neutral-900/40" @click="openSwitchModal=false"></div>
<div class="absolute inset-0 flex items-center justify-center p-4 pointer-events-none">
<div class="w-full max-w-sm bg-white rounded-xl shadow-lg border border-neutral-200 pointer-events-auto">
<div class="p-5 text-center space-y-3">
<div class="mx-auto w-12 h-12 rounded-full bg-warning-50 flex items-center justify-center">
<svg class="w-6 h-6 text-warning-600" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9.303 3.376c.866 1.5-.217 3.374-1.948 3.374H4.646c-1.73 0-2.813-1.874-1.948-3.374l7.354-12.748c.866-1.5 3.03-1.5 3.896 0l7.355 12.748Z"/><path stroke-linecap="round" stroke-linejoin="round" d="M12 16.5h.008v.008H12v-.008Z"/></svg>
</div>
<h3 class="text-base font-semibold text-neutral-800">切换公司</h3>
<p class="text-sm text-neutral-500">切换公司将清除当前租户识别信息,并返回识别页。是否继续?</p>
</div>
<div class="flex items-center justify-center gap-2 px-5 py-3 border-t border-neutral-200 bg-neutral-50">
<button @click="openSwitchModal=false" class="px-4 py-1.5 text-sm border border-neutral-300 rounded-md bg-white hover:bg-neutral-50">取消</button>
<button @click="confirmSwitchCompany" class="px-4 py-1.5 text-sm rounded-md bg-danger-600 text-white hover:bg-danger-600/90">继续切换</button>
</div>
</div>
</div>
</div>
<script>
function loginPrototype() {
function tenantVerifyPage() {
return {
view: 'tenant',
tenantId: '',
tenantName: '',
tenantLoading: false,
tenantError: '',
tenantNetworkError: false,
tenantCode: '',
loading: false,
networkError: false,
errorText: '',
successText: '',
username: '',
password: '',
passwordVisible: false,
captchaTarget: 46,
sliderValue: 0,
captchaState: 'idle',
loginLoading: false,
loginError: '',
accountState: 'active',
failedCount: 0,
sessionExpiredNotice: false,
openSwitchModal: false,
sanitizeTenantId() {
this.tenantId = this.tenantId.replace(/\D/g, '').slice(0, 12)
this.tenantError = ''
this.tenantNetworkError = false
sanitizeTenantCode() {
this.tenantCode = this.tenantCode.replace(/\s+/g, '').replace(/\D/g, '').slice(0, 12)
this.errorText = ''
this.networkError = false
this.successText = ''
},
submitTenant() {
this.tenantError = ''
this.tenantNetworkError = false
submitTenantCode() {
this.errorText = ''
this.networkError = false
this.successText = ''
if (this.tenantId.length !== 12) {
this.tenantError = '识别码须为 12 位数字'
if (this.tenantCode.length !== 12) {
this.errorText = '识别码须为 12 位数字'
return
}
this.tenantLoading = true
setTimeout(() => {
this.tenantLoading = false
this.loading = true
if (this.tenantId === '999999999999') {
this.tenantNetworkError = true
setTimeout(() => {
this.loading = false
if (this.tenantCode === '999999999999') {
this.networkError = true
return
}
if (this.tenantId === '202500010001') {
this.tenantName = '沪居地产(演示租户)'
localStorage.setItem('tenant_id', this.tenantId)
localStorage.setItem('tenant_name', this.tenantName)
// 串联到 Story 2 独立登录页
this.view = 'login'
if (this.tenantCode === '202500010001') {
const tenantName = '沪居地产(演示租户)'
localStorage.setItem('tenant_code', this.tenantCode)
localStorage.setItem('tenant_name', tenantName)
this.successText = `识别成功,正在登录:${tenantName}`
setTimeout(() => {
window.location.href = `./登录_账号密码_UI.html?tenantId=${this.tenantId}&tenantName=${encodeURIComponent(this.tenantName)}`
}, 350)
this.resetLoginState()
} else {
this.tenantError = '识别码无效,请联系您的系统管理员获取正确的识别码'
window.location.href = `./登录_账号密码_UI.html?tenantCode=${this.tenantCode}&tenantName=${encodeURIComponent(tenantName)}`
}, 500)
return
}
this.errorText = '识别码无效请联系您的Tenant Admin租户管理员获取正确的识别码'
}, 800)
},
refreshCaptcha() {
this.captchaTarget = Math.floor(Math.random() * 60) + 20
this.sliderValue = 0
this.captchaState = 'idle'
},
verifyCaptcha() {
const diff = Math.abs(this.sliderValue - this.captchaTarget)
if (diff <= 3) {
this.captchaState = 'pass'
this.loginError = ''
} else {
this.captchaState = 'fail'
setTimeout(() => this.refreshCaptcha(), 700)
}
},
get canSubmit() {
return this.username.trim() && this.password && this.captchaState === 'pass'
},
submitLogin() {
this.loginError = ''
if (this.accountState === 'locked') {
this.loginError = '账号已被临时锁定,请 30 分钟后重试,或联系管理员解锁'
return
}
if (this.accountState === 'disabled') {
this.loginError = '账号已停用,请联系您的管理员'
return
}
if (!this.canSubmit) {
this.loginError = '请先完成用户名、密码和行为验证'
return
}
this.loginLoading = true
setTimeout(() => {
this.loginLoading = false
const credentialPass = this.username === 'agent_001' && this.password === 'Fonrey@2025'
if (credentialPass) {
this.loginError = ''
this.failedCount = 0
this.sessionExpiredNotice = false
this.password = ''
this.refreshCaptcha()
// 静态原型串联:登录成功后跳转到主页(当前用房源列表页作为首页)
setTimeout(() => {
window.location.href = './房源列表_UI.html?from=login&login=success'
}, 350)
return
}
this.failedCount += 1
this.loginError = '用户名或密码错误,请重新输入'
this.password = ''
this.refreshCaptcha()
if (this.failedCount >= 5) {
this.accountState = 'locked'
this.loginError = '账号已被临时锁定,请 30 分钟后重试,或联系管理员解锁'
}
}, 900)
},
simulateInvalidCredential() {
this.loginError = '用户名或密码错误,请重新输入'
this.password = ''
this.refreshCaptcha()
},
simulateLock() {
this.accountState = 'locked'
this.loginError = '账号已被临时锁定,请 30 分钟后重试,或联系管理员解锁'
},
simulateDisabled() {
this.accountState = 'disabled'
this.loginError = '账号已停用,请联系您的管理员'
},
resetLoginState() {
this.username = ''
this.password = ''
this.passwordVisible = false
this.loginLoading = false
this.loginError = ''
this.failedCount = 0
this.accountState = 'active'
this.sessionExpiredNotice = false
this.refreshCaptcha()
},
confirmSwitchCompany() {
this.openSwitchModal = false
this.view = 'tenant'
this.tenantName = ''
this.tenantId = ''
this.tenantError = ''
this.tenantNetworkError = false
this.resetLoginState()
}
}
}