248 lines
12 KiB
HTML
248 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN" data-theme="light">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=1280">
|
||
<title>Fonrey 登录管理 · 设置新密码</title>
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
||
<script>
|
||
tailwind.config = {
|
||
theme: {
|
||
extend: {
|
||
colors: {
|
||
primary: {
|
||
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'
|
||
},
|
||
success: { 50: '#F0FDF4', 600: '#16A34A' },
|
||
warning: { 50: '#FFFBEB', 600: '#D97706' },
|
||
danger: { 50: '#FEF2F2', 600: '#DC2626' }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
<style>
|
||
[x-cloak] { display: none !important; }
|
||
</style>
|
||
</head>
|
||
<body class="min-h-screen bg-neutral-50 font-sans text-sm text-neutral-700 antialiased" x-data="passwordResetPage()" x-init="init()">
|
||
<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>
|
||
<div class="absolute bottom-0 left-1/3 w-80 h-80 rounded-full bg-primary-200/15 blur-2xl"></div>
|
||
</div>
|
||
|
||
<main class="mx-auto max-w-[1440px] min-h-screen grid grid-cols-12">
|
||
<section class="col-span-7 px-12 py-12 text-white flex flex-col justify-between">
|
||
<div>
|
||
<div class="inline-flex items-center gap-2 px-3 py-2 rounded-lg bg-white/10 border border-white/20">
|
||
<div class="w-7 h-7 rounded-md bg-primary-500/90 flex items-center justify-center text-white font-semibold">F</div>
|
||
<span class="text-base font-semibold">Fonrey 房睿</span>
|
||
</div>
|
||
|
||
<h1 class="mt-8 text-4xl font-semibold leading-tight" x-text="pageTitle"></h1>
|
||
<p class="mt-4 text-primary-100 text-base leading-7 max-w-xl" x-text="pageDesc"></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">至少 8 位,含字母+数字</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" x-text="mode === 'initial' ? '首次登录强制修改' : '找回密码重置'"></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" x-text="pageTitle"></h2>
|
||
<p class="mt-2 text-sm text-neutral-500" x-text="pageDesc"></p>
|
||
|
||
<form class="mt-5 space-y-4" @submit.prevent="submitReset">
|
||
<div class="space-y-1">
|
||
<label for="new-password" class="block text-sm font-medium text-neutral-700">新密码<span class="text-danger-600">*</span></label>
|
||
<div class="relative">
|
||
<input
|
||
id="new-password"
|
||
:type="newPasswordVisible ? 'text' : 'password'"
|
||
x-model="newPassword"
|
||
@input="validatePassword"
|
||
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="submitting"
|
||
>
|
||
<button type="button" @click="newPasswordVisible=!newPasswordVisible" 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>
|
||
<p x-show="newPasswordError" x-text="newPasswordError" class="text-xs text-danger-600"></p>
|
||
</div>
|
||
|
||
<div class="space-y-1">
|
||
<label for="confirm-password" class="block text-sm font-medium text-neutral-700">确认新密码<span class="text-danger-600">*</span></label>
|
||
<div class="relative">
|
||
<input
|
||
id="confirm-password"
|
||
:type="confirmPasswordVisible ? 'text' : 'password'"
|
||
x-model="confirmPassword"
|
||
@input="validatePassword"
|
||
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="submitting"
|
||
>
|
||
<button type="button" @click="confirmPasswordVisible=!confirmPasswordVisible" 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>
|
||
<p x-show="confirmPasswordError" x-text="confirmPasswordError" class="text-xs text-danger-600"></p>
|
||
</div>
|
||
|
||
<div class="rounded-lg border border-neutral-200 bg-neutral-50 p-3 space-y-2">
|
||
<p class="text-xs font-medium text-neutral-700">密码强度校验</p>
|
||
<div class="text-xs flex items-center gap-2" :class="rules.minLength ? 'text-success-600' : 'text-neutral-500'">
|
||
<span x-text="rules.minLength ? '✓' : '✗'"></span>
|
||
<span>长度至少 8 位</span>
|
||
</div>
|
||
<div class="text-xs flex items-center gap-2" :class="rules.hasLetter ? 'text-success-600' : 'text-neutral-500'">
|
||
<span x-text="rules.hasLetter ? '✓' : '✗'"></span>
|
||
<span>包含字母</span>
|
||
</div>
|
||
<div class="text-xs flex items-center gap-2" :class="rules.hasNumber ? 'text-success-600' : 'text-neutral-500'">
|
||
<span x-text="rules.hasNumber ? '✓' : '✗'"></span>
|
||
<span>包含数字</span>
|
||
</div>
|
||
</div>
|
||
|
||
<template x-if="globalError">
|
||
<div class="rounded-md border border-danger-600/30 bg-danger-50 px-3 py-2 text-xs text-danger-600" x-text="globalError"></div>
|
||
</template>
|
||
|
||
<template x-if="successMessage">
|
||
<div class="rounded-md border border-success-600/30 bg-success-50 px-3 py-2 text-xs text-success-600" x-text="successMessage"></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="submitting"
|
||
x-text="submitting ? '提交中…' : submitText"
|
||
></button>
|
||
</form>
|
||
|
||
<div class="mt-4 text-xs text-neutral-500" x-show="mode === 'recover'">
|
||
<a href="./登录_账号密码_UI.html" class="text-primary-600 hover:underline">返回登录页</a>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<script>
|
||
function passwordResetPage() {
|
||
return {
|
||
mode: 'recover', // initial | recover
|
||
pageTitle: '',
|
||
pageDesc: '',
|
||
submitText: '',
|
||
|
||
newPassword: '',
|
||
confirmPassword: '',
|
||
newPasswordVisible: false,
|
||
confirmPasswordVisible: false,
|
||
|
||
newPasswordError: '',
|
||
confirmPasswordError: '',
|
||
globalError: '',
|
||
successMessage: '',
|
||
submitting: false,
|
||
|
||
rules: {
|
||
minLength: false,
|
||
hasLetter: false,
|
||
hasNumber: false
|
||
},
|
||
|
||
init() {
|
||
const params = new URLSearchParams(window.location.search)
|
||
this.mode = params.get('mode') === 'initial' ? 'initial' : 'recover'
|
||
|
||
if (this.mode === 'initial') {
|
||
this.pageTitle = '欢迎使用 Fonrey,请先设置您的登录密码'
|
||
this.pageDesc = '您当前使用的是初始密码,为保障账号安全,请立即设置新密码后开始使用'
|
||
this.submitText = '确认并进入系统'
|
||
} else {
|
||
this.pageTitle = '重置您的登录密码'
|
||
this.pageDesc = '请输入您的新密码,设置完成后请使用新密码重新登录'
|
||
this.submitText = '确认重置密码'
|
||
}
|
||
},
|
||
|
||
validatePassword() {
|
||
this.newPasswordError = ''
|
||
this.confirmPasswordError = ''
|
||
this.globalError = ''
|
||
|
||
this.rules.minLength = this.newPassword.length >= 8
|
||
this.rules.hasLetter = /[A-Za-z]/.test(this.newPassword)
|
||
this.rules.hasNumber = /\d/.test(this.newPassword)
|
||
|
||
if (this.confirmPassword && this.newPassword !== this.confirmPassword) {
|
||
this.confirmPasswordError = '两次输入密码不一致'
|
||
}
|
||
},
|
||
|
||
submitReset() {
|
||
this.successMessage = ''
|
||
this.globalError = ''
|
||
this.validatePassword()
|
||
|
||
if (!this.newPassword) {
|
||
this.newPasswordError = '请输入新密码'
|
||
return
|
||
}
|
||
if (!this.rules.minLength || !this.rules.hasLetter || !this.rules.hasNumber) {
|
||
this.globalError = '密码强度不足,请满足全部规则后再提交'
|
||
return
|
||
}
|
||
if (!this.confirmPassword) {
|
||
this.confirmPasswordError = '请再次输入新密码'
|
||
return
|
||
}
|
||
if (this.newPassword !== this.confirmPassword) {
|
||
this.confirmPasswordError = '两次输入密码不一致'
|
||
return
|
||
}
|
||
|
||
this.submitting = true
|
||
setTimeout(() => {
|
||
this.submitting = false
|
||
|
||
if (this.mode === 'initial') {
|
||
this.successMessage = '密码设置成功,正在进入系统…'
|
||
setTimeout(() => {
|
||
window.location.href = './房源列表_UI.html?from=reset&mode=initial&status=success'
|
||
}, 700)
|
||
return
|
||
}
|
||
|
||
this.successMessage = '密码已重置,请使用新密码登录'
|
||
setTimeout(() => {
|
||
window.location.href = './登录_账号密码_UI.html?reason=password_reset_success'
|
||
}, 900)
|
||
}, 800)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|