新增管理登录页面

This commit is contained in:
2026-04-14 13:43:16 +08:00
parent 596c7f357f
commit 426d01d99b
6 changed files with 908 additions and 0 deletions
+561
View File
@@ -0,0 +1,561 @@
{{-- 文件功能:站长隐藏登录页 --}}
@php
$systemName = \App\Models\SysParam::where('alias', 'sys_name')->value('body') ?? '和平聊吧';
@endphp
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>站长后台入口 - {{ $systemName }}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700;900&family=Noto+Serif+SC:wght@700;900&display=swap" rel="stylesheet">
<style>
:root {
--bg: #fafaf9;
--paper: #f5f1e8;
--ink: #0c0a09;
--ink-soft: #57534e;
--ink-faint: #a8a29e;
--line: #d6d3d1;
--line-strong: #1c1917;
--panel: #111111;
--panel-soft: #1f1f1f;
--gold: #a16207;
--gold-soft: #f4d9a8;
--danger: #b91c1c;
--danger-bg: #fef2f2;
--success: #166534;
--success-bg: #f0fdf4;
--shadow: 0 28px 90px rgba(12, 10, 9, 0.12);
}
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
min-height: 100%;
}
body {
font-family: "DM Sans", "PingFang SC", "Microsoft YaHei", sans-serif;
color: var(--ink);
background:
radial-gradient(circle at top left, rgba(161, 98, 7, 0.08), transparent 18%),
linear-gradient(180deg, #fcfbf8 0%, #f7f4ee 100%);
}
.shell {
width: min(1380px, calc(100% - 32px));
margin: 16px auto;
min-height: calc(100vh - 32px);
border: 1px solid var(--line-strong);
background: var(--bg);
box-shadow: var(--shadow);
display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(420px, 0.9fr);
position: relative;
overflow: hidden;
}
.shell::before {
content: "";
position: absolute;
inset: 18px;
border: 1px solid rgba(28, 25, 23, 0.08);
pointer-events: none;
}
.hero {
padding: 38px 42px 34px;
border-right: 1px solid var(--line-strong);
display: grid;
grid-template-rows: auto 1fr auto;
gap: 30px;
background:
linear-gradient(180deg, rgba(161, 98, 7, 0.03), transparent 26%),
linear-gradient(90deg, transparent 0, transparent calc(100% - 1px), rgba(28, 25, 23, 0.04) calc(100% - 1px));
}
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding-bottom: 18px;
border-bottom: 1px solid var(--line);
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 10px 16px;
border: 1px solid var(--line-strong);
background: #fff;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.24em;
text-transform: uppercase;
}
.eyebrow::before {
content: "";
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--gold);
}
.system-name {
font-size: 12px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink-soft);
text-align: right;
}
.hero-main {
display: grid;
align-content: center;
gap: 24px;
}
.kicker {
font-size: 13px;
font-weight: 700;
letter-spacing: 0.28em;
text-transform: uppercase;
color: var(--gold);
}
.title {
margin: 0;
font-size: clamp(4rem, 10vw, 8.8rem);
line-height: 0.88;
letter-spacing: -0.06em;
font-weight: 900;
text-transform: uppercase;
}
.title-accent {
display: block;
color: var(--gold);
}
.lead {
max-width: 620px;
margin: 0;
color: var(--ink-soft);
font-size: 18px;
line-height: 1.8;
}
.rule {
width: 92px;
height: 6px;
background: var(--line-strong);
}
.hero-meta {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 18px;
border-top: 1px solid var(--line);
padding-top: 22px;
}
.meta-card {
padding: 20px 18px;
background: var(--paper);
border: 1px solid var(--line);
min-height: 152px;
}
.meta-label {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--ink-soft);
}
.meta-value {
margin-top: 14px;
font-size: clamp(1.9rem, 3vw, 3.2rem);
line-height: 0.95;
font-weight: 900;
color: var(--ink);
}
.meta-copy {
margin-top: 12px;
color: var(--ink-soft);
font-size: 14px;
line-height: 1.8;
}
.panel {
background: var(--panel);
color: #fff;
padding: 26px;
display: flex;
align-items: center;
justify-content: center;
}
.panel-card {
width: min(100%, 470px);
border: 1px solid rgba(255, 255, 255, 0.12);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.015)),
var(--panel);
padding: 28px;
position: relative;
}
.panel-card::before {
content: "";
position: absolute;
inset: 14px;
border: 1px solid rgba(255, 255, 255, 0.08);
pointer-events: none;
}
.panel-top {
position: relative;
z-index: 1;
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 26px;
}
.panel-kicker {
font-size: 12px;
font-weight: 700;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--gold-soft);
}
.panel-title {
margin: 10px 0 8px;
font-family: "Noto Serif SC", serif;
font-size: 34px;
line-height: 1.12;
font-weight: 900;
}
.panel-copy {
margin: 0;
color: rgba(255, 255, 255, 0.72);
font-size: 15px;
line-height: 1.85;
}
.status-tag {
padding: 10px 12px;
border: 1px solid rgba(255, 255, 255, 0.14);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: #fff;
white-space: nowrap;
}
.form {
position: relative;
z-index: 1;
display: grid;
gap: 18px;
}
.field {
display: grid;
gap: 8px;
}
.field label {
font-size: 13px;
font-weight: 700;
letter-spacing: 0.06em;
color: rgba(255, 255, 255, 0.76);
}
.field input {
width: 100%;
border: 1px solid rgba(255, 255, 255, 0.16);
background: rgba(255, 255, 255, 0.04);
color: #fff;
padding: 16px 18px;
font-size: 16px;
font-weight: 600;
outline: none;
transition: border-color .2s ease, background-color .2s ease, box-shadow .2s ease;
}
.field input::placeholder {
color: rgba(255, 255, 255, 0.36);
}
.field input:focus {
border-color: rgba(244, 217, 168, 0.9);
background: rgba(255, 255, 255, 0.08);
box-shadow: 0 0 0 3px rgba(161, 98, 7, 0.22);
}
.captcha-row {
display: grid;
grid-template-columns: minmax(0, 1fr) 148px;
gap: 12px;
}
.captcha-image {
width: 100%;
height: 56px;
object-fit: cover;
cursor: pointer;
border: 1px solid rgba(255, 255, 255, 0.16);
background: #fff;
}
.message {
padding: 14px 16px;
border: 1px solid;
font-size: 14px;
line-height: 1.75;
}
.message-success {
border-color: rgba(34, 197, 94, 0.34);
background: rgba(22, 101, 52, 0.18);
color: #dcfce7;
}
.message-error {
border-color: rgba(248, 113, 113, 0.34);
background: rgba(127, 29, 29, 0.16);
color: #fecaca;
}
.submit {
width: 100%;
border: 1px solid var(--gold);
background: linear-gradient(180deg, #d5a548 0%, #a16207 100%);
color: #fff;
padding: 17px 18px;
font-size: 15px;
font-weight: 900;
letter-spacing: 0.12em;
text-transform: uppercase;
cursor: pointer;
transition: transform .2s ease, filter .2s ease, box-shadow .2s ease;
box-shadow: 0 18px 28px rgba(161, 98, 7, 0.2);
}
.submit:hover {
transform: translateY(-2px);
filter: saturate(1.03);
box-shadow: 0 22px 34px rgba(161, 98, 7, 0.28);
}
.helper {
position: relative;
z-index: 1;
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.12);
color: rgba(255, 255, 255, 0.56);
font-size: 13px;
line-height: 1.8;
}
.helper strong {
color: #fff;
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation: none !important;
transition: none !important;
scroll-behavior: auto !important;
}
}
@media (max-width: 1080px) {
.shell {
grid-template-columns: 1fr;
}
.hero {
border-right: 0;
border-bottom: 1px solid var(--line-strong);
}
.hero-meta {
grid-template-columns: 1fr;
}
}
@media (max-width: 720px) {
.shell {
width: min(100%, calc(100% - 18px));
margin: 9px auto;
min-height: calc(100vh - 18px);
}
.shell::before {
inset: 10px;
}
.hero,
.panel {
padding: 18px;
}
.hero-main {
gap: 18px;
}
.title {
font-size: clamp(3rem, 20vw, 4.8rem);
}
.panel-card {
padding: 20px;
}
.panel-top,
.captcha-row {
grid-template-columns: 1fr;
display: grid;
}
}
</style>
</head>
<body>
<main class="shell">
<section class="hero">
<header class="topbar">
<div class="eyebrow">Hidden Admin Entry</div>
<div class="system-name">{{ $systemName }}</div>
</header>
<div class="hero-main">
<div class="kicker">Trust / Authority / Private Access</div>
<h1 class="title">
Owner
<span class="title-accent">Console</span>
</h1>
<div class="rule" aria-hidden="true"></div>
<p class="lead">
这是一个独立于聊天室首页的后台登录入口。这里只做站长控制台访问,不承载普通用户登录,不会在登录成功后打开聊天室页面。
</p>
</div>
<div class="hero-meta">
<article class="meta-card">
<div class="meta-label">入口后缀</div>
<div class="meta-value">{{ $loginSuffix }}</div>
<div class="meta-copy">保留隐藏后缀,不在首页暴露。这个入口只用于后台控制台访问。</div>
</article>
<article class="meta-card">
<div class="meta-label">权限约束</div>
<div class="meta-value">id=1</div>
<div class="meta-copy">只有站长主账号可以从这里登录,其他账号即使密码正确也会被拒绝。</div>
</article>
<article class="meta-card">
<div class="meta-label">登录去向</div>
<div class="meta-value">Admin</div>
<div class="meta-copy">验证通过后直接进入后台首页,不跳聊天室大厅,不走“登录即注册”。</div>
</article>
</div>
</section>
<section class="panel">
<div class="panel-card">
<div class="panel-top">
<div>
<div class="panel-kicker">Owner Authentication</div>
<h2 class="panel-title">登录后台控制台</h2>
<p class="panel-copy">输入站长账号、密码和验证码后进入后台。</p>
</div>
<div class="status-tag">Restricted</div>
</div>
<form method="POST" action="{{ route('admin.login.store') }}" class="form">
@csrf
<div class="field">
<label for="username">站长账号</label>
<input id="username" name="username" type="text" value="{{ old('username') }}"
placeholder="请输入 id=1 对应的用户名" autocomplete="username" autofocus required>
</div>
<div class="field">
<label for="password">登录密码</label>
<input id="password" name="password" type="password" placeholder="请输入站长密码"
autocomplete="current-password" required>
</div>
<div class="field">
<label for="captcha">验证码</label>
<div class="captcha-row">
<input id="captcha" name="captcha" type="text" placeholder="输入验证码" required>
<img src="/captcha/default?{{ mt_rand() }}" alt="站长登录验证码" id="captcha-img" class="captcha-image"
onclick="refreshCaptcha()" title="点击刷新验证码">
</div>
</div>
@if (session('success'))
<div class="message message-success" role="status" aria-live="polite">
{{ session('success') }}
</div>
@endif
@if ($errors->any())
<div class="message message-error" role="alert" aria-live="assertive">
<ul style="margin: 0; padding-left: 18px;">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<button type="submit" class="submit">进入后台控制台</button>
</form>
<div class="helper">
<strong>提示:</strong>点击右侧验证码图片可刷新。如果页面仍显示旧样式,直接强制刷新浏览器缓存即可。
</div>
</div>
</section>
</main>
<script>
/**
* 刷新验证码图片,避免浏览器缓存旧图。
*/
function refreshCaptcha() {
document.getElementById('captcha-img').src = '/captcha/default?' + Math.random();
}
</script>
</body>
</html>