面板调整 #32

Merged
Ghost merged 1 commits from why_ga into main 2026-09-07 10:44:47 +08:00
+195 -39
View File
@@ -245,42 +245,203 @@ body { background: #0a0e17; overflow: hidden; font-family: 'Microsoft YaHei', sa
.toolbar-toggle{ background:rgba(139,92,246,.2); border-color:rgba(139,92,246,.5); color:#c4b5fd; }
.toolbar-toggle:hover{ background:linear-gradient(135deg, rgba(139,92,246,.4), rgba(74,175,255,.3)); border-color:rgba(139,92,246,.7); color:#fff; }
/* ===== 任务侧栏(左侧固定面板,独立于通用 modal) ===== */
/* ===== 任务侧栏:赛博蓝粉霓虹风格(蓝为主 + 浅粉点缀) ===== */
@keyframes tsp-blink{ 0%,49%{opacity:1} 50%,100%{opacity:0} }
@keyframes tsp-flicker{ 0%,100%{opacity:1} 92%{opacity:1} 94%{opacity:.55} 96%{opacity:1} }
.task-side-panel{
position: fixed; left: max(14px, env(safe-area-inset-left)); top: calc(58px + env(safe-area-inset-top) + 12px + 100px);
width: 320px;
width: 340px;
/* 高度限制:可用高度(减 topBar58 + bottomBar72 + padding24 + 安全区)再减半,避免盖住聊天框 */
max-height: calc((100vh - 58px - 72px - 24px - env(safe-area-inset-top) - env(safe-area-inset-bottom)) / 2);
z-index: 250; /* 高于 #microView(200),进入微观世界也能看到;低于 #dataPanel(260) 与 #appModal(400) */
display:flex; flex-direction: column;
background: linear-gradient(180deg, rgba(17,23,42,.96), rgba(10,14,26,.96));
border: 1px solid rgba(139,92,246,.35); border-radius: 14px;
box-shadow: 0 12px 36px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.06);
backdrop-filter: blur(8px);
font-size: 13px; color: #cfd5ea;
color: #ffd4e8;
font-family: 'Consolas','Courier New','Microsoft YaHei',monospace;
/* 暗底 + 蓝粉霓虹网格(蓝为主、浅粉点缀) */
background:
linear-gradient(rgba(43,157,255,.05) 1px, transparent 1px) 0 0/100% 24px,
linear-gradient(90deg, rgba(255,158,209,.05) 1px, transparent 1px) 0 0/24px 100%,
radial-gradient(ellipse at 50% 0, rgba(43,157,255,.10), transparent 65%),
linear-gradient(180deg, #070a12 0%, #030408 100%);
border: 1px solid rgba(43,157,255,.45);
border-radius: 10px;
box-shadow:
0 0 0 1px rgba(0,0,0,.6),
0 0 22px rgba(43,157,255,.18),
0 0 14px rgba(255,158,209,.12),
0 18px 50px rgba(0,0,0,.7),
inset 0 0 30px rgba(0,0,0,.6);
overflow: hidden;
}
/* 扫描线叠加 */
.task-side-panel::before{
content:'';
position:absolute; inset:0; pointer-events:none; z-index:6;
background: repeating-linear-gradient(0deg, rgba(0,0,0,.22) 0 1px, transparent 1px 3px);
opacity:.55; mix-blend-mode: overlay;
}
.task-side-panel[hidden]{ display:none !important; }
.tsp-head{
position:relative; z-index:2;
display: flex; align-items: center; justify-content: space-between;
padding: 12px 14px; border-bottom: 1px solid rgba(139,92,246,.22); flex: 0 0 auto;
padding: 12px 16px;
background: linear-gradient(90deg, rgba(43,157,255,.14), rgba(255,158,209,.05) 60%, transparent);
border-bottom: 1px solid rgba(43,157,255,.35);
flex: 0 0 auto;
}
.tsp-title{ font-size: 15px; font-weight: 700; color: #fff; margin: 0; letter-spacing: .5px; }
.tsp-title{
font-family: 'Consolas','Courier New',monospace;
font-size: 16px; font-weight: 700; letter-spacing: 1px;
color: #5bc4ff;
text-shadow: 0 0 8px rgba(43,157,255,.6);
margin: 0;
animation: tsp-flicker 4s infinite;
}
.tsp-title::before{ content:'> '; color: #2b9dff; }
.tsp-title::after{ content:'_'; margin-left:2px; color:#2b9dff; animation: tsp-blink 1s steps(1) infinite; }
.tsp-close{
width: 26px; height: 26px; border-radius: 7px; border: 1px solid rgba(255,255,255,.14);
background: rgba(255,80,80,.12); color: #ffaaaa;
font-size: 14px; font-weight: 700; cursor: pointer;
position:relative; z-index:2;
width: 28px; height: 28px; border-radius: 6px;
border: 1px solid #ff3b3b;
background: radial-gradient(circle at 30% 30%, #ff6b6b, #b81d1d 60%, #5a0c0c);
color: #fff; font-size: 15px; font-weight: 700; line-height: 1;
display: inline-flex; align-items: center; justify-content: center;
transition: background .15s, transform .12s;
cursor: pointer; text-shadow: 0 1px 2px #000;
box-shadow: 0 0 10px rgba(255,60,60,.35), inset 0 1px 0 rgba(255,255,255,.25);
transition: transform .12s, filter .15s, box-shadow .15s;
}
.tsp-close:hover{ background: rgba(255,80,80,.28); color: #fff; transform: scale(1.06); }
.tsp-close:active{ transform: scale(.94); }
.tsp-body{ flex: 1 1 auto; overflow-y: auto; padding: 12px 14px 14px; scrollbar-width: thin; scrollbar-color: rgba(139,92,246,.35) rgba(255,255,255,.04); }
.tsp-body::-webkit-scrollbar{ width: 6px; }
.tsp-body::-webkit-scrollbar-track{ background: rgba(255,255,255,.04); border-radius: 4px; }
.tsp-body::-webkit-scrollbar-thumb{ background: rgba(139,92,246,.35); border-radius: 4px; }
.tsp-body::-webkit-scrollbar-thumb:hover{ background: rgba(139,92,246,.55); }
.tsp-close:hover{ transform: scale(1.08); filter: brightness(1.15); box-shadow: 0 0 14px rgba(255,60,60,.6); }
.tsp-close:active{ transform: scale(.95); }
.tsp-body{
position:relative; z-index:2;
flex: 1 1 auto; overflow-y: auto;
padding: 10px 14px 14px;
scrollbar-width: thin;
scrollbar-color: #2b9dff #070a12;
}
.tsp-body::-webkit-scrollbar{ width: 8px; }
.tsp-body::-webkit-scrollbar-track{ background: #070a12; border-radius: 4px; border: 1px solid rgba(43,157,255,.2); }
.tsp-body::-webkit-scrollbar-thumb{ background: linear-gradient(180deg,#2b9dff,#ff9ed1); border-radius: 4px; border: 1px solid rgba(43,157,255,.3); }
.tsp-body::-webkit-scrollbar-thumb:hover{ background: linear-gradient(180deg,#6db8ff,#ffb0d8); }
.chapter-title{
margin: 14px 0 8px; padding: 5px 0;
text-align: left;
font-size: 13px; font-weight: 700; letter-spacing: 1px; color: #5bc4ff;
text-shadow: 0 0 8px rgba(43,157,255,.45);
border-left: 3px solid #2b9dff;
background: linear-gradient(90deg, rgba(43,157,255,.10), transparent);
padding-left: 10px;
}
.chapter-title::before{ content: '// '; color: #ff9ed1; opacity:.8; }
.task-card{
position: relative;
margin: 10px 0; padding: 12px 14px;
color: #ffd4e8;
background: linear-gradient(160deg, rgba(10,16,24,.92), rgba(8,6,18,.92));
border: 1px solid rgba(43,157,255,.30);
border-left: 3px solid #2b9dff;
border-radius: 8px;
box-shadow:
0 4px 14px rgba(0,0,0,.5),
inset 0 0 18px rgba(43,157,255,.05);
overflow: hidden;
transition: transform .18s, box-shadow .18s, border-color .18s;
}
.task-card:hover{
transform: translateY(-2px);
border-color: rgba(43,157,255,.6);
box-shadow: 0 8px 22px rgba(0,0,0,.55), inset 0 0 22px rgba(43,157,255,.10), 0 0 14px rgba(43,157,255,.18), 0 0 10px rgba(255,158,209,.10);
}
.task-card.locked{ opacity:.5; filter: grayscale(.6); }
.task-card.done{ border-left-color:#5bc4ff; }
.task-card.done::after{
content:'✓';
position:absolute; right:8px; top:6px;
font-size: 42px; font-weight: 900; color: #2b9dff;
opacity:.14; pointer-events:none;
}
.task-card-header{
display: flex; justify-content: space-between; align-items: flex-start; gap: 8px;
}
.task-title{
font-family: 'Consolas','Courier New',monospace;
font-size: 14px; font-weight: 700; color: #ffffff; letter-spacing: .3px;
text-shadow: 0 0 6px rgba(43,157,255,.3);
}
.task-title::before{ content:''; }
.task-state{
flex-shrink: 0;
font-size: 11px; font-weight: 700;
padding: 2px 8px; border-radius: 4px;
border: 1px solid currentColor;
box-shadow: 0 0 8px currentColor;
}
.task-state.state-done{ color:#5bc4ff; background:rgba(43,157,255,.12); }
.task-state.state-claimable{ color:#ff9ed1; background:rgba(255,158,209,.12); }
.task-state.state-in_progress{ color:#9aa7ff; background:rgba(85,102,255,.12); }
.task-state.state-locked{ color:#6b6b6b; background:rgba(255,255,255,.04); box-shadow:none; }
.task-desc{
margin: 6px 0 8px;
font-size: 12px; line-height: 1.55; color: #a8c5ff;
}
.task-objective{ margin: 6px 0; }
.task-obj-meta{
display: flex; justify-content: space-between; align-items: center;
font-size: 11px; color: #b8d4ff; margin-bottom: 3px;
}
.task-obj-meta .hint{ color: #6a7fb8; }
.task-progress{
height: 8px;
background: #070a12;
border-radius: 4px;
overflow: hidden;
border: 1px solid rgba(43,157,255,.25);
box-shadow: inset 0 1px 3px rgba(0,0,0,.6);
}
.task-progress-fill{
height: 100%;
background: linear-gradient(90deg, #2b9dff, #ff9ed1);
border-radius: 4px 0 0 4px;
box-shadow: 0 0 8px rgba(43,157,255,.55);
transition: width .35s ease;
}
.task-reward{
margin: 8px 0 4px;
font-size: 12px; font-weight: 700; color: #ffcf4d;
text-shadow: 0 0 6px rgba(255,207,77,.35);
}
.task-claim-btn{
margin-top: 8px;
padding: 6px 16px;
border: 1px solid #2b9dff;
border-radius: 6px;
font-family: 'Consolas','Courier New',monospace;
font-size: 12px; font-weight: 800; letter-spacing: 1px;
color: #031024;
cursor: pointer;
background: linear-gradient(180deg, rgba(43,157,255,.95), rgba(255,158,209,.95));
box-shadow: 0 0 12px rgba(43,157,255,.4), inset 0 1px 0 rgba(255,255,255,.3);
text-shadow: none;
transition: transform .12s, filter .12s, box-shadow .12s;
}
.task-claim-btn:hover:not(:disabled){ transform: translateY(-1px); filter: brightness(1.12); box-shadow: 0 0 18px rgba(43,157,255,.6), 0 0 12px rgba(255,158,209,.4); }
.task-claim-btn:active:not(:disabled){ transform: translateY(1px); }
.task-claim-btn:disabled{
background: #1a1a1a; color: #555; border-color:#333; cursor:not-allowed; box-shadow:none;
}
@media (max-width: 768px){
.task-side-panel{ width: min(300px, calc(100vw - 28px)); }
.task-side-panel{ width: min(320px, calc(100vw - 28px)); }
}
@media (max-width: 420px){
.task-side-panel{ left: 8px; right: 8px; width: auto; max-height: 50vh; }
@@ -2833,7 +2994,7 @@ function renderTaskPanel() {
taskData.chapters.forEach(ch => {
const list = taskData.tasks.filter(t => t.chapter === ch);
if (!list.length) return;
html += `<div style="margin:14px 0 6px;font-weight:600;color:#cdd9e5;">第 ${ch} 章</div>`;
html += `<div class="chapter-title">第 ${ch} 章</div>`;
list.forEach(t => {
const locked = t.state === 'locked';
const done = t.state === 'done';
@@ -2848,32 +3009,27 @@ function renderTaskPanel() {
const have = Math.min(o.have || 0, need);
const pct = need ? Math.round(have / need * 100) : 100;
objHtml += `
<div style="margin:6px 0;">
<div style="display:flex;justify-content:space-between;font-size:12px;color:#9fb0c0;">
<span>${o.label || o.type}${o.hint ? ` <span style="color:#66788a;">(${o.hint})</span>` : ''}</span>
<div class="task-objective">
<div class="task-obj-meta">
<span>${o.label || o.type}${o.hint ? ` <span class="hint">(${o.hint})</span>` : ''}</span>
<span>${have}/${need}</span>
</div>
<div style="height:6px;background:#1d2733;border-radius:4px;overflow:hidden;margin-top:3px;">
<div style="height:100%;width:${pct}%;background:linear-gradient(90deg,#3a7afe,#38d0c8);border-radius:4px;transition:width .35s;"></div>
<div class="task-progress">
<div class="task-progress-fill" style="width:${pct}%;"></div>
</div>
</div>`;
});
const btnLabel = done ? '已完成' : (claimable ? '领取奖励' : (locked ? '未解锁' : '进行中'));
const btnColor = done ? '#33414f' : (claimable ? '#3a7afe' : '#33414f');
const btnCursor = (done || locked || !claimable) ? 'not-allowed' : 'pointer';
html += `
<div style="border:1px solid #2a3645;border-radius:10px;padding:10px 12px;margin:8px 0;background:#16202c;${locked ? 'opacity:.5;' : ''}">
<div style="display:flex;justify-content:space-between;align-items:center;">
<span style="font-weight:600;color:#e8eef4;">${t.title}</span>
<span style="font-size:12px;color:#8899aa;">${stateLabel[t.state] || t.state}</span>
<div class="task-card ${locked ? 'locked' : ''} ${done ? 'done' : ''} ${claimable ? 'claimable' : ''}">
<div class="task-card-header">
<span class="task-title">${t.title}</span>
<span class="task-state state-${t.state}">${stateLabel[t.state] || t.state}</span>
</div>
<div style="font-size:12px;color:#9fb0c0;margin:4px 0;">${t.desc || ''}</div>
<div class="task-desc">${t.desc || ''}</div>
${objHtml}
${rewards.length ? `<div style="font-size:12px;color:#ffd27f;margin:2px 0;">奖励:${rewards.join(' · ')}</div>` : ''}
${done ? '' : `<button class="task-claim-btn" data-code="${t.code}"
${claimable ? '' : 'disabled'}
style="margin-top:6px;padding:5px 14px;border:none;border-radius:8px;cursor:${btnCursor};
background:${btnColor};color:#fff;font-size:13px;">${btnLabel}</button>`}
${rewards.length ? `<div class="task-reward">奖励:${rewards.join(' · ')}</div>` : ''}
${done ? '' : `<button class="task-claim-btn" data-code="${t.code}" ${claimable ? '' : 'disabled'}>${btnLabel}</button>`}
</div>`;
});
});