Upload 439 files
Browse files- src/components/admin/AdminApp.svelte +198 -16
- src/layouts/AdminLayout.astro +8 -61
src/components/admin/AdminApp.svelte
CHANGED
|
@@ -140,7 +140,14 @@
|
|
| 140 |
];
|
| 141 |
|
| 142 |
function cloneData<T>(value: T): T {
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
}
|
| 145 |
|
| 146 |
function prettyJson(value: unknown) {
|
|
@@ -282,11 +289,13 @@
|
|
| 282 |
};
|
| 283 |
}
|
| 284 |
|
| 285 |
-
let sessionLoading =
|
| 286 |
let sessionRefreshing = false;
|
|
|
|
| 287 |
let authenticated = false;
|
| 288 |
let configured = true;
|
| 289 |
let username = "";
|
|
|
|
| 290 |
let activeNav: NavKey = "overview";
|
| 291 |
let toast: ToastState | null = null;
|
| 292 |
let loginForm = { username: "", password: "" };
|
|
@@ -430,7 +439,8 @@
|
|
| 430 |
}
|
| 431 |
|
| 432 |
async function refreshSession() {
|
| 433 |
-
|
|
|
|
| 434 |
try {
|
| 435 |
const data = await api<any>("/api/admin/session");
|
| 436 |
authenticated = Boolean(data.authenticated);
|
|
@@ -445,6 +455,7 @@
|
|
| 445 |
error instanceof Error ? error.message : "后台会话检查失败",
|
| 446 |
);
|
| 447 |
} finally {
|
|
|
|
| 448 |
sessionLoading = false;
|
| 449 |
sessionRefreshing = false;
|
| 450 |
}
|
|
@@ -584,12 +595,15 @@
|
|
| 584 |
|
| 585 |
async function handleLogin(event?: SubmitEvent) {
|
| 586 |
event?.preventDefault();
|
|
|
|
| 587 |
try {
|
| 588 |
const response = await api<any>("/api/admin/login", {
|
| 589 |
method: "POST",
|
| 590 |
body: JSON.stringify(loginForm),
|
| 591 |
});
|
| 592 |
authenticated = Boolean(response.authenticated);
|
|
|
|
|
|
|
| 593 |
username = String(response.username || loginForm.username || "");
|
| 594 |
loginForm.password = "";
|
| 595 |
applyBuild(response);
|
|
@@ -597,6 +611,8 @@
|
|
| 597 |
showToast("success", "登录成功");
|
| 598 |
} catch (error) {
|
| 599 |
showToast("error", error instanceof Error ? error.message : "\u767b\u5f55\u5931\u8d25");
|
|
|
|
|
|
|
| 600 |
}
|
| 601 |
}
|
| 602 |
|
|
@@ -604,6 +620,8 @@
|
|
| 604 |
await api("/api/admin/logout", { method: "POST", body: JSON.stringify({}) });
|
| 605 |
authenticated = false;
|
| 606 |
username = "";
|
|
|
|
|
|
|
| 607 |
showToast("info", "已退出后台");
|
| 608 |
}
|
| 609 |
|
|
@@ -789,23 +807,75 @@
|
|
| 789 |
<div class={`admin-toast ${toast.tone}`}>{toast.text}</div>
|
| 790 |
{/if}
|
| 791 |
|
| 792 |
-
{#if !
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 793 |
<div class="card-base admin-panel admin-empty">
|
| 794 |
<h2>后台尚未启用</h2>
|
| 795 |
<p>请在 Hugging Face Space 中配置环境变量 <code>ADMIN</code> 和 <code>PASSWORD</code> 后再访问管理页。</p>
|
| 796 |
</div>
|
| 797 |
{:else if !authenticated}
|
| 798 |
-
<
|
| 799 |
-
<div class="admin-
|
| 800 |
-
|
| 801 |
-
|
| 802 |
-
|
| 803 |
-
|
| 804 |
-
|
| 805 |
-
|
| 806 |
-
|
| 807 |
-
|
| 808 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 809 |
{:else}
|
| 810 |
<div class="admin-workbench">
|
| 811 |
<aside class="admin-sidebar">
|
|
@@ -1005,6 +1075,63 @@
|
|
| 1005 |
.admin-panel, .admin-login {
|
| 1006 |
padding: 1.2rem;
|
| 1007 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1008 |
.admin-login {
|
| 1009 |
max-width: 30rem;
|
| 1010 |
margin: 0 auto;
|
|
@@ -1012,6 +1139,43 @@
|
|
| 1012 |
flex-direction: column;
|
| 1013 |
gap: 0.9rem;
|
| 1014 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1015 |
.admin-login-stage {
|
| 1016 |
position: relative;
|
| 1017 |
z-index: 6;
|
|
@@ -1048,6 +1212,7 @@
|
|
| 1048 |
.admin-quick-actions { display: flex; gap: 0.6rem; align-items: center; }
|
| 1049 |
.admin-quick-actions.wrap { flex-wrap: wrap; }
|
| 1050 |
.admin-button { min-height: 2.7rem; padding: 0.65rem 1rem; border-radius: 0.95rem; }
|
|
|
|
| 1051 |
.admin-button.ghost { border: 1px solid var(--line-divider); }
|
| 1052 |
.admin-button.danger { color: oklch(0.62 0.2 25); }
|
| 1053 |
.admin-nav-button, .editor-item, .overview-item {
|
|
@@ -1098,8 +1263,25 @@
|
|
| 1098 |
.build-log { max-height: 22rem; overflow: auto; }
|
| 1099 |
.danger-text { color: oklch(0.62 0.2 25); }
|
| 1100 |
.admin-empty { padding: 2rem; text-align: center; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1101 |
@media (max-width: 1200px) {
|
| 1102 |
-
.admin-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1103 |
}
|
| 1104 |
@media (max-width: 800px) {
|
| 1105 |
.admin-login {
|
|
|
|
| 140 |
];
|
| 141 |
|
| 142 |
function cloneData<T>(value: T): T {
|
| 143 |
+
if (value === null || value === undefined || typeof value !== "object") {
|
| 144 |
+
return value;
|
| 145 |
+
}
|
| 146 |
+
try {
|
| 147 |
+
return structuredClone(value);
|
| 148 |
+
} catch {
|
| 149 |
+
return JSON.parse(JSON.stringify(value)) as T;
|
| 150 |
+
}
|
| 151 |
}
|
| 152 |
|
| 153 |
function prettyJson(value: unknown) {
|
|
|
|
| 289 |
};
|
| 290 |
}
|
| 291 |
|
| 292 |
+
let sessionLoading = true;
|
| 293 |
let sessionRefreshing = false;
|
| 294 |
+
let sessionResolved = false;
|
| 295 |
let authenticated = false;
|
| 296 |
let configured = true;
|
| 297 |
let username = "";
|
| 298 |
+
let loginSubmitting = false;
|
| 299 |
let activeNav: NavKey = "overview";
|
| 300 |
let toast: ToastState | null = null;
|
| 301 |
let loginForm = { username: "", password: "" };
|
|
|
|
| 439 |
}
|
| 440 |
|
| 441 |
async function refreshSession() {
|
| 442 |
+
sessionLoading = true;
|
| 443 |
+
sessionRefreshing = sessionResolved;
|
| 444 |
try {
|
| 445 |
const data = await api<any>("/api/admin/session");
|
| 446 |
authenticated = Boolean(data.authenticated);
|
|
|
|
| 455 |
error instanceof Error ? error.message : "后台会话检查失败",
|
| 456 |
);
|
| 457 |
} finally {
|
| 458 |
+
sessionResolved = true;
|
| 459 |
sessionLoading = false;
|
| 460 |
sessionRefreshing = false;
|
| 461 |
}
|
|
|
|
| 595 |
|
| 596 |
async function handleLogin(event?: SubmitEvent) {
|
| 597 |
event?.preventDefault();
|
| 598 |
+
loginSubmitting = true;
|
| 599 |
try {
|
| 600 |
const response = await api<any>("/api/admin/login", {
|
| 601 |
method: "POST",
|
| 602 |
body: JSON.stringify(loginForm),
|
| 603 |
});
|
| 604 |
authenticated = Boolean(response.authenticated);
|
| 605 |
+
configured = response.configured !== false;
|
| 606 |
+
sessionResolved = true;
|
| 607 |
username = String(response.username || loginForm.username || "");
|
| 608 |
loginForm.password = "";
|
| 609 |
applyBuild(response);
|
|
|
|
| 611 |
showToast("success", "登录成功");
|
| 612 |
} catch (error) {
|
| 613 |
showToast("error", error instanceof Error ? error.message : "\u767b\u5f55\u5931\u8d25");
|
| 614 |
+
} finally {
|
| 615 |
+
loginSubmitting = false;
|
| 616 |
}
|
| 617 |
}
|
| 618 |
|
|
|
|
| 620 |
await api("/api/admin/logout", { method: "POST", body: JSON.stringify({}) });
|
| 621 |
authenticated = false;
|
| 622 |
username = "";
|
| 623 |
+
sessionResolved = true;
|
| 624 |
+
loginForm.password = "";
|
| 625 |
showToast("info", "已退出后台");
|
| 626 |
}
|
| 627 |
|
|
|
|
| 807 |
<div class={`admin-toast ${toast.tone}`}>{toast.text}</div>
|
| 808 |
{/if}
|
| 809 |
|
| 810 |
+
{#if !sessionResolved}
|
| 811 |
+
<section class="admin-auth-shell admin-auth-shell-pending">
|
| 812 |
+
<div class="card-base admin-panel admin-auth-copy">
|
| 813 |
+
<div class="admin-login-badge">Firefly Admin</div>
|
| 814 |
+
<div class="admin-auth-intro">
|
| 815 |
+
<h2>正在连接后台</h2>
|
| 816 |
+
<p>正在检查管理员会话与后台启用状态,确认完成后会显示登录入口或直接进入内容管理台。</p>
|
| 817 |
+
</div>
|
| 818 |
+
<div class="admin-loading-stack" aria-hidden="true">
|
| 819 |
+
<div class="admin-loading-line"></div>
|
| 820 |
+
<div class="admin-loading-line short"></div>
|
| 821 |
+
<div class="admin-loading-line medium"></div>
|
| 822 |
+
</div>
|
| 823 |
+
</div>
|
| 824 |
+
<div class="card-base admin-login admin-login-stage admin-login-pending">
|
| 825 |
+
<div class="admin-login-badge">会话检查中</div>
|
| 826 |
+
<h2>后台准备中</h2>
|
| 827 |
+
<p class="admin-login-status">正在从当前 Space 读取后台环境变量并确认登录状态。</p>
|
| 828 |
+
</div>
|
| 829 |
+
</section>
|
| 830 |
+
{:else if !configured}
|
| 831 |
<div class="card-base admin-panel admin-empty">
|
| 832 |
<h2>后台尚未启用</h2>
|
| 833 |
<p>请在 Hugging Face Space 中配置环境变量 <code>ADMIN</code> 和 <code>PASSWORD</code> 后再访问管理页。</p>
|
| 834 |
</div>
|
| 835 |
{:else if !authenticated}
|
| 836 |
+
<section class="admin-auth-shell">
|
| 837 |
+
<div class="card-base admin-panel admin-auth-copy">
|
| 838 |
+
<div class="admin-login-badge">Firefly Admin</div>
|
| 839 |
+
<div class="admin-auth-intro">
|
| 840 |
+
<h2>后台管理入口</h2>
|
| 841 |
+
<p>在这里统一管理博客标题、介绍、导航、侧栏、文章、图片素材与构建状态,界面风格与前台博客保持一致。</p>
|
| 842 |
+
</div>
|
| 843 |
+
<div class="admin-auth-facts">
|
| 844 |
+
<div class="admin-auth-fact">
|
| 845 |
+
<strong>账号来源</strong>
|
| 846 |
+
<span><code>ADMIN</code></span>
|
| 847 |
+
</div>
|
| 848 |
+
<div class="admin-auth-fact">
|
| 849 |
+
<strong>密码来源</strong>
|
| 850 |
+
<span><code>PASSWORD</code></span>
|
| 851 |
+
</div>
|
| 852 |
+
<div class="admin-auth-fact">
|
| 853 |
+
<strong>内容存储</strong>
|
| 854 |
+
<span>当前版本保存到容器本地目录</span>
|
| 855 |
+
</div>
|
| 856 |
+
</div>
|
| 857 |
+
</div>
|
| 858 |
+
<form class="card-base admin-login admin-login-stage" method="post" action={`${entryPath}/login`} on:submit|preventDefault={handleLogin}>
|
| 859 |
+
<div class="admin-login-badge">管理员登录</div>
|
| 860 |
+
<h2>输入管理员凭证</h2>
|
| 861 |
+
<p>登录成功后将进入内容工作台,可继续管理站点配置、文章与图片资源。</p>
|
| 862 |
+
<label class="admin-field">
|
| 863 |
+
<span>管理员账号</span>
|
| 864 |
+
<input class="admin-input" name="username" autocomplete="username" autocapitalize="none" placeholder="管理员账号" bind:value={loginForm.username} required />
|
| 865 |
+
</label>
|
| 866 |
+
<label class="admin-field">
|
| 867 |
+
<span>登录密码</span>
|
| 868 |
+
<input class="admin-input" name="password" autocomplete="current-password" type="password" placeholder="密码" bind:value={loginForm.password} required />
|
| 869 |
+
</label>
|
| 870 |
+
<button class="btn-regular admin-button primary" type="submit" disabled={loginSubmitting}>
|
| 871 |
+
{loginSubmitting ? "登录中..." : "登录后台"}
|
| 872 |
+
</button>
|
| 873 |
+
<p class="admin-login-status">后台凭证直接读取自 Hugging Face Space 环境变量,不写入前端代码。</p>
|
| 874 |
+
{#if sessionRefreshing || sessionLoading}
|
| 875 |
+
<p class="admin-login-status">正在刷新当前后台会话...</p>
|
| 876 |
+
{/if}
|
| 877 |
+
</form>
|
| 878 |
+
</section>
|
| 879 |
{:else}
|
| 880 |
<div class="admin-workbench">
|
| 881 |
<aside class="admin-sidebar">
|
|
|
|
| 1075 |
.admin-panel, .admin-login {
|
| 1076 |
padding: 1.2rem;
|
| 1077 |
}
|
| 1078 |
+
.admin-auth-shell {
|
| 1079 |
+
display: grid;
|
| 1080 |
+
grid-template-columns: minmax(0, 1.15fr) minmax(22rem, 0.85fr);
|
| 1081 |
+
gap: 1rem;
|
| 1082 |
+
align-items: stretch;
|
| 1083 |
+
}
|
| 1084 |
+
.admin-auth-copy {
|
| 1085 |
+
position: relative;
|
| 1086 |
+
overflow: hidden;
|
| 1087 |
+
display: flex;
|
| 1088 |
+
flex-direction: column;
|
| 1089 |
+
justify-content: space-between;
|
| 1090 |
+
gap: 1.4rem;
|
| 1091 |
+
min-height: 22rem;
|
| 1092 |
+
background:
|
| 1093 |
+
radial-gradient(circle at top right, rgb(255 255 255 / 0.18), transparent 32%),
|
| 1094 |
+
linear-gradient(145deg, color-mix(in oklch, var(--card-bg) 78%, white 22%), color-mix(in oklch, var(--card-bg) 92%, var(--btn-card-bg-hover) 8%));
|
| 1095 |
+
border: 1px solid color-mix(in oklch, var(--line-divider) 72%, white 28%);
|
| 1096 |
+
box-shadow: 0 28px 80px rgb(8 20 28 / 0.12);
|
| 1097 |
+
}
|
| 1098 |
+
.admin-auth-intro {
|
| 1099 |
+
display: flex;
|
| 1100 |
+
flex-direction: column;
|
| 1101 |
+
gap: 0.75rem;
|
| 1102 |
+
}
|
| 1103 |
+
.admin-auth-intro h2 {
|
| 1104 |
+
margin: 0;
|
| 1105 |
+
font-size: clamp(2rem, 4vw, 3rem);
|
| 1106 |
+
line-height: 1.08;
|
| 1107 |
+
}
|
| 1108 |
+
.admin-auth-intro p {
|
| 1109 |
+
max-width: 40rem;
|
| 1110 |
+
}
|
| 1111 |
+
.admin-auth-facts {
|
| 1112 |
+
display: grid;
|
| 1113 |
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
| 1114 |
+
gap: 0.8rem;
|
| 1115 |
+
}
|
| 1116 |
+
.admin-auth-fact {
|
| 1117 |
+
display: flex;
|
| 1118 |
+
flex-direction: column;
|
| 1119 |
+
gap: 0.35rem;
|
| 1120 |
+
padding: 0.9rem;
|
| 1121 |
+
border-radius: 1rem;
|
| 1122 |
+
border: 1px solid color-mix(in oklch, var(--line-divider) 74%, white 26%);
|
| 1123 |
+
background: color-mix(in oklch, var(--card-bg) 82%, white 18%);
|
| 1124 |
+
}
|
| 1125 |
+
.admin-auth-fact strong {
|
| 1126 |
+
font-size: 0.95rem;
|
| 1127 |
+
}
|
| 1128 |
+
.admin-auth-fact span {
|
| 1129 |
+
font-size: 0.92rem;
|
| 1130 |
+
color: rgba(0,0,0,.72);
|
| 1131 |
+
}
|
| 1132 |
+
:root.dark .admin-auth-fact span {
|
| 1133 |
+
color: rgba(255,255,255,.72);
|
| 1134 |
+
}
|
| 1135 |
.admin-login {
|
| 1136 |
max-width: 30rem;
|
| 1137 |
margin: 0 auto;
|
|
|
|
| 1139 |
flex-direction: column;
|
| 1140 |
gap: 0.9rem;
|
| 1141 |
}
|
| 1142 |
+
.admin-auth-shell .admin-login {
|
| 1143 |
+
max-width: none;
|
| 1144 |
+
width: 100%;
|
| 1145 |
+
margin: 0;
|
| 1146 |
+
justify-content: center;
|
| 1147 |
+
}
|
| 1148 |
+
.admin-field {
|
| 1149 |
+
display: flex;
|
| 1150 |
+
flex-direction: column;
|
| 1151 |
+
gap: 0.4rem;
|
| 1152 |
+
}
|
| 1153 |
+
.admin-field > span {
|
| 1154 |
+
font-size: 0.88rem;
|
| 1155 |
+
font-weight: 700;
|
| 1156 |
+
}
|
| 1157 |
+
.admin-login-pending {
|
| 1158 |
+
min-height: 22rem;
|
| 1159 |
+
justify-content: center;
|
| 1160 |
+
}
|
| 1161 |
+
.admin-loading-stack {
|
| 1162 |
+
display: flex;
|
| 1163 |
+
flex-direction: column;
|
| 1164 |
+
gap: 0.75rem;
|
| 1165 |
+
}
|
| 1166 |
+
.admin-loading-line {
|
| 1167 |
+
height: 0.9rem;
|
| 1168 |
+
border-radius: 999px;
|
| 1169 |
+
background: linear-gradient(90deg, rgb(255 255 255 / 0.28), rgb(255 255 255 / 0.62), rgb(255 255 255 / 0.22));
|
| 1170 |
+
background-size: 200% 100%;
|
| 1171 |
+
animation: adminPulse 1.8s ease-in-out infinite;
|
| 1172 |
+
}
|
| 1173 |
+
.admin-loading-line.short {
|
| 1174 |
+
width: 48%;
|
| 1175 |
+
}
|
| 1176 |
+
.admin-loading-line.medium {
|
| 1177 |
+
width: 70%;
|
| 1178 |
+
}
|
| 1179 |
.admin-login-stage {
|
| 1180 |
position: relative;
|
| 1181 |
z-index: 6;
|
|
|
|
| 1212 |
.admin-quick-actions { display: flex; gap: 0.6rem; align-items: center; }
|
| 1213 |
.admin-quick-actions.wrap { flex-wrap: wrap; }
|
| 1214 |
.admin-button { min-height: 2.7rem; padding: 0.65rem 1rem; border-radius: 0.95rem; }
|
| 1215 |
+
.admin-button[disabled] { opacity: 0.7; cursor: wait; }
|
| 1216 |
.admin-button.ghost { border: 1px solid var(--line-divider); }
|
| 1217 |
.admin-button.danger { color: oklch(0.62 0.2 25); }
|
| 1218 |
.admin-nav-button, .editor-item, .overview-item {
|
|
|
|
| 1263 |
.build-log { max-height: 22rem; overflow: auto; }
|
| 1264 |
.danger-text { color: oklch(0.62 0.2 25); }
|
| 1265 |
.admin-empty { padding: 2rem; text-align: center; }
|
| 1266 |
+
@keyframes adminPulse {
|
| 1267 |
+
0% {
|
| 1268 |
+
background-position: 0% 50%;
|
| 1269 |
+
}
|
| 1270 |
+
100% {
|
| 1271 |
+
background-position: 100% 50%;
|
| 1272 |
+
}
|
| 1273 |
+
}
|
| 1274 |
@media (max-width: 1200px) {
|
| 1275 |
+
.admin-auth-shell,
|
| 1276 |
+
.admin-auth-facts,
|
| 1277 |
+
.admin-workbench,
|
| 1278 |
+
.two-col,
|
| 1279 |
+
.metrics-grid,
|
| 1280 |
+
.asset-grid,
|
| 1281 |
+
.editor-layout,
|
| 1282 |
+
.posts-layout {
|
| 1283 |
+
grid-template-columns: 1fr;
|
| 1284 |
+
}
|
| 1285 |
}
|
| 1286 |
@media (max-width: 800px) {
|
| 1287 |
.admin-login {
|
src/layouts/AdminLayout.astro
CHANGED
|
@@ -78,8 +78,8 @@ const isWallpaperSwitchable = backgroundWallpaper.switchable ?? true;
|
|
| 78 |
<div class="admin-hero-fallback absolute inset-0" />
|
| 79 |
)}
|
| 80 |
<div class="absolute inset-0 admin-hero-overlay" />
|
| 81 |
-
<div class="
|
| 82 |
-
<div class="admin-hero-copy flex flex-col
|
| 83 |
<div class="inline-flex w-fit items-center gap-2 rounded-full bg-white/15 px-3 py-1 text-sm font-medium backdrop-blur-md">
|
| 84 |
<span class="h-2.5 w-2.5 rounded-full bg-[oklch(0.75_0.14_var(--hue))]" />
|
| 85 |
Firefly Admin
|
|
@@ -93,27 +93,13 @@ const isWallpaperSwitchable = backgroundWallpaper.switchable ?? true;
|
|
| 93 |
</p>
|
| 94 |
</div>
|
| 95 |
</div>
|
| 96 |
-
<div class="admin-hero-side">
|
| 97 |
-
<div class="admin-hero-chip">
|
| 98 |
-
<span>管理入口</span>
|
| 99 |
-
<strong>/admin</strong>
|
| 100 |
-
</div>
|
| 101 |
-
<div class="admin-hero-chip">
|
| 102 |
-
<span>登录方式</span>
|
| 103 |
-
<strong>ADMIN / PASSWORD</strong>
|
| 104 |
-
</div>
|
| 105 |
-
<div class="admin-hero-chip">
|
| 106 |
-
<span>内容存储</span>
|
| 107 |
-
<strong>容器本地目录</strong>
|
| 108 |
-
</div>
|
| 109 |
-
</div>
|
| 110 |
</div>
|
| 111 |
</div>
|
| 112 |
</div>
|
| 113 |
</div>
|
| 114 |
</section>
|
| 115 |
|
| 116 |
-
<main class="relative z-30 -mt-
|
| 117 |
<div class="mx-auto max-w-(--page-width)">
|
| 118 |
<slot />
|
| 119 |
</div>
|
|
@@ -133,7 +119,7 @@ const isWallpaperSwitchable = backgroundWallpaper.switchable ?? true;
|
|
| 133 |
|
| 134 |
.admin-hero-frame {
|
| 135 |
position: relative;
|
| 136 |
-
height: clamp(
|
| 137 |
}
|
| 138 |
|
| 139 |
.admin-hero-media {
|
|
@@ -158,57 +144,18 @@ const isWallpaperSwitchable = backgroundWallpaper.switchable ?? true;
|
|
| 158 |
}
|
| 159 |
|
| 160 |
.admin-hero-grid {
|
| 161 |
-
display: grid;
|
| 162 |
-
align-items: end;
|
| 163 |
-
}
|
| 164 |
-
|
| 165 |
-
.admin-hero-side {
|
| 166 |
-
display: grid;
|
| 167 |
-
gap: 0.75rem;
|
| 168 |
-
align-self: stretch;
|
| 169 |
-
}
|
| 170 |
-
|
| 171 |
-
.admin-hero-chip {
|
| 172 |
display: flex;
|
| 173 |
-
|
| 174 |
-
gap: 0.2rem;
|
| 175 |
-
border: 1px solid rgb(255 255 255 / 0.14);
|
| 176 |
-
border-radius: 1rem;
|
| 177 |
-
background: rgb(255 255 255 / 0.1);
|
| 178 |
-
backdrop-filter: blur(18px);
|
| 179 |
-
padding: 0.9rem 1rem;
|
| 180 |
-
}
|
| 181 |
-
|
| 182 |
-
.admin-hero-chip span {
|
| 183 |
-
font-size: 0.8rem;
|
| 184 |
-
color: rgb(255 255 255 / 0.72);
|
| 185 |
}
|
| 186 |
|
| 187 |
-
.admin-hero-
|
| 188 |
-
|
| 189 |
-
letter-spacing: 0.02em;
|
| 190 |
-
}
|
| 191 |
-
|
| 192 |
-
@media (min-width: 960px) {
|
| 193 |
-
.admin-hero-grid {
|
| 194 |
-
grid-template-columns: minmax(0, 1.35fr) minmax(16rem, 0.75fr);
|
| 195 |
-
}
|
| 196 |
}
|
| 197 |
|
| 198 |
@media (max-width: 959px) {
|
| 199 |
.admin-hero-frame {
|
| 200 |
height: auto;
|
| 201 |
-
min-height:
|
| 202 |
-
}
|
| 203 |
-
|
| 204 |
-
.admin-hero-side {
|
| 205 |
-
grid-template-columns: repeat(3, minmax(0, 1fr));
|
| 206 |
-
}
|
| 207 |
-
}
|
| 208 |
-
|
| 209 |
-
@media (max-width: 720px) {
|
| 210 |
-
.admin-hero-side {
|
| 211 |
-
grid-template-columns: 1fr;
|
| 212 |
}
|
| 213 |
}
|
| 214 |
</style>
|
|
|
|
| 78 |
<div class="admin-hero-fallback absolute inset-0" />
|
| 79 |
)}
|
| 80 |
<div class="absolute inset-0 admin-hero-overlay" />
|
| 81 |
+
<div class="absolute inset-0 admin-hero-grid gap-5 p-6 text-white md:p-8">
|
| 82 |
+
<div class="admin-hero-copy flex max-w-3xl flex-col gap-4">
|
| 83 |
<div class="inline-flex w-fit items-center gap-2 rounded-full bg-white/15 px-3 py-1 text-sm font-medium backdrop-blur-md">
|
| 84 |
<span class="h-2.5 w-2.5 rounded-full bg-[oklch(0.75_0.14_var(--hue))]" />
|
| 85 |
Firefly Admin
|
|
|
|
| 93 |
</p>
|
| 94 |
</div>
|
| 95 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
</div>
|
| 97 |
</div>
|
| 98 |
</div>
|
| 99 |
</div>
|
| 100 |
</section>
|
| 101 |
|
| 102 |
+
<main class="relative z-30 -mt-5 w-full px-2 pb-10 pt-0 md:-mt-6 md:px-4">
|
| 103 |
<div class="mx-auto max-w-(--page-width)">
|
| 104 |
<slot />
|
| 105 |
</div>
|
|
|
|
| 119 |
|
| 120 |
.admin-hero-frame {
|
| 121 |
position: relative;
|
| 122 |
+
height: clamp(15rem, 26vw, 20rem);
|
| 123 |
}
|
| 124 |
|
| 125 |
.admin-hero-media {
|
|
|
|
| 144 |
}
|
| 145 |
|
| 146 |
.admin-hero-grid {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
display: flex;
|
| 148 |
+
align-items: flex-start;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
}
|
| 150 |
|
| 151 |
+
.admin-hero-copy {
|
| 152 |
+
padding-top: clamp(0.5rem, 2vw, 1.25rem);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
}
|
| 154 |
|
| 155 |
@media (max-width: 959px) {
|
| 156 |
.admin-hero-frame {
|
| 157 |
height: auto;
|
| 158 |
+
min-height: 14rem;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
}
|
| 160 |
}
|
| 161 |
</style>
|