auto-save 2026-05-15 19:19 (~4)
This commit is contained in:
@@ -1,18 +1,5 @@
|
|||||||
{
|
{
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
|
||||||
"files_changed": 3,
|
|
||||||
"message": "Codex 会话活跃 · 最近命令:codex · 3 项未提交变更 · 最近提交:auto-save 2026-05-14 03:14 (~2)",
|
|
||||||
"ts": "2026-05-13T19:18:49Z",
|
|
||||||
"type": "session-heartbeat"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files_changed": 4,
|
|
||||||
"hash": "2144c37",
|
|
||||||
"message": "auto-save 2026-05-14 03:20 (~4)",
|
|
||||||
"ts": "2026-05-14T03:20:46+08:00",
|
|
||||||
"type": "commit"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"files_changed": 1,
|
"files_changed": 1,
|
||||||
"message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 03:20 (~4)",
|
"message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 03:20 (~4)",
|
||||||
@@ -3257,6 +3244,19 @@
|
|||||||
"message": "auto-save 2026-05-15 19:08 (~3)",
|
"message": "auto-save 2026-05-15 19:08 (~3)",
|
||||||
"hash": "086585a",
|
"hash": "086585a",
|
||||||
"files_changed": 3
|
"files_changed": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ts": "2026-05-15T19:13:46+08:00",
|
||||||
|
"type": "commit",
|
||||||
|
"message": "auto-save 2026-05-15 19:13 (~2)",
|
||||||
|
"hash": "ea52864",
|
||||||
|
"files_changed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ts": "2026-05-15T11:14:49Z",
|
||||||
|
"type": "session-heartbeat",
|
||||||
|
"message": "Codex 会话活跃 · 最近命令:codex · 1 项未提交变更 · 最近提交:auto-save 2026-05-15 19:13 (~2)",
|
||||||
|
"files_changed": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -591,10 +591,10 @@
|
|||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td><code>web/app/page.tsx</code></td><td>产品工作台主状态:jobs、activeJobId、按 job 隔离的 selectedFrames/详情面板状态、clipboard、ReactFlow 节点和边;负责打开/找回画布工作面板。</td></tr>
|
<tr><td><code>web/app/page.tsx</code></td><td>产品工作台主状态:jobs、activeJobId、按 job 隔离的 selectedFrames/详情面板状态、clipboard、ReactFlow 节点和边;负责打开/找回画布工作面板。</td></tr>
|
||||||
<tr><td><code>web/app/login/page.tsx</code></td><td>生产登录页:访问账号/访问密钥表单、保持会话、错误/成功状态;页面改为全屏 Digital Oasis 式动态背景、黑白/香槟金视觉、Internal Access 胶囊和画面/声音/成片状态栏;右侧身份验证面板保持简约窄列,不再强制大高度。</td></tr>
|
<tr><td><code>web/app/login/page.tsx</code></td><td>生产登录页:访问账号/访问密钥表单、保持会话、错误/成功状态;当前只在原版 Digital Oasis 动态背景上叠加登录表单和动态角色,不再保留 SKG 标题、内容创作中枢、Pipeline、指标栏等额外文案。</td></tr>
|
||||||
<tr><td><code>web/components/login/oasis-canvas.tsx</code></td><td>登录页全屏动态视觉层:用 iframe 直接承载下载包 <code>web/public/oasis-source/index.html</code> 的原 WebGPU / Three.js 草场源码;父级登录页只覆盖自己的文案和表单,并在捕获阶段把全局鼠标坐标转发给 iframe,避免登录面板遮挡时草地失去鼠标响应。</td></tr>
|
<tr><td><code>web/components/login/oasis-canvas.tsx</code></td><td>登录页全屏动态视觉层:用 iframe 直接承载下载包 <code>web/public/oasis-source/index.html</code> 的原 WebGPU / Three.js 草场源码;父级登录页只覆盖自己的文案和表单,并在捕获阶段把全局鼠标坐标转发给 iframe,避免登录面板遮挡时草地失去鼠标响应。</td></tr>
|
||||||
<tr><td><code>web/public/oasis-source/index.html</code></td><td>从下载包 <code>remix-3d-website-the-digital-o</code> 复制来的原始视觉源码。只额外隐藏 demo 站自己的导航、文字和设置面板,保留原 WebGPU 渲染、草场、景深和鼠标交互源码;同时接收父页面 <code>postMessage</code> 指针坐标驱动草地交互。</td></tr>
|
<tr><td><code>web/public/oasis-source/index.html</code></td><td>从下载包 <code>remix-3d-website-the-digital-o</code> 复制来的原始视觉源码。只额外隐藏 demo 站自己的导航、文字和设置面板,保留原 WebGPU 渲染、草场、景深和鼠标交互源码;同时接收父页面 <code>postMessage</code> 指针坐标驱动草地交互。</td></tr>
|
||||||
<tr><td><code>web/components/login/animated-login-characters.tsx</code></td><td>登录页四个几何动态角色组件:当前作为左侧透明框架里的放大主视觉展示,保留鼠标眼神跟随、输入、显示密码、错误和成功状态反馈。</td></tr>
|
<tr><td><code>web/components/login/animated-login-characters.tsx</code></td><td>登录页四个几何动态角色组件:当前作为原版 Oasis 背景上的独立透明框架展示,保留鼠标眼神跟随、输入、显示密码、错误和成功状态反馈。</td></tr>
|
||||||
<tr><td><code>web/components/nodes/index.tsx</code></td><td>DAG 节点定义:Input、VisualLab、Audio、Compose,以及画布工作面板 KeyframePanel / VideoFramePanel;旧 Keyframe/Storyboard/VideoGen 组件保留但不再挂主画布。</td></tr>
|
<tr><td><code>web/components/nodes/index.tsx</code></td><td>DAG 节点定义:Input、VisualLab、Audio、Compose,以及画布工作面板 KeyframePanel / VideoFramePanel;旧 Keyframe/Storyboard/VideoGen 组件保留但不再挂主画布。</td></tr>
|
||||||
<tr><td><code>web/components/audio-strip.tsx</code></td><td>底部吸附音频条:可拖拽调整高度;播放原音频时移动指针,逐个高亮英文/中文字幕节点和对应波形,并在右侧固定显示按原音频时长生成的 SKG 英文产品口播和 MiniMax 随机英文配音。</td></tr>
|
<tr><td><code>web/components/audio-strip.tsx</code></td><td>底部吸附音频条:可拖拽调整高度;播放原音频时移动指针,逐个高亮英文/中文字幕节点和对应波形,并在右侧固定显示按原音频时长生成的 SKG 英文产品口播和 MiniMax 随机英文配音。</td></tr>
|
||||||
<tr><td><code>web/components/lightbox.tsx</code></td><td>关键帧素材准备面板:清洗、统一主体候选、参考帧网格、六张主体重绘图、每帧去主体场景图、纵向 6 行产品融合镜头工作表和审核。</td></tr>
|
<tr><td><code>web/components/lightbox.tsx</code></td><td>关键帧素材准备面板:清洗、统一主体候选、参考帧网格、六张主体重绘图、每帧去主体场景图、纵向 6 行产品融合镜头工作表和审核。</td></tr>
|
||||||
@@ -944,6 +944,18 @@ SubjectAsset {
|
|||||||
<h2>变更记录</h2>
|
<h2>变更记录</h2>
|
||||||
<p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p>
|
<p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p>
|
||||||
<div class="changelog">
|
<div class="changelog">
|
||||||
|
<article class="change">
|
||||||
|
<header>
|
||||||
|
<h3>2026-05-15 · 登录页只保留源码背景、登录和动画</h3>
|
||||||
|
<span class="tag rose">UI</span>
|
||||||
|
<span class="tag violet">Login</span>
|
||||||
|
</header>
|
||||||
|
<div class="body">
|
||||||
|
<p><strong>问题:</strong>登录页仍像内容页,保留了 SKG 标题、内容创作中枢、Pipeline 状态和底部指标,和“直接把源码背景写过来,上面只放登录和动画”的目标不一致。</p>
|
||||||
|
<p><strong>改动:</strong><code>web/app/login/page.tsx</code> 删除左侧营销文案、状态胶囊、标题、说明、Pipeline 和指标栏,只保留 <code>OasisCanvas</code>、动态角色透明框和登录表单。<code>web/app/globals.css</code> 新增 <code>login-page--source</code>、<code>login-source-overlay</code>、<code>login-source-character-panel</code> 等样式,让两个功能浮层直接叠在原版 Digital Oasis 背景上。</p>
|
||||||
|
<p><strong>影响:</strong><code>web/app/login/page.tsx</code>、<code>web/app/globals.css</code>、<code>docs/source-analysis.html</code>。</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
<article class="change">
|
<article class="change">
|
||||||
<header>
|
<header>
|
||||||
<h3>2026-05-15 · 登录页草地鼠标转发、面板压缩和角色放大</h3>
|
<h3>2026-05-15 · 登录页草地鼠标转发、面板压缩和角色放大</h3>
|
||||||
|
|||||||
@@ -365,6 +365,61 @@
|
|||||||
-webkit-text-fill-color: #fff;
|
-webkit-text-fill-color: #fff;
|
||||||
transition: background-color 9999s ease-in-out 0s;
|
transition: background-color 9999s ease-in-out 0s;
|
||||||
}
|
}
|
||||||
|
.login-page--source::before,
|
||||||
|
.login-page--source::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.login-page--source .login-oasis-shade {
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 34% 52%, rgba(230, 245, 120, 0.08), transparent 28%),
|
||||||
|
linear-gradient(90deg, rgba(0, 0, 0, 0.22), rgba(0, 0, 0, 0.04) 44%, rgba(0, 0, 0, 0.38));
|
||||||
|
}
|
||||||
|
.login-source-overlay {
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
display: grid;
|
||||||
|
min-height: 100vh;
|
||||||
|
grid-template-columns: minmax(320px, 460px) minmax(280px, 320px);
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: clamp(28px, 8vw, 120px);
|
||||||
|
padding: clamp(20px, 4vw, 56px);
|
||||||
|
}
|
||||||
|
.login-source-character-panel {
|
||||||
|
width: min(460px, 36vw);
|
||||||
|
border: 1px solid rgba(140, 180, 120, 0.14);
|
||||||
|
border-radius: 16px;
|
||||||
|
background: rgba(10, 18, 10, 0.28);
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.32);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
}
|
||||||
|
.login-source-character-panel .login-character-stage {
|
||||||
|
min-height: 330px;
|
||||||
|
border-color: rgba(255, 255, 255, 0.12);
|
||||||
|
background:
|
||||||
|
linear-gradient(90deg, rgba(255, 255, 255, 0.055) 1px, transparent 1px),
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.055) 1px, transparent 1px),
|
||||||
|
rgba(255, 255, 255, 0.035);
|
||||||
|
background-size: 28px 28px, 28px 28px, auto;
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
.login-source-character-panel .login-character-stage::after {
|
||||||
|
background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.24));
|
||||||
|
}
|
||||||
|
.login-source-character-panel .login-stage-grid {
|
||||||
|
border-color: rgba(255, 255, 255, 0.11);
|
||||||
|
}
|
||||||
|
.login-source-character-panel .login-characters-container {
|
||||||
|
transform: translateX(-50%) scale(0.78);
|
||||||
|
}
|
||||||
|
.login-page--oasis .login-source-auth-panel {
|
||||||
|
width: min(320px, calc(100vw - 40px));
|
||||||
|
padding: 22px;
|
||||||
|
}
|
||||||
|
.login-page--source .login-auth-icon {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
.login-hero {
|
.login-hero {
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
color: #282828;
|
color: #282828;
|
||||||
@@ -1089,6 +1144,29 @@
|
|||||||
.login-page--oasis .login-auth-panel {
|
.login-page--oasis .login-auth-panel {
|
||||||
padding: 24px 18px;
|
padding: 24px 18px;
|
||||||
}
|
}
|
||||||
|
.login-source-overlay {
|
||||||
|
min-height: 100vh;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
align-content: center;
|
||||||
|
gap: 18px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.login-source-character-panel {
|
||||||
|
width: min(100%, 350px);
|
||||||
|
justify-self: center;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
.login-source-character-panel .login-character-stage {
|
||||||
|
min-height: 220px;
|
||||||
|
}
|
||||||
|
.login-source-character-panel .login-characters-container {
|
||||||
|
transform: translateX(-50%) scale(0.48);
|
||||||
|
}
|
||||||
|
.login-page--oasis .login-source-auth-panel {
|
||||||
|
width: min(100%, 330px);
|
||||||
|
justify-self: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
.login-page--oasis .login-wordmark__logo {
|
.login-page--oasis .login-wordmark__logo {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import {
|
|||||||
Eye,
|
Eye,
|
||||||
EyeOff,
|
EyeOff,
|
||||||
LockKeyhole,
|
LockKeyhole,
|
||||||
ShieldCheck,
|
|
||||||
Sparkles,
|
|
||||||
UserRound,
|
UserRound,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { AnimatedLoginCharacters, type LoginCharacterMood } from "@/components/login/animated-login-characters"
|
import { AnimatedLoginCharacters, type LoginCharacterMood } from "@/components/login/animated-login-characters"
|
||||||
@@ -86,162 +84,110 @@ export default function LoginPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="login-page login-page--oasis relative min-h-screen overflow-hidden px-5 py-6 text-white sm:px-8 lg:px-10">
|
<main className="login-page login-page--oasis login-page--source relative min-h-screen overflow-hidden text-white">
|
||||||
<OasisCanvas />
|
<OasisCanvas />
|
||||||
<div className="login-oasis-shade" />
|
<div className="login-oasis-shade" />
|
||||||
<div className="relative z-10 mx-auto flex min-h-[calc(100vh-3rem)] w-full max-w-7xl items-center">
|
<div className="login-source-overlay">
|
||||||
<div className="grid w-full gap-5 lg:grid-cols-[minmax(0,1fr)_minmax(290px,320px)] lg:items-center">
|
<section className="login-source-character-panel" aria-hidden="true">
|
||||||
<section className="login-hero login-oasis-hero order-2 relative min-h-[540px] overflow-hidden p-1 text-white sm:p-2 lg:order-1 lg:min-h-[620px]">
|
<AnimatedLoginCharacters mood={mood} eyeOffset={eyeOffset} />
|
||||||
<div className="relative z-10 flex h-full flex-col">
|
</section>
|
||||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
|
||||||
<div className="login-wordmark">
|
|
||||||
<span className="login-wordmark__logo">SKG</span>
|
|
||||||
<span className="login-wordmark__sub">Creative OS</span>
|
|
||||||
</div>
|
|
||||||
<div className="login-secure-pill">
|
|
||||||
<ShieldCheck className="h-4 w-4" />
|
|
||||||
<span>Internal Access</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-10 max-w-[620px]">
|
<section className="login-auth-panel login-source-auth-panel flex items-center rounded-[8px]">
|
||||||
<p className="login-kicker">CONTENT PRODUCTION SYSTEM</p>
|
<form className="w-full" onSubmit={onSubmit}>
|
||||||
<h1 className="login-premium-title">内容创作中枢</h1>
|
<div className="mb-4">
|
||||||
<p className="login-premium-copy">统一进入素材拆解、画面处理、声音处理与成片生成流程。</p>
|
<div className="login-auth-icon inline-flex h-9 w-9 items-center justify-center rounded-[8px] text-white">
|
||||||
</div>
|
<LockKeyhole className="h-5 w-5" />
|
||||||
|
|
||||||
<div className="login-creative-stage" aria-label="Creative workflow visual">
|
|
||||||
<div className="login-creative-caption">
|
|
||||||
<span>Creative Pipeline</span>
|
|
||||||
<b>Pipeline ready</b>
|
|
||||||
</div>
|
|
||||||
<div className="login-dynamic-dock">
|
|
||||||
<span className="login-dynamic-dock__label">Live Creative Modules</span>
|
|
||||||
<AnimatedLoginCharacters mood={mood} eyeOffset={eyeOffset} />
|
|
||||||
</div>
|
|
||||||
<div className="login-studio-chip login-studio-chip--visual">
|
|
||||||
<Sparkles className="h-4 w-4" />
|
|
||||||
<span>Pipeline Online</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="login-premium-metrics">
|
|
||||||
{[
|
|
||||||
["Frame Lab", "画面"],
|
|
||||||
["Sound Lab", "声音"],
|
|
||||||
["Final Cut", "成片"],
|
|
||||||
].map(([label, value]) => (
|
|
||||||
<div key={label} className="login-premium-metric">
|
|
||||||
<span>{label}</span>
|
|
||||||
<b>{value}</b>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="login-auth-panel order-1 flex items-center rounded-[8px] p-5 sm:p-6 lg:order-2 lg:p-6">
|
<div className="space-y-3">
|
||||||
<form className="w-full" onSubmit={onSubmit}>
|
<label className="block">
|
||||||
<div className="mb-5">
|
<span className="sr-only">访问账号</span>
|
||||||
<div className="login-auth-icon mb-3 inline-flex h-9 w-9 items-center justify-center rounded-[8px] text-white">
|
<span className="flex h-11 items-center gap-3 rounded-[8px] border border-white/10 bg-black/25 px-3 text-white transition focus-within:border-[#d6b36a] focus-within:bg-black/35 focus-within:ring-2 focus-within:ring-[#d6b36a]/25">
|
||||||
<LockKeyhole className="h-5 w-5" />
|
<UserRound className="h-4 w-4 text-white/45" />
|
||||||
</div>
|
|
||||||
<h2 className="text-xl font-semibold text-white">身份验证</h2>
|
|
||||||
<p className="mt-2 text-sm leading-6 text-white/55">验证后进入内容生产环境</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="block">
|
|
||||||
<span className="mb-2 block text-sm font-medium text-white/70">访问账号</span>
|
|
||||||
<span className="flex h-11 items-center gap-3 rounded-[8px] border border-white/10 bg-black/25 px-3 text-white transition focus-within:border-[#d6b36a] focus-within:bg-black/35 focus-within:ring-2 focus-within:ring-[#d6b36a]/25">
|
|
||||||
<UserRound className="h-4 w-4 text-white/45" />
|
|
||||||
<input
|
|
||||||
className="h-full min-w-0 flex-1 bg-transparent text-base text-white outline-none placeholder:text-white/30"
|
|
||||||
value={username}
|
|
||||||
disabled={disabled}
|
|
||||||
autoComplete="username"
|
|
||||||
placeholder="输入访问账号"
|
|
||||||
onFocus={() => setActiveField("username")}
|
|
||||||
onBlur={() => setActiveField(null)}
|
|
||||||
onChange={(event) => {
|
|
||||||
setUsername(event.target.value)
|
|
||||||
if (error) setError("")
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label className="block">
|
|
||||||
<span className="mb-2 block text-sm font-medium text-white/70">访问密钥</span>
|
|
||||||
<span className="flex h-11 items-center gap-3 rounded-[8px] border border-white/10 bg-black/25 px-3 text-white transition focus-within:border-[#d6b36a] focus-within:bg-black/35 focus-within:ring-2 focus-within:ring-[#d6b36a]/25">
|
|
||||||
<LockKeyhole className="h-4 w-4 text-white/45" />
|
|
||||||
<input
|
|
||||||
className="h-full min-w-0 flex-1 bg-transparent text-base text-white outline-none placeholder:text-white/30"
|
|
||||||
value={password}
|
|
||||||
disabled={disabled}
|
|
||||||
type={showPassword ? "text" : "password"}
|
|
||||||
autoComplete="current-password"
|
|
||||||
placeholder="输入访问密钥"
|
|
||||||
onFocus={() => setActiveField("password")}
|
|
||||||
onBlur={() => setActiveField(null)}
|
|
||||||
onChange={(event) => {
|
|
||||||
setPassword(event.target.value)
|
|
||||||
if (error) setError("")
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
className="grid h-9 w-9 place-items-center rounded-[8px] text-white/55 transition hover:bg-white/10 hover:text-white focus:outline-none focus:ring-2 focus:ring-[#d6b36a]/45 disabled:opacity-50"
|
|
||||||
type="button"
|
|
||||||
disabled={disabled}
|
|
||||||
aria-label={showPassword ? "隐藏密码" : "显示密码"}
|
|
||||||
onMouseDown={(event) => event.preventDefault()}
|
|
||||||
onClick={() => setShowPassword((value) => !value)}
|
|
||||||
>
|
|
||||||
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-3 flex items-center justify-between gap-4">
|
|
||||||
<label className="flex cursor-pointer items-center gap-2 text-sm text-white/60">
|
|
||||||
<input
|
<input
|
||||||
className="h-4 w-4 rounded border-white/20 bg-black/30 accent-[#c89b3c]"
|
className="h-full min-w-0 flex-1 bg-transparent text-base text-white outline-none placeholder:text-white/30"
|
||||||
type="checkbox"
|
value={username}
|
||||||
checked={remember}
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={(event) => setRemember(event.target.checked)}
|
autoComplete="username"
|
||||||
|
placeholder="账号"
|
||||||
|
onFocus={() => setActiveField("username")}
|
||||||
|
onBlur={() => setActiveField(null)}
|
||||||
|
onChange={(event) => {
|
||||||
|
setUsername(event.target.value)
|
||||||
|
if (error) setError("")
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<span>保持会话</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<span className="text-xs text-white/35">marketing.skg.com</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-4 min-h-9" aria-live="polite">
|
<label className="block">
|
||||||
{error ? (
|
<span className="sr-only">访问密钥</span>
|
||||||
<div className="flex items-start gap-2 rounded-[8px] border border-red-400/30 bg-red-500/10 px-3 py-2 text-sm text-red-100">
|
<span className="flex h-11 items-center gap-3 rounded-[8px] border border-white/10 bg-black/25 px-3 text-white transition focus-within:border-[#d6b36a] focus-within:bg-black/35 focus-within:ring-2 focus-within:ring-[#d6b36a]/25">
|
||||||
<AlertCircle className="mt-0.5 h-4 w-4 shrink-0" />
|
<LockKeyhole className="h-4 w-4 text-white/45" />
|
||||||
<span>{error}</span>
|
<input
|
||||||
</div>
|
className="h-full min-w-0 flex-1 bg-transparent text-base text-white outline-none placeholder:text-white/30"
|
||||||
) : status === "success" ? (
|
value={password}
|
||||||
<div className="flex items-start gap-2 rounded-[8px] border border-emerald-300/30 bg-emerald-400/10 px-3 py-2 text-sm text-emerald-100">
|
disabled={disabled}
|
||||||
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0" />
|
type={showPassword ? "text" : "password"}
|
||||||
<span>验证通过,正在打开工作台</span>
|
autoComplete="current-password"
|
||||||
</div>
|
placeholder="密钥"
|
||||||
) : null}
|
onFocus={() => setActiveField("password")}
|
||||||
</div>
|
onBlur={() => setActiveField(null)}
|
||||||
|
onChange={(event) => {
|
||||||
|
setPassword(event.target.value)
|
||||||
|
if (error) setError("")
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="grid h-9 w-9 place-items-center rounded-[8px] text-white/55 transition hover:bg-white/10 hover:text-white focus:outline-none focus:ring-2 focus:ring-[#d6b36a]/45 disabled:opacity-50"
|
||||||
|
type="button"
|
||||||
|
disabled={disabled}
|
||||||
|
aria-label={showPassword ? "隐藏密码" : "显示密码"}
|
||||||
|
onMouseDown={(event) => event.preventDefault()}
|
||||||
|
onClick={() => setShowPassword((value) => !value)}
|
||||||
|
>
|
||||||
|
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<label className="mt-3 flex cursor-pointer items-center gap-2 text-sm text-white/60">
|
||||||
className="mt-1 flex h-11 w-full items-center justify-center gap-2 rounded-[8px] bg-white px-4 text-base font-semibold text-black shadow-xl shadow-black/25 transition hover:bg-[#f5efe3] focus:outline-none focus:ring-2 focus:ring-[#d6b36a]/60 disabled:cursor-wait disabled:opacity-70"
|
<input
|
||||||
type="submit"
|
className="h-4 w-4 rounded border-white/20 bg-black/30 accent-[#c89b3c]"
|
||||||
|
type="checkbox"
|
||||||
|
checked={remember}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
onChange={(event) => setRemember(event.target.checked)}
|
||||||
<span>{status === "loading" ? "验证中" : status === "success" ? "已验证" : "打开工作台"}</span>
|
/>
|
||||||
<ArrowRight className="h-4 w-4" />
|
<span>保持会话</span>
|
||||||
</button>
|
</label>
|
||||||
</form>
|
|
||||||
</section>
|
<div className="mt-4 min-h-9" aria-live="polite">
|
||||||
</div>
|
{error ? (
|
||||||
|
<div className="flex items-start gap-2 rounded-[8px] border border-red-400/30 bg-red-500/10 px-3 py-2 text-sm text-red-100">
|
||||||
|
<AlertCircle className="mt-0.5 h-4 w-4 shrink-0" />
|
||||||
|
<span>{error}</span>
|
||||||
|
</div>
|
||||||
|
) : status === "success" ? (
|
||||||
|
<div className="flex items-start gap-2 rounded-[8px] border border-emerald-300/30 bg-emerald-400/10 px-3 py-2 text-sm text-emerald-100">
|
||||||
|
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0" />
|
||||||
|
<span>正在进入</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="mt-1 flex h-11 w-full items-center justify-center gap-2 rounded-[8px] bg-white px-4 text-base font-semibold text-black shadow-xl shadow-black/25 transition hover:bg-[#f5efe3] focus:outline-none focus:ring-2 focus:ring-[#d6b36a]/60 disabled:cursor-wait disabled:opacity-70"
|
||||||
|
type="submit"
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
<span>{status === "loading" ? "验证中" : status === "success" ? "进入中" : "登录"}</span>
|
||||||
|
<ArrowRight className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user