357 lines
11 KiB
HTML
357 lines
11 KiB
HTML
<!doctype html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Admin Portal</title>
|
||
<style>
|
||
:root {
|
||
--bg: #f4efe6;
|
||
--panel: rgba(255, 252, 246, 0.92);
|
||
--ink: #1f1a16;
|
||
--muted: #665c53;
|
||
--line: rgba(31, 26, 22, 0.12);
|
||
--accent: #0c6cc9;
|
||
--accent-soft: rgba(12, 108, 201, 0.12);
|
||
--success: #136f46;
|
||
--success-soft: rgba(19, 111, 70, 0.1);
|
||
--warn: #9f6417;
|
||
--warn-soft: rgba(159, 100, 23, 0.12);
|
||
--shadow: 0 24px 70px rgba(46, 37, 28, 0.1);
|
||
--radius: 24px;
|
||
--radius-sm: 16px;
|
||
--font-sans: "IBM Plex Sans", "Noto Sans SC", "PingFang SC", sans-serif;
|
||
}
|
||
|
||
* { box-sizing: border-box; }
|
||
body {
|
||
margin: 0;
|
||
color: var(--ink);
|
||
font-family: var(--font-sans);
|
||
background:
|
||
radial-gradient(circle at top left, rgba(12, 108, 201, 0.18), transparent 24rem),
|
||
radial-gradient(circle at bottom right, rgba(19, 111, 70, 0.12), transparent 28rem),
|
||
linear-gradient(180deg, #f8f3ea 0%, #f1e9dd 100%);
|
||
}
|
||
a { color: inherit; }
|
||
.shell {
|
||
max-width: 1280px;
|
||
margin: 0 auto;
|
||
padding: 34px 20px 64px;
|
||
}
|
||
.topnav {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
margin-bottom: 18px;
|
||
}
|
||
.topnav a {
|
||
text-decoration: none;
|
||
padding: 10px 14px;
|
||
border-radius: 999px;
|
||
border: 1px solid var(--line);
|
||
background: rgba(255,255,255,0.74);
|
||
color: var(--muted);
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
}
|
||
.topnav a.is-current {
|
||
background: var(--ink);
|
||
border-color: var(--ink);
|
||
color: #fff;
|
||
}
|
||
.hero {
|
||
display: grid;
|
||
grid-template-columns: 1.2fr 0.8fr;
|
||
gap: 18px;
|
||
margin-bottom: 18px;
|
||
}
|
||
.card {
|
||
background: var(--panel);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
box-shadow: var(--shadow);
|
||
}
|
||
.hero-card {
|
||
padding: 30px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.hero-card::after {
|
||
content: "";
|
||
position: absolute;
|
||
right: -4rem;
|
||
bottom: -4rem;
|
||
width: 16rem;
|
||
height: 16rem;
|
||
border-radius: 999px;
|
||
background: linear-gradient(135deg, rgba(12, 108, 201, 0.2), rgba(19, 111, 70, 0.06));
|
||
filter: blur(10px);
|
||
}
|
||
.eyebrow {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 8px 12px;
|
||
border-radius: 999px;
|
||
background: var(--accent-soft);
|
||
color: var(--accent);
|
||
font-size: 12px;
|
||
font-weight: 800;
|
||
letter-spacing: 0.06em;
|
||
text-transform: uppercase;
|
||
}
|
||
h1 {
|
||
margin: 18px 0 10px;
|
||
font-size: clamp(34px, 5vw, 50px);
|
||
line-height: 1;
|
||
letter-spacing: -0.05em;
|
||
}
|
||
.hero-copy {
|
||
max-width: 56rem;
|
||
color: var(--muted);
|
||
line-height: 1.75;
|
||
font-size: 16px;
|
||
}
|
||
.hero-points {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
padding: 0;
|
||
margin: 18px 0 0;
|
||
list-style: none;
|
||
}
|
||
.hero-points li {
|
||
padding: 9px 12px;
|
||
border-radius: 999px;
|
||
border: 1px solid var(--line);
|
||
background: rgba(255,255,255,0.74);
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
}
|
||
.stack {
|
||
padding: 24px;
|
||
display: grid;
|
||
gap: 12px;
|
||
align-content: start;
|
||
}
|
||
.metric {
|
||
border-radius: 20px;
|
||
padding: 16px;
|
||
border: 1px solid var(--line);
|
||
background: #fff;
|
||
}
|
||
.metric-label {
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
letter-spacing: 0.05em;
|
||
text-transform: uppercase;
|
||
}
|
||
.metric-value {
|
||
margin-top: 8px;
|
||
font-size: 26px;
|
||
font-weight: 800;
|
||
letter-spacing: -0.04em;
|
||
}
|
||
.grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: 18px;
|
||
}
|
||
.panel {
|
||
padding: 24px;
|
||
}
|
||
.panel h2 {
|
||
margin: 0 0 8px;
|
||
font-size: 24px;
|
||
letter-spacing: -0.04em;
|
||
}
|
||
.panel p {
|
||
margin: 0;
|
||
color: var(--muted);
|
||
line-height: 1.7;
|
||
font-size: 14px;
|
||
}
|
||
.cta-row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
margin-top: 18px;
|
||
}
|
||
.cta {
|
||
text-decoration: none;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 10rem;
|
||
padding: 12px 18px;
|
||
border-radius: 999px;
|
||
font-weight: 800;
|
||
border: 1px solid var(--line);
|
||
transition: transform 120ms ease, background 120ms ease, color 120ms ease;
|
||
}
|
||
.cta:hover { transform: translateY(-1px); }
|
||
.cta.primary { background: var(--ink); color: #fff; border-color: var(--ink); }
|
||
.cta.secondary { background: var(--accent-soft); color: var(--accent); }
|
||
.list {
|
||
margin: 18px 0 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
display: grid;
|
||
gap: 10px;
|
||
}
|
||
.list li {
|
||
padding: 14px 16px;
|
||
border-radius: 18px;
|
||
border: 1px solid var(--line);
|
||
background: rgba(255,255,255,0.8);
|
||
}
|
||
.list strong {
|
||
display: block;
|
||
margin-bottom: 4px;
|
||
font-size: 15px;
|
||
}
|
||
.status-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 12px;
|
||
margin-top: 18px;
|
||
}
|
||
.status-card {
|
||
padding: 16px;
|
||
border-radius: 20px;
|
||
border: 1px solid var(--line);
|
||
background: #fff;
|
||
}
|
||
.status-card strong {
|
||
display: block;
|
||
margin-top: 8px;
|
||
font-size: 24px;
|
||
letter-spacing: -0.04em;
|
||
}
|
||
.status-available {
|
||
background: linear-gradient(180deg, #fff 0%, rgba(19, 111, 70, 0.08) 100%);
|
||
}
|
||
.status-caution {
|
||
background: linear-gradient(180deg, #fff 0%, rgba(159, 100, 23, 0.08) 100%);
|
||
}
|
||
.status-note {
|
||
background: linear-gradient(180deg, #fff 0%, rgba(12, 108, 201, 0.08) 100%);
|
||
}
|
||
code {
|
||
font-family: "IBM Plex Mono", "JetBrains Mono", monospace;
|
||
font-size: 12px;
|
||
}
|
||
@media (max-width: 980px) {
|
||
.hero, .grid, .status-grid { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<main class="shell">
|
||
<nav class="topnav" aria-label="Admin Navigation">
|
||
<a href="/portal/admin/" class="is-current">管理首页</a>
|
||
<a href="/portal/admin/providers.html">新增模型 / 供应商目录</a>
|
||
<a href="/portal/admin/batch-import.html">导入供应商帐号</a>
|
||
<a href="/portal/" target="_blank" rel="noreferrer">用户 Portal</a>
|
||
</nav>
|
||
|
||
<section class="hero">
|
||
<article class="card hero-card">
|
||
<div class="eyebrow">Admin Portal</div>
|
||
<h1>把新增模型与导入帐号收进同一套入口</h1>
|
||
<p class="hero-copy">
|
||
这个入口不再把“新增模型供应商”和“导入供应商帐号”拆散在不同地址。当前版本统一从
|
||
<code>/portal/admin/</code> 进入:一边看 pack/provider 目录、做 preview/import,一边继续保留
|
||
item 级 <code>reused / reactivated / replaced</code> 的 batch-import 结果面板。
|
||
</p>
|
||
<ul class="hero-points">
|
||
<li>默认同域走 <code>/portal-admin-api/</code></li>
|
||
<li>静态页与 CRM API 解耦</li>
|
||
<li>保留旧地址兼容,不打断现有操作</li>
|
||
</ul>
|
||
</article>
|
||
|
||
<aside class="card stack">
|
||
<div class="metric">
|
||
<div class="metric-label">统一入口</div>
|
||
<div class="metric-value">/portal/admin/</div>
|
||
</div>
|
||
<div class="metric">
|
||
<div class="metric-label">Provider 目录</div>
|
||
<div class="metric-value">/providers</div>
|
||
</div>
|
||
<div class="metric">
|
||
<div class="metric-label">Batch Import</div>
|
||
<div class="metric-value">/batch-import</div>
|
||
</div>
|
||
</aside>
|
||
</section>
|
||
|
||
<section class="grid">
|
||
<article class="card panel">
|
||
<h2>新增模型 / 供应商目录</h2>
|
||
<p>
|
||
这页负责浏览已安装 pack、选择 provider、调用 <code>preview-import</code> /
|
||
<code>import</code>,同时提供 provider manifest 草稿生成与发布。当前版本已经支持先保存草稿,再经由 CRM
|
||
服务端写入 pack/provider 文件并自动提交到仓库。
|
||
</p>
|
||
<div class="cta-row">
|
||
<a class="cta primary" href="/portal/admin/providers.html">打开供应商页</a>
|
||
<a class="cta secondary" href="/portal/admin/providers.html#manifest-draft">跳到 manifest 草稿</a>
|
||
</div>
|
||
<ul class="list">
|
||
<li>
|
||
<strong>适用动作</strong>
|
||
查看 pack 与 provider、输入 keys 做 preview/import、生成 provider 草稿,并一键发布到仓库。
|
||
</li>
|
||
<li>
|
||
<strong>默认 API Base</strong>
|
||
<code>https://sub.tksea.top/portal-admin-api</code>
|
||
</li>
|
||
</ul>
|
||
</article>
|
||
|
||
<article class="card panel">
|
||
<h2>导入供应商帐号</h2>
|
||
<p>
|
||
这页继续负责 live batch-import:创建 run、拉取 run summary、查看 item 级别的
|
||
<code>matched_account_state</code> 与 <code>account_resolution</code>。
|
||
</p>
|
||
<div class="cta-row">
|
||
<a class="cta primary" href="/portal/admin/batch-import.html">打开导入页</a>
|
||
<a class="cta secondary" href="/portal/admin-batch-import.html">旧地址兼容入口</a>
|
||
</div>
|
||
<ul class="list">
|
||
<li>
|
||
<strong>适用动作</strong>
|
||
批量导入第三方 key,验证 <code>reused / created / reactivated / replaced</code>。
|
||
</li>
|
||
<li>
|
||
<strong>默认 API Base</strong>
|
||
<code>https://sub.tksea.top/portal-admin-api</code>
|
||
</li>
|
||
</ul>
|
||
</article>
|
||
</section>
|
||
|
||
<section class="status-grid">
|
||
<article class="status-card status-available">
|
||
<div class="metric-label">可立即使用</div>
|
||
<strong>Provider 浏览 + 导入</strong>
|
||
<p>依赖现有 <code>/api/packs</code>、<code>/api/providers/*</code>、<code>/api/batch-import/*</code> 即可完成。</p>
|
||
</article>
|
||
<article class="status-card status-note">
|
||
<div class="metric-label">当前边界</div>
|
||
<strong>浏览器提交到 CRM,再由 CRM 写仓库</strong>
|
||
<p>页面不会直接拼 Git 命令;所有写 pack/provider 与提交仓库的动作,都统一走 CRM 服务端的发布接口。</p>
|
||
</article>
|
||
<article class="status-card status-caution">
|
||
<div class="metric-label">安全前提</div>
|
||
<strong>仍需 Admin Token</strong>
|
||
<p>CRM 的 API 权限仍由 Bearer token 控制,同域反代只解决浏览器可达性,不降低鉴权门槛。</p>
|
||
</article>
|
||
</section>
|
||
</main>
|
||
</body>
|
||
</html>
|