feat: 大地图跟随机器人并支持可调图幅,新增定时消息,控制台界面重做并适配移动端
大地图跟随机器人 + 可调图幅 - 爬取与显示区域改为以机器人当前位置为中心(bigMap.followBot 默认开启), 不再傻傻地爬世界原点周边 - 界面新增「图幅」下拉(±512/1024/2048/4096/8192/全世界)与「爬取周边」按钮, 只爬当前图幅内的瓦片:层级 0 每张覆盖 4096 格,±4096 只需 9 张,全世界要 100 张 - POST /api/bigmap/crawl 支持 centerX/centerZ/rangeBlocks;GET /api/bigmap 支持 rangeBlocks/bot 查询参数;启动预爬取会先等机器人进入世界再以其位置为中心 领土地图以机器人所在邦国为中心 - 之前初始视图用的是控制器的 playerChunk,而任务启动前它是 (0,0), 导致地图永远停在世界原点附近。现在按「机器人所在区块 → 包含它的邦国 → 该邦国王城/外接矩形」定位 定时重复发送消息(src/bot/scheduledMessenger.js) - 每个机器人可配多条定时任务,按固定间隔重复发送同一内容,如每 300 秒 /u restore confirm - 以 / 开头按游戏指令发送,否则按公屏消息发送 - 只在已连接且通过登录验证时发送;未就绪的周期顺延且不补发,避免重连后一次性连发 - 与指令节流队列共用,间隔下限 5 秒、单机器人最多 20 条 - 控制台新增「定时消息」卡片(新增/删除/启停 + 剩余秒数倒计时) 控制台界面重做 + 移动端适配 - 重新设计的深色主题:统一圆角、阴影、焦点态与悬停态,卡片式分层 - 响应式三档:≥1200px 三栏;≤1200px 两栏;≤820px 单栏 + 底部导航 - 移动端:触控目标 ≥36px、地图/日志高度按视口自适应、避让底部安全区、 横屏压矮地图、尊重「减少动效」偏好、Toast 全宽 - 「添加玩家」折叠进 details;顶栏窄屏自动换行且按钮等分 修复 - POST /api/bigmap/crawl 响应把中心与图幅字段传错(实际爬取正确,返回值显示成原点) - 定时任务保存后接口返回原始列表而非归一化结果,前端拿不到剩余秒数 测试 308 -> 325,全部通过。
这个提交包含在:
+360
-180
@@ -1,16 +1,38 @@
|
||||
/* ==========================================================================
|
||||
AutoStore Bot 控制台样式
|
||||
设计要点:
|
||||
- 深色主题,卡片式布局,三层栅格(左状态 / 中地图日志 / 右配置)
|
||||
- 响应式:≤1200px 收成两栏,≤820px 收成单栏并启用底部导航
|
||||
- 移动端:触控目标 ≥ 36px、地图自适应高度、底部安全区避让
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
--bg: #0d1117;
|
||||
--panel: #161b22;
|
||||
--panel-2: #1c2330;
|
||||
--border: #30363d;
|
||||
--text: #c9d1d9;
|
||||
--muted: #8b949e;
|
||||
--bg: #0b0f16;
|
||||
--bg-soft: #10151d;
|
||||
--panel: #151b25;
|
||||
--panel-2: #1b2230;
|
||||
--panel-3: #212a3a;
|
||||
--border: #2b3444;
|
||||
--border-soft: #222a37;
|
||||
--text: #d7dee8;
|
||||
--text-dim: #9aa7b8;
|
||||
--muted: #6b7787;
|
||||
|
||||
--accent: #e94560;
|
||||
--accent-2: #2f81f7;
|
||||
--accent-hover: #f2586f;
|
||||
--accent-2: #3b8ef7;
|
||||
--accent-2-hover: #5aa0f9;
|
||||
--green: #3fb950;
|
||||
--yellow: #d29922;
|
||||
--yellow: #d6a01f;
|
||||
--red: #f85149;
|
||||
--purple: #bc8cff;
|
||||
|
||||
--radius-sm: 6px;
|
||||
--radius: 10px;
|
||||
--radius-lg: 14px;
|
||||
--shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px -12px rgba(0, 0, 0, .5);
|
||||
--gap: 12px;
|
||||
--topbar-h: 58px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
@@ -18,114 +40,141 @@
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
background: var(--bg);
|
||||
min-height: 100%;
|
||||
background: radial-gradient(1200px 600px at 20% -10%, #16202e 0%, var(--bg) 55%);
|
||||
color: var(--text);
|
||||
font-family: "Segoe UI", "Microsoft YaHei", system-ui, -apple-system, sans-serif;
|
||||
font-family: "Segoe UI", "Microsoft YaHei", system-ui, -apple-system, "Helvetica Neue", sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
.mono { font-family: Consolas, "Cascadia Mono", monospace; }
|
||||
.mono { font-family: Consolas, "Cascadia Mono", ui-monospace, monospace; }
|
||||
.small { font-size: 12px; }
|
||||
.hidden { display: none !important; }
|
||||
.error-text { color: var(--red); }
|
||||
|
||||
/* ==================== 顶栏 ==================== */
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 30;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 18px;
|
||||
background: linear-gradient(90deg, #161b22, #1b2230);
|
||||
border-bottom: 1px solid var(--border);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
padding: 10px 16px;
|
||||
background: rgba(16, 21, 29, .92);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--border-soft);
|
||||
}
|
||||
.brand { display: flex; align-items: center; gap: 12px; }
|
||||
|
||||
.brand { display: flex; align-items: center; gap: 11px; min-width: 0; }
|
||||
.logo {
|
||||
width: 38px; height: 38px;
|
||||
width: 36px; height: 36px;
|
||||
display: grid; place-items: center;
|
||||
border-radius: 9px;
|
||||
background: var(--accent);
|
||||
color: #fff; font-weight: 700; letter-spacing: 1px;
|
||||
border-radius: var(--radius);
|
||||
background: linear-gradient(135deg, var(--accent), #b5324a);
|
||||
color: #fff; font-weight: 700; letter-spacing: .5px;
|
||||
box-shadow: 0 4px 14px -6px rgba(233, 69, 96, .9);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.brand h1 { font-size: 16px; margin: 0; }
|
||||
.brand p { font-size: 12px; margin: 2px 0 0; color: var(--muted); }
|
||||
.topbar-right { display: flex; align-items: center; gap: 8px; }
|
||||
.brand-text { min-width: 0; }
|
||||
.brand h1 { font-size: 15px; margin: 0; white-space: nowrap; }
|
||||
.brand p { font-size: 11.5px; margin: 1px 0 0; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
|
||||
.topbar-right { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
||||
.badges { display: flex; gap: 6px; flex-wrap: wrap; }
|
||||
.topbar-actions { display: flex; gap: 6px; flex-wrap: wrap; }
|
||||
|
||||
.badge {
|
||||
padding: 4px 10px;
|
||||
padding: 3px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-size: 11.5px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--panel-2);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.badge-online { color: var(--green); border-color: #1f6f34; }
|
||||
.badge-online { color: var(--green); border-color: #1f6f34; background: rgba(63, 185, 80, .1); }
|
||||
.badge-offline { color: var(--muted); }
|
||||
.badge-running { color: var(--accent-2); border-color: #1f4f8f; }
|
||||
.badge-running { color: var(--accent-2); border-color: #1f4f8f; background: rgba(59, 142, 247, .12); }
|
||||
.badge-idle { color: var(--muted); }
|
||||
.badge-error { color: var(--red); border-color: #8f2b26; }
|
||||
|
||||
/* ==================== 按钮 ==================== */
|
||||
.btn {
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--panel-2);
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: background .15s, border-color .15s;
|
||||
font-family: inherit;
|
||||
white-space: nowrap;
|
||||
transition: background .15s, border-color .15s, transform .06s, opacity .15s;
|
||||
}
|
||||
.btn:hover { background: #232b39; border-color: #48505e; }
|
||||
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
|
||||
.btn-primary:hover { background: #d63851; border-color: #d63851; }
|
||||
.btn-danger { background: #3a1d22; border-color: #7a2b34; color: #ff8b98; }
|
||||
.btn-danger:hover { background: #4a2329; }
|
||||
.btn:hover:not(:disabled) { background: var(--panel-3); border-color: #3c4759; }
|
||||
.btn:active:not(:disabled) { transform: translateY(1px); }
|
||||
.btn:disabled { opacity: .55; cursor: not-allowed; }
|
||||
.btn-primary { background: var(--accent-2); border-color: var(--accent-2); color: #fff; }
|
||||
.btn-primary:hover:not(:disabled) { background: var(--accent-2-hover); border-color: var(--accent-2-hover); }
|
||||
.btn-danger { background: rgba(248, 81, 73, .12); border-color: #7a2b34; color: #ff9aa2; }
|
||||
.btn-danger:hover:not(:disabled) { background: rgba(248, 81, 73, .2); }
|
||||
.btn-sm { padding: 4px 9px; font-size: 12px; }
|
||||
.btn-block { width: 100%; }
|
||||
.btn-grow { flex: 1 1 auto; }
|
||||
|
||||
/* ==================== 布局 ==================== */
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-columns: 320px minmax(460px, 1fr) 380px;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
grid-template-columns: 320px minmax(420px, 1fr) 380px;
|
||||
gap: var(--gap);
|
||||
padding: var(--gap);
|
||||
align-items: start;
|
||||
}
|
||||
.column { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
|
||||
.column { display: flex; flex-direction: column; gap: var(--gap); min-width: 0; }
|
||||
|
||||
/* ==================== 卡片 ==================== */
|
||||
.card {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
background: linear-gradient(180deg, var(--panel) 0%, var(--bg-soft) 100%);
|
||||
border: 1px solid var(--border-soft);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 13px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
.card h2 {
|
||||
font-size: 13px;
|
||||
margin: 0 0 8px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
margin: 0 0 9px;
|
||||
color: var(--text-dim);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .6px;
|
||||
letter-spacing: .7px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.card-head {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
gap: 8px; margin-bottom: 8px;
|
||||
gap: 8px; margin-bottom: 9px;
|
||||
}
|
||||
.card-head h2 { margin: 0; }
|
||||
.card-head-wrap { flex-wrap: wrap; }
|
||||
.row { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-top: 8px; }
|
||||
.row-compact { margin-top: 6px; }
|
||||
|
||||
/* ==================== 键值表 ==================== */
|
||||
.kv {
|
||||
display: grid;
|
||||
grid-template-columns: 88px 1fr;
|
||||
grid-template-columns: 92px 1fr;
|
||||
gap: 4px 8px;
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
.kv dt { color: var(--muted); }
|
||||
.kv dd { margin: 0; word-break: break-all; }
|
||||
.kv dd { margin: 0; word-break: break-word; }
|
||||
|
||||
.progress {
|
||||
margin-top: 10px; height: 6px; border-radius: 3px;
|
||||
background: #0b0f14; overflow: hidden;
|
||||
background: #080b11; overflow: hidden;
|
||||
}
|
||||
.progress-fill {
|
||||
height: 100%; width: 0%;
|
||||
@@ -136,194 +185,325 @@ html, body {
|
||||
.issues { margin: 8px 0 0; padding-left: 18px; color: var(--yellow); font-size: 12px; }
|
||||
.issues li { margin-bottom: 3px; }
|
||||
|
||||
/* ==================== 机器人列表 ==================== */
|
||||
.bot-list { display: flex; flex-direction: column; gap: 6px; }
|
||||
.bot-item {
|
||||
display: grid;
|
||||
grid-template-columns: 10px minmax(0, 1fr) auto;
|
||||
gap: 9px; align-items: center;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid var(--border-soft);
|
||||
border-radius: var(--radius);
|
||||
background: var(--panel-2);
|
||||
cursor: pointer;
|
||||
transition: border-color .15s, background .15s;
|
||||
}
|
||||
.bot-item:hover { border-color: #3c4759; }
|
||||
.bot-item.active { border-color: var(--accent-2); background: rgba(59, 142, 247, .08); }
|
||||
.bot-item .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--muted); }
|
||||
.bot-item .dot.online { background: var(--green); box-shadow: 0 0 8px rgba(63, 185, 80, .7); }
|
||||
.bot-item .dot.connecting { background: var(--yellow); }
|
||||
.bot-item .dot.offline { background: var(--red); }
|
||||
.bot-item .dot.disabled { background: #454e5e; }
|
||||
.bot-item .name { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.bot-item .sub { font-size: 11px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.bot-item .meta { font-size: 11px; color: var(--muted); text-align: right; white-space: nowrap; }
|
||||
.bot-item .remove {
|
||||
margin-left: 6px; padding: 0 6px;
|
||||
border: 1px solid var(--border); border-radius: 4px;
|
||||
background: transparent; color: var(--muted); cursor: pointer;
|
||||
font-size: 13px; line-height: 18px;
|
||||
}
|
||||
.bot-item .remove:hover { color: var(--red); border-color: #7a2b34; }
|
||||
|
||||
.add-bot { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border); }
|
||||
.add-bot summary {
|
||||
cursor: pointer; font-size: 12.5px; color: var(--accent-2);
|
||||
list-style: none; user-select: none;
|
||||
}
|
||||
.add-bot summary::-webkit-details-marker { display: none; }
|
||||
.add-bot summary::before { content: '▸ '; }
|
||||
.add-bot[open] summary::before { content: '▾ '; }
|
||||
.add-bot .field { margin-top: 8px; }
|
||||
.add-bot .hint { font-size: 11px; color: var(--muted); margin: 8px 0 0; }
|
||||
|
||||
/* ==================== 定时消息 ==================== */
|
||||
.schedule-list { display: flex; flex-direction: column; gap: 6px; }
|
||||
.schedule-item {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto auto;
|
||||
gap: 8px; align-items: center;
|
||||
padding: 7px 9px;
|
||||
border: 1px solid var(--border-soft);
|
||||
border-radius: var(--radius);
|
||||
background: var(--panel-2);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
.schedule-item.off { opacity: .55; }
|
||||
.schedule-item .content { min-width: 0; }
|
||||
.schedule-item .content code {
|
||||
display: block;
|
||||
font-family: Consolas, ui-monospace, monospace;
|
||||
color: var(--text);
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
.schedule-item .sub { font-size: 11px; color: var(--muted); }
|
||||
.schedule-item .remove {
|
||||
padding: 0 6px; border: 1px solid var(--border); border-radius: 4px;
|
||||
background: transparent; color: var(--muted); cursor: pointer; line-height: 18px;
|
||||
}
|
||||
.schedule-item .remove:hover { color: var(--red); border-color: #7a2b34; }
|
||||
.schedule-empty { font-size: 12px; color: var(--muted); padding: 4px 0; }
|
||||
.schedule-editor {
|
||||
margin-top: 10px; padding-top: 10px;
|
||||
border-top: 1px dashed var(--border);
|
||||
}
|
||||
.schedule-editor .hint { font-size: 11px; color: var(--muted); margin: 6px 0 0; }
|
||||
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
|
||||
|
||||
/* ==================== 地图 ==================== */
|
||||
.card-map { display: flex; flex-direction: column; }
|
||||
#mapCanvas {
|
||||
width: 100%;
|
||||
height: 440px;
|
||||
height: clamp(300px, 46vh, 520px);
|
||||
background: #05070a;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-soft);
|
||||
border-radius: var(--radius);
|
||||
display: block;
|
||||
cursor: grab;
|
||||
touch-action: none;
|
||||
}
|
||||
#mapCanvas:active { cursor: grabbing; }
|
||||
.map-legend {
|
||||
display: flex; gap: 14px; flex-wrap: wrap;
|
||||
margin-top: 8px; font-size: 12px; color: var(--muted);
|
||||
display: flex; gap: 12px; flex-wrap: wrap;
|
||||
margin-top: 8px; font-size: 11.5px; color: var(--muted);
|
||||
align-items: center;
|
||||
}
|
||||
.sw {
|
||||
display: inline-block; width: 10px; height: 10px;
|
||||
border-radius: 2px; margin-right: 4px; vertical-align: -1px;
|
||||
}
|
||||
.sw-self { background: #2f81f7; }
|
||||
.sw-self { background: #3b8ef7; }
|
||||
.sw-session { background: #3fb950; }
|
||||
.sw-flying { background: #f85149; }
|
||||
.sw-target { background: #d29922; }
|
||||
.sw-target { background: #d6a01f; }
|
||||
.sw-player { background: #ffffff; }
|
||||
.sw-capital { background: #bc8cff; }
|
||||
.sw-bot { background: #ffd33d; }
|
||||
|
||||
.chk { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--muted); cursor: pointer; }
|
||||
.hidden { display: none !important; }
|
||||
.inline-field { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
|
||||
.input-sm { width: auto; min-width: 110px; padding: 4px 8px; font-size: 12px; }
|
||||
.tabs-inline { margin-bottom: 0; }
|
||||
.error-text { color: var(--red); }
|
||||
|
||||
/* ==================== 机器人列表 ==================== */
|
||||
.bot-list { display: flex; flex-direction: column; gap: 6px; }
|
||||
.bot-item {
|
||||
display: grid;
|
||||
grid-template-columns: 10px 1fr auto;
|
||||
gap: 8px; align-items: center;
|
||||
padding: 7px 9px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
background: var(--panel-2);
|
||||
cursor: pointer;
|
||||
}
|
||||
.bot-item:hover { border-color: #48505e; }
|
||||
.bot-item.active { border-color: var(--accent-2); box-shadow: 0 0 0 1px rgba(47,129,247,.35); }
|
||||
.bot-item .dot {
|
||||
width: 9px; height: 9px; border-radius: 50%; background: var(--muted);
|
||||
}
|
||||
.bot-item .dot.online { background: var(--green); }
|
||||
.bot-item .dot.connecting { background: var(--yellow); }
|
||||
.bot-item .dot.offline { background: var(--red); }
|
||||
.bot-item .dot.disabled { background: #484f58; }
|
||||
.bot-item .name { font-size: 13px; }
|
||||
.bot-item .sub { font-size: 11px; color: var(--muted); }
|
||||
.bot-item .meta { font-size: 11px; color: var(--muted); text-align: right; white-space: nowrap; }
|
||||
.bot-item .remove {
|
||||
margin-left: 6px;
|
||||
padding: 0 5px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
.bot-item .remove:hover { color: var(--red); border-color: #7a2b34; }
|
||||
|
||||
.add-bot { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border); }
|
||||
.add-bot .row { margin-top: 0; }
|
||||
.add-bot .input-sm { flex: 1 1 60px; min-width: 0; }
|
||||
.add-bot .hint { font-size: 11px; color: #5d6672; margin: 6px 0 0; }
|
||||
|
||||
/* ==================== 公屏聊天 ==================== */
|
||||
.chat-line { display: block; }
|
||||
.chat-time { color: #586069; margin-right: 6px; }
|
||||
.chat-bot { color: var(--accent-2); margin-right: 4px; }
|
||||
.chat-user { color: var(--purple); margin-right: 4px; }
|
||||
.chat-out { color: var(--green); }
|
||||
.chat-command { color: var(--yellow); }
|
||||
.chat-system { color: var(--muted); }
|
||||
.chat-action { color: #6e7681; }
|
||||
|
||||
/* ==================== 开拓日志 ==================== */
|
||||
#expView { white-space: normal; padding: 0; }
|
||||
.exp-table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
||||
.exp-table th {
|
||||
position: sticky; top: 0;
|
||||
text-align: left; color: var(--muted); font-weight: 500;
|
||||
background: #05070a; padding: 4px 6px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.exp-table td { padding: 3px 6px; border-bottom: 1px solid #12161d; white-space: nowrap; }
|
||||
.exp-table tbody tr:hover { background: #0e131a; }
|
||||
.exp-empty { color: var(--muted); }
|
||||
|
||||
.config-scope { margin-bottom: 8px; }
|
||||
|
||||
/* ==================== 登录遮罩 ==================== */
|
||||
.overlay {
|
||||
position: fixed; inset: 0; z-index: 200;
|
||||
display: grid; place-items: center;
|
||||
background: rgba(5, 7, 10, .78);
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
.overlay-box {
|
||||
width: min(360px, 92vw);
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 18px;
|
||||
}
|
||||
.overlay-box h2 { font-size: 15px; margin: 0 0 6px; }
|
||||
.overlay-box .input { margin: 10px 0; }
|
||||
|
||||
/* ==================== 日志 ==================== */
|
||||
/* ==================== 日志 / 聊天 / 表格 ==================== */
|
||||
.card-log { display: flex; flex-direction: column; }
|
||||
.log-view {
|
||||
height: 300px;
|
||||
height: clamp(220px, 34vh, 380px);
|
||||
overflow-y: auto;
|
||||
background: #05070a;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
font-family: Consolas, "Cascadia Mono", monospace;
|
||||
border: 1px solid var(--border-soft);
|
||||
border-radius: var(--radius);
|
||||
padding: 8px 10px;
|
||||
font-family: Consolas, "Cascadia Mono", ui-monospace, monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
.log-line { display: block; }
|
||||
.log-time { color: #586069; margin-right: 6px; }
|
||||
.log-time { color: #55606f; margin-right: 6px; }
|
||||
.log-info { color: var(--text); }
|
||||
.log-warn { color: var(--yellow); }
|
||||
.log-error { color: var(--red); }
|
||||
.log-success { color: var(--green); }
|
||||
|
||||
/* ==================== 配置 ==================== */
|
||||
.chat-line { display: block; }
|
||||
.chat-time { color: #55606f; margin-right: 6px; }
|
||||
.chat-bot { color: var(--accent-2); margin-right: 4px; }
|
||||
.chat-user { color: var(--purple); margin-right: 4px; }
|
||||
.chat-out { color: var(--green); }
|
||||
.chat-command { color: var(--yellow); }
|
||||
.chat-system { color: var(--text-dim); }
|
||||
.chat-action { color: var(--muted); }
|
||||
|
||||
.log-table { white-space: normal; padding: 0; }
|
||||
.exp-table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
||||
.exp-table th {
|
||||
position: sticky; top: 0; z-index: 1;
|
||||
text-align: left; color: var(--text-dim); font-weight: 600;
|
||||
background: #05070a; padding: 5px 7px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.exp-table td { padding: 3px 7px; border-bottom: 1px solid #10151d; white-space: nowrap; }
|
||||
.exp-table tbody tr:hover { background: #0d1219; }
|
||||
.exp-empty { color: var(--muted); padding: 8px; }
|
||||
|
||||
/* ==================== 表单 ==================== */
|
||||
.card-config { display: flex; flex-direction: column; }
|
||||
.tabs { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 8px; }
|
||||
.config-scope { margin-bottom: 8px; }
|
||||
.tabs { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 9px; }
|
||||
.tabs-inline { margin-bottom: 0; }
|
||||
.tab {
|
||||
padding: 4px 10px; font-size: 12px; cursor: pointer;
|
||||
background: var(--panel-2); border: 1px solid var(--border);
|
||||
border-radius: 6px; color: var(--muted);
|
||||
background: var(--panel-2); border: 1px solid var(--border-soft);
|
||||
border-radius: 999px; color: var(--muted);
|
||||
font-family: inherit;
|
||||
transition: background .15s, color .15s, border-color .15s;
|
||||
}
|
||||
.tab:hover { color: var(--text); }
|
||||
.tab.active { color: #fff; background: var(--accent-2); border-color: var(--accent-2); }
|
||||
.config-body { max-height: 520px; overflow-y: auto; }
|
||||
.config-body { max-height: min(58vh, 560px); overflow-y: auto; padding-right: 2px; }
|
||||
|
||||
.field { margin-bottom: 9px; }
|
||||
.field label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 3px; }
|
||||
.field .hint { font-size: 11px; color: #5d6672; margin-top: 2px; }
|
||||
.field .hint { font-size: 11px; color: var(--muted); margin-top: 3px; line-height: 1.45; }
|
||||
.input, select.input {
|
||||
width: 100%; padding: 5px 8px;
|
||||
background: #0b0f14; border: 1px solid var(--border);
|
||||
border-radius: 6px; color: var(--text); font-size: 13px;
|
||||
width: 100%; padding: 6px 9px;
|
||||
background: #080b11; border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm); color: var(--text); font-size: 13px;
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
transition: border-color .15s, box-shadow .15s;
|
||||
}
|
||||
.input:focus { border-color: var(--accent-2); }
|
||||
.field-inline { display: flex; align-items: center; gap: 8px; }
|
||||
.field-inline input[type=checkbox] { width: 15px; height: 15px; }
|
||||
.input:focus { border-color: var(--accent-2); box-shadow: 0 0 0 3px rgba(59, 142, 247, .15); }
|
||||
.field-inline { display: flex; align-items: center; gap: 6px; }
|
||||
.field-inline input[type=checkbox] { width: 15px; height: 15px; accent-color: var(--accent-2); }
|
||||
.field-label { font-size: 12px; color: var(--muted); white-space: nowrap; }
|
||||
.input-sm { width: auto; min-width: 104px; padding: 4px 8px; font-size: 12px; }
|
||||
.input-grow { flex: 1 1 120px; min-width: 0; width: auto; }
|
||||
.group-title {
|
||||
font-size: 12px; color: var(--accent-2);
|
||||
margin: 12px 0 6px; padding-bottom: 4px;
|
||||
margin: 13px 0 7px; padding-bottom: 4px;
|
||||
border-bottom: 1px dashed var(--border);
|
||||
font-weight: 600;
|
||||
}
|
||||
.group-title:first-child { margin-top: 0; }
|
||||
|
||||
.chk { display: flex; align-items: center; gap: 5px; font-size: 12px; color: var(--text-dim); cursor: pointer; }
|
||||
.chk input[type=checkbox] { accent-color: var(--accent-2); }
|
||||
|
||||
/* ==================== 提示 ==================== */
|
||||
.toast {
|
||||
position: fixed; right: 18px; bottom: 18px;
|
||||
background: var(--panel-2); border: 1px solid var(--border);
|
||||
position: fixed; right: 16px; bottom: 16px;
|
||||
background: var(--panel-3); border: 1px solid var(--border);
|
||||
border-left: 3px solid var(--accent-2);
|
||||
padding: 10px 14px; border-radius: 8px;
|
||||
font-size: 13px; max-width: 380px;
|
||||
padding: 10px 14px; border-radius: var(--radius);
|
||||
font-size: 13px; max-width: min(380px, calc(100vw - 32px));
|
||||
opacity: 0; transform: translateY(8px);
|
||||
transition: opacity .2s, transform .2s;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
.toast.show { opacity: 1; transform: translateY(0); }
|
||||
.toast.error { border-left-color: var(--red); }
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.layout { grid-template-columns: 1fr; }
|
||||
.config-body { max-height: none; }
|
||||
/* ==================== 登录遮罩 ==================== */
|
||||
.overlay {
|
||||
position: fixed; inset: 0; z-index: 200;
|
||||
display: grid; place-items: center;
|
||||
padding: 16px;
|
||||
background: rgba(5, 7, 10, .8);
|
||||
backdrop-filter: blur(3px);
|
||||
}
|
||||
.overlay-box {
|
||||
width: min(380px, 100%);
|
||||
background: linear-gradient(180deg, var(--panel) 0%, var(--bg-soft) 100%);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 20px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
.overlay-box h2 { font-size: 16px; margin: 0 0 6px; color: var(--text); text-transform: none; letter-spacing: 0; }
|
||||
.overlay-box .input { margin: 12px 0; }
|
||||
|
||||
/* ==================== 移动端底部导航 ==================== */
|
||||
.mobile-nav { display: none; }
|
||||
|
||||
/* ==================== 响应式 ==================== */
|
||||
|
||||
/* 中等屏幕:配置栏移到下方,两栏布局 */
|
||||
@media (max-width: 1200px) {
|
||||
.layout { grid-template-columns: minmax(280px, 320px) minmax(0, 1fr); }
|
||||
.col-right { grid-column: 1 / -1; }
|
||||
.col-right { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); align-items: start; }
|
||||
.config-body { max-height: min(46vh, 460px); }
|
||||
}
|
||||
|
||||
/* 平板 / 手机:单栏 + 底部导航 */
|
||||
@media (max-width: 820px) {
|
||||
:root { --gap: 10px; }
|
||||
|
||||
.topbar {
|
||||
padding: 8px 12px;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.brand p { display: none; }
|
||||
.topbar-right { width: 100%; justify-content: space-between; }
|
||||
.topbar-right .field-inline { flex: 1 1 100%; }
|
||||
.topbar-right .field-inline select { flex: 1 1 auto; width: auto; }
|
||||
.topbar-actions { width: 100%; }
|
||||
.topbar-actions .btn { flex: 1 1 0; text-align: center; }
|
||||
.badges { width: 100%; }
|
||||
|
||||
.layout {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 10px 10px calc(64px + env(safe-area-inset-bottom, 0px));
|
||||
}
|
||||
.col-right { grid-column: auto; display: flex; }
|
||||
|
||||
/* 底部导航:把「状态 / 地图 / 日志 / 配置」四块一键定位 */
|
||||
.mobile-nav {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
left: 0; right: 0; bottom: 0;
|
||||
z-index: 40;
|
||||
background: rgba(16, 21, 29, .96);
|
||||
backdrop-filter: blur(10px);
|
||||
border-top: 1px solid var(--border-soft);
|
||||
padding-bottom: env(safe-area-inset-bottom, 0px);
|
||||
}
|
||||
.mobile-nav-item {
|
||||
flex: 1 1 0;
|
||||
padding: 11px 4px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
border-top: 2px solid transparent;
|
||||
}
|
||||
.mobile-nav-item.active { color: var(--accent-2); border-top-color: var(--accent-2); }
|
||||
|
||||
/* 触控目标放大 */
|
||||
.btn { padding: 8px 13px; font-size: 13px; }
|
||||
.btn-sm { padding: 6px 11px; font-size: 12.5px; }
|
||||
.input, select.input { padding: 8px 10px; font-size: 14px; }
|
||||
.input-sm { padding: 6px 9px; font-size: 12.5px; }
|
||||
.tab { padding: 6px 12px; font-size: 12.5px; }
|
||||
|
||||
#mapCanvas { height: clamp(260px, 52vh, 420px); }
|
||||
.log-view { height: clamp(240px, 46vh, 400px); font-size: 12.5px; }
|
||||
.config-body { max-height: none; }
|
||||
|
||||
.card { padding: 12px; }
|
||||
.kv { grid-template-columns: 84px 1fr; }
|
||||
.toast { left: 12px; right: 12px; bottom: calc(64px + env(safe-area-inset-bottom, 0px)); max-width: none; }
|
||||
}
|
||||
|
||||
@media (max-width: 420px) {
|
||||
.brand h1 { font-size: 14px; }
|
||||
.schedule-item { grid-template-columns: auto minmax(0, 1fr) auto; }
|
||||
.schedule-item .next { display: none; }
|
||||
.field-row { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
/* 横屏手机:地图压矮一点,避免看不到下面的日志 */
|
||||
@media (max-height: 520px) and (orientation: landscape) {
|
||||
#mapCanvas { height: 46vh; }
|
||||
.log-view { height: 40vh; }
|
||||
}
|
||||
|
||||
/* 尊重「减少动效」偏好 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* { transition: none !important; animation: none !important; }
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户