mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 00:48:00 +00:00
fix(ui): narrow-screen header (hide brand name, icon-only nav, scrollable), wrap wide tables for horizontal scroll, add favicon
This commit is contained in:
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>ModelRouter</title>
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 34 34'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%233f6ef5'/%3E%3Cstop offset='1' stop-color='%236a8ffb'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='34' height='34' rx='10' fill='url(%23g)'/%3E%3Ctext x='17' y='24' text-anchor='middle' font-family='Arial,sans-serif' font-size='17' font-weight='700' fill='%23fff'%3EM%3C/text%3E%3C/svg%3E">
|
||||
<style>
|
||||
:root{
|
||||
--bg:#f4f6fb; --card:#ffffff; --card2:#f9fafd; --line:#e3e6f0; --fg:#1d2434; --muted:#66708a;
|
||||
@ -38,6 +39,7 @@ header { display:flex; align-items:center; gap:14px; padding:14px 24px; border-b
|
||||
nav { display:flex; gap:6px; padding:0; }
|
||||
nav button { background:transparent; border:1px solid transparent; color:var(--muted); padding:7px 16px;
|
||||
cursor:pointer; border-radius:9px; font:inherit; font-weight:500; transition:all .15s; }
|
||||
nav button .ni { font-style:normal; display:none; }
|
||||
nav button:hover { color:var(--fg); background:var(--card2); }
|
||||
nav button.active { background:var(--card2); border-color:var(--line); color:var(--accent); box-shadow:var(--shadow); }
|
||||
main { padding:20px 24px 48px; max-width:1240px; margin:0 auto; }
|
||||
@ -343,16 +345,22 @@ html[data-theme="dark"] .dropzone.dragover, html[data-theme="dark"] .dropzone:ho
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
header { gap:8px; padding:10px 12px; }
|
||||
.brand h1 { font-size:15px; }
|
||||
.brand h1 { display:none; }
|
||||
.brand .sub { display:none; }
|
||||
.hd-actions button { padding:5px 9px; }
|
||||
nav { gap:4px; padding:10px 12px 0; }
|
||||
nav { gap:4px; padding:10px 12px 0; flex:1 1 auto; min-width:0; }
|
||||
nav button { padding:6px 10px; font-size:13px; }
|
||||
nav button .ni { display:inline-block; }
|
||||
nav button span { display:none; }
|
||||
#btn-logout .ni { display:none; }
|
||||
#btn-lang span, #btn-logout span { display:none; }
|
||||
#btn-logout .ni { display:inline-block; font-style:normal; }
|
||||
main { padding:12px 12px 32px; }
|
||||
.card { padding:13px; border-radius:12px; margin-bottom:14px; }
|
||||
.card h2 { font-size:13px; }
|
||||
th,td { padding:8px 10px; }
|
||||
.row { flex-direction:column; gap:0; }
|
||||
.dash-row { flex-direction:column; }
|
||||
.model-row { flex-wrap:wrap; }
|
||||
.model-row input { flex:1 1 120px; }
|
||||
.model-row select { flex:0 0 auto; }
|
||||
@ -384,18 +392,18 @@ html[data-theme="dark"] .dropzone.dragover, html[data-theme="dark"] .dropzone:ho
|
||||
<div><h1>ModelRouter</h1><div class="sub" data-i="tagline">统一 LLM 网关</div></div>
|
||||
</div>
|
||||
<nav>
|
||||
<button data-tab="status" class="active" data-i="navStatus">状态</button>
|
||||
<button data-tab="chat" data-i="navChat">Chat 测试</button>
|
||||
<button data-tab="keys" data-i="navKeys">密钥</button>
|
||||
<button data-tab="sort" data-i="navSort">优先级</button>
|
||||
<button data-tab="sources" data-i="navSources">源</button>
|
||||
<button data-tab="adapters" data-i="navAdapters">适配器</button>
|
||||
<button data-tab="status" class="active"><i class="ni" aria-hidden="true">◉</i><span data-i="navStatus">状态</span></button>
|
||||
<button data-tab="chat"><i class="ni" aria-hidden="true">▤</i><span data-i="navChat">对话</span></button>
|
||||
<button data-tab="keys"><i class="ni" aria-hidden="true">⚿</i><span data-i="navKeys">密钥</span></button>
|
||||
<button data-tab="sort"><i class="ni" aria-hidden="true">⇅</i><span data-i="navSort">优先级</span></button>
|
||||
<button data-tab="sources"><i class="ni" aria-hidden="true">⛁</i><span data-i="navSources">源</span></button>
|
||||
<button data-tab="adapters"><i class="ni" aria-hidden="true">▦</i><span data-i="navAdapters">适配器</span></button>
|
||||
</nav>
|
||||
<div class="spacer"></div>
|
||||
<div class="hd-actions">
|
||||
<button id="btn-theme" title="theme">◐</button>
|
||||
<button id="btn-lang">EN</button>
|
||||
<button class="ghost" id="btn-logout" data-i="logout">退出登录</button>
|
||||
<button class="ghost" id="btn-logout"><i class="ni">⏻</i><span data-i="logout">退出登录</span></button>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
@ -607,7 +615,7 @@ async function renderStatus() {
|
||||
<div class="chips" id="model-chips"></div>
|
||||
</div>
|
||||
<div class="card"><h2>${t('srcTitle')} (${s.sources.length})</h2>
|
||||
<table><tr><th>${t('tName')}</th><th>${t('tAdapter')}</th><th>${t('tModels')}</th><th>${t('tURL')}</th><th>${t('tConn')}</th><th>${t('tConc')}</th></tr>${srcRows || `<tr><td colspan="6" class="empty">${t('srcEmpty')}</td></tr>`}</table>
|
||||
<div class="tbl-wrap"><table><tr><th>${t('tName')}</th><th>${t('tAdapter')}</th><th>${t('tModels')}</th><th>${t('tURL')}</th><th>${t('tConn')}</th><th>${t('tConc')}</th></tr>${srcRows || `<tr><td colspan="6" class="empty">${t('srcEmpty')}</td></tr>`}</table></div>
|
||||
</div>
|
||||
<div class="dash-row">
|
||||
<div class="card"><h2>${t('dashModel')}</h2><div id="tb-model"></div></div>
|
||||
@ -622,8 +630,8 @@ async function renderStatus() {
|
||||
<div class="recs-scroll" id="tb-recs"></div>
|
||||
</div>
|
||||
<div class="card"><h2>${t('adTitle')} (${s.adapters.length})</h2>
|
||||
<table><tr><th>${t('tName')}</th><th>${t('tVersion')}</th></tr>
|
||||
${s.adapters.map(a => `<tr><td>${esc(a.name)}</td><td>${esc(a.version || '')}</td></tr>`).join('')}</table>
|
||||
<div class="tbl-wrap"><table><tr><th>${t('tName')}</th><th>${t('tVersion')}</th></tr>
|
||||
${s.adapters.map(a => `<tr><td>${esc(a.name)}</td><td>${esc(a.version || '')}</td></tr>`).join('')}</table></div>
|
||||
</div>`;
|
||||
$('#conncfg').textContent = '';
|
||||
showModelConfig(null, '');
|
||||
@ -997,8 +1005,8 @@ async function renderSources() {
|
||||
$('#tab-sources').innerHTML = `
|
||||
<div class="card"><h2><span>${t('srcTitle')}</span><span class="grow"></span>
|
||||
<button class="small" onclick="editSource('')">${t('srcAdd')}</button></h2>
|
||||
<table><tr><th>${t('tName')}</th><th>${t('tURL')}</th><th>${t('tAdapter')}</th><th>${t('tModels')}</th><th></th></tr>
|
||||
${rows || `<tr><td colspan="5" class="empty">${t('srcEmpty')}</td></tr>`}</table>
|
||||
<div class="tbl-wrap"><table><tr><th>${t('tName')}</th><th>${t('tURL')}</th><th>${t('tAdapter')}</th><th>${t('tModels')}</th><th></th></tr>
|
||||
${rows || `<tr><td colspan="5" class="empty">${t('srcEmpty')}</td></tr>`}</table></div>
|
||||
</div>`;
|
||||
}
|
||||
function editSource(name) {
|
||||
@ -1625,8 +1633,8 @@ async function renderAdapters() {
|
||||
<td><button class="danger small" onclick="delAdapter('${escAttr(a.name)}')">${t('srcDel')}</button></td></tr>`).join('');
|
||||
$('#tab-adapters').innerHTML = `
|
||||
<div class="card"><h2>${t('adTitle')} (${j.adapters.length})</h2>
|
||||
<table><tr><th>${t('tName')}</th><th>${t('tVersion')}</th><th></th></tr>
|
||||
${rows || `<tr><td colspan="3" class="empty">${t('adEmpty')}</td></tr>`}</table>
|
||||
<div class="tbl-wrap"><table><tr><th>${t('tName')}</th><th>${t('tVersion')}</th><th></th></tr>
|
||||
${rows || `<tr><td colspan="3" class="empty">${t('adEmpty')}</td></tr>`}</table></div>
|
||||
</div>
|
||||
<div class="card"><h2>${t('uploadTitle')}</h2>
|
||||
<div class="dropzone" id="dz">${t('dropHint')}</div>
|
||||
@ -1704,13 +1712,13 @@ async function renderKeys() {
|
||||
async function renderKeysUser(me) {
|
||||
$('#tab-keys').innerHTML = `
|
||||
<div class="card"><h2>${t('kMeTitle')}</h2>
|
||||
<table><tr><th>${t('kName')}</th><th>${t('kMeRole')}</th><th>${t('kKey')}</th><th>${t('kMeModels')}</th></tr>
|
||||
<div class="tbl-wrap"><table><tr><th>${t('kName')}</th><th>${t('kMeRole')}</th><th>${t('kKey')}</th><th>${t('kMeModels')}</th></tr>
|
||||
<tr><td><b>${esc(me.name || '—')}</b></td><td>${roleTag(me.role)}</td>
|
||||
<td><span class="kr-key">${esc(me.key)}</span>
|
||||
<button class="ghost small" onclick="copyText('${escAttr(me.key)}')">${t('kCopy')}</button></td>
|
||||
<td>${(me.models && me.models.length)
|
||||
? me.models.map(m => { const src = normSrc(m.source); return `<span class="tag tag-blue" title="${m.token_quota ? 'quota ' + fmtQuota(m.token_quota) : t('kQuotaUnlim')}">${esc(m.model)}${src ? ' · ' + esc(src) : ''}${m.token_quota ? ' · ' + esc(fmtQuota(m.token_quota)) : ''}</span>` }).join('')
|
||||
: `<span class="scope-unlim">${t('kAll')}</span>`}</td></tr></table>
|
||||
: `<span class="scope-unlim">${t('kAll')}</span>`}</td></tr></table></div>
|
||||
<div class="muted" style="margin-top:10px">${t('kMeHint')}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user