fix: 暗色主题全面优化 - 滚动条、颜色适配、dark:伪类修复

- 添加全局暗色滚动条样式 (globals.css)
- 修复 channels/subscriptions 页面 dark: 伪类无效的bug (改用 isDark)
- 修复 RefundDialog 暗色下退款金额、disabled 按钮颜色
- 修复 admin OrderTable 暗色下缺少背景色
- 统一所有 admin 页面 text-gray-500 为 slate 色系
- 修复 UserSubscriptions 续费按钮暗色适配
- 修复日期标签文字缺少暗色颜色类
- MainTabs 暗色容器改用 slate-900 提升对比度
- OpenAI 默认模型区域暗色边框透明度提升
- 更新 README 文档
This commit is contained in:
erio
2026-03-14 03:19:39 +08:00
parent 8a5f8662d0
commit 3b5a3ba5df
13 changed files with 186 additions and 60 deletions

View File

@@ -328,7 +328,7 @@ function ChannelsContent() {
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-center text-red-500">
<p className="text-lg font-medium">{t.missingToken}</p>
<p className="mt-2 text-sm text-gray-500">{t.missingTokenHint}</p>
<p className={`mt-2 text-sm ${isDark ? 'text-slate-400' : 'text-slate-500'}`}>{t.missingTokenHint}</p>
</div>
</div>
);
@@ -721,13 +721,13 @@ function ChannelsContent() {
</td>
<td className="px-4 py-3 text-center">
{channel.groupExists ? (
<span className="inline-flex h-5 w-5 items-center justify-center rounded-full bg-emerald-100 text-emerald-600 dark:bg-emerald-900/40 dark:text-emerald-400">
<span className={`inline-flex h-5 w-5 items-center justify-center rounded-full ${isDark ? 'bg-emerald-900/40 text-emerald-400' : 'bg-emerald-100 text-emerald-600'}`}>
<svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
</svg>
</span>
) : (
<span className="inline-flex h-5 w-5 items-center justify-center rounded-full bg-red-100 text-red-600 dark:bg-red-900/40 dark:text-red-400">
<span className={`inline-flex h-5 w-5 items-center justify-center rounded-full ${isDark ? 'bg-red-900/40 text-red-400' : 'bg-red-100 text-red-600'}`}>
<svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
@@ -997,7 +997,7 @@ function ChannelsContent() {
disabled={alreadyImported}
checked={syncSelected.has(group.id)}
onChange={() => toggleSyncGroup(group.id)}
className="mt-0.5 h-4 w-4 rounded border-slate-300 text-indigo-500 focus:ring-indigo-500"
className={`mt-0.5 h-4 w-4 rounded text-indigo-500 focus:ring-indigo-500 ${isDark ? 'border-slate-600 bg-slate-700' : 'border-slate-300'}`}
/>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
@@ -1061,7 +1061,7 @@ function ChannelsPageFallback() {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-gray-500">{locale === 'en' ? 'Loading...' : '加载中...'}</div>
<div className="text-slate-500">{locale === 'en' ? 'Loading...' : '加载中...'}</div>
</div>
);
}

View File

@@ -103,7 +103,7 @@ function DashboardContent() {
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-center text-red-500">
<p className="text-lg font-medium">{text.missingToken}</p>
<p className="mt-2 text-sm text-gray-500">{text.missingTokenHint}</p>
<p className={`mt-2 text-sm ${isDark ? 'text-slate-400' : 'text-slate-500'}`}>{text.missingTokenHint}</p>
</div>
</div>
);
@@ -185,7 +185,7 @@ function DashboardPageFallback() {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-gray-500">{locale === 'en' ? 'Loading...' : '加载中...'}</div>
<div className="text-slate-500">{locale === 'en' ? 'Loading...' : '加载中...'}</div>
</div>
);
}

View File

@@ -162,7 +162,7 @@ function AdminContent() {
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-center text-red-500">
<p className="text-lg font-medium">{text.missingToken}</p>
<p className="mt-2 text-sm text-gray-500">{text.missingTokenHint}</p>
<p className={`mt-2 text-sm ${isDark ? 'text-slate-400' : 'text-slate-500'}`}>{text.missingTokenHint}</p>
</div>
</div>
);
@@ -335,7 +335,7 @@ function AdminPageFallback() {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-gray-500">{locale === 'en' ? 'Loading...' : '加载中...'}</div>
<div className="text-slate-500">{locale === 'en' ? 'Loading...' : '加载中...'}</div>
</div>
);
}

View File

@@ -103,7 +103,7 @@ function DashboardContent() {
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-center text-red-500">
<p className="text-lg font-medium">{text.missingToken}</p>
<p className="mt-2 text-sm text-gray-500">{text.missingTokenHint}</p>
<p className={`mt-2 text-sm ${isDark ? 'text-slate-400' : 'text-slate-500'}`}>{text.missingTokenHint}</p>
</div>
</div>
);
@@ -185,7 +185,7 @@ function DashboardPageFallback() {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-gray-500">{locale === 'en' ? 'Loading...' : '加载中...'}</div>
<div className="text-slate-500">{locale === 'en' ? 'Loading...' : '加载中...'}</div>
</div>
);
}

View File

@@ -596,7 +596,7 @@ function SubscriptionsContent() {
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-center text-red-500">
<p className="text-lg font-medium">{t.missingToken}</p>
<p className="mt-2 text-sm text-gray-500">{t.missingTokenHint}</p>
<p className={`mt-2 text-sm ${isDark ? 'text-slate-400' : 'text-slate-500'}`}>{t.missingTokenHint}</p>
</div>
</div>
);
@@ -920,7 +920,7 @@ function SubscriptionsContent() {
<>
<div>
<span className={isDark ? 'text-slate-500' : 'text-slate-400'}>/v1/messages </span>
<div className={['mt-0.5 text-xs font-medium', plan.groupAllowMessagesDispatch ? 'text-green-600 dark:text-green-400' : isDark ? 'text-slate-400' : 'text-slate-500'].join(' ')}>
<div className={['mt-0.5 text-xs font-medium', plan.groupAllowMessagesDispatch ? (isDark ? 'text-green-400' : 'text-green-600') : isDark ? 'text-slate-400' : 'text-slate-500'].join(' ')}>
{plan.groupAllowMessagesDispatch ? '已启用' : '未启用'}
</div>
</div>
@@ -1241,7 +1241,7 @@ function SubscriptionsContent() {
{selectedGroup.platform?.toLowerCase() === 'openai' && (
<div>
<span className={isDark ? 'text-slate-500' : 'text-slate-400'}>/v1/messages </span>
<div className={['mt-0.5 font-medium', selectedGroup.allow_messages_dispatch ? 'text-green-600 dark:text-green-400' : isDark ? 'text-slate-400' : 'text-slate-500'].join(' ')}>
<div className={['mt-0.5 font-medium', selectedGroup.allow_messages_dispatch ? (isDark ? 'text-green-400' : 'text-green-600') : isDark ? 'text-slate-400' : 'text-slate-500'].join(' ')}>
{selectedGroup.allow_messages_dispatch ? '已启用' : '未启用'}
</div>
</div>
@@ -1431,7 +1431,7 @@ function SubscriptionsPageFallback() {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-gray-500">{locale === 'en' ? 'Loading...' : '加载中...'}</div>
<div className="text-slate-500">{locale === 'en' ? 'Loading...' : '加载中...'}</div>
</div>
);
}

View File

@@ -16,3 +16,31 @@ body {
'Microsoft YaHei',
sans-serif;
}
/* Scrollbar - Dark theme */
* {
scrollbar-width: thin;
scrollbar-color: #475569 #1e293b;
}
*::-webkit-scrollbar {
width: 8px;
height: 8px;
}
*::-webkit-scrollbar-track {
background: #1e293b;
}
*::-webkit-scrollbar-thumb {
background: #475569;
border-radius: 4px;
}
*::-webkit-scrollbar-thumb:hover {
background: #64748b;
}
*::-webkit-scrollbar-corner {
background: #1e293b;
}

View File

@@ -29,7 +29,7 @@ export default function MainTabs({ activeTab, onTabChange, showSubscribeTab, sho
<div
className={[
'inline-flex rounded-xl p-1',
isDark ? 'bg-slate-800' : 'bg-slate-100',
isDark ? 'bg-slate-900' : 'bg-slate-100',
].join(' ')}
>
{tabs.map((tab) => {

View File

@@ -168,7 +168,7 @@ export default function SubscriptionConfirm({
{isOpenAI && plan.defaultMappedModel && (
<div className={[
'flex items-center justify-between rounded-lg border px-3 py-2 text-sm',
isDark ? 'border-green-500/20 bg-green-500/5' : 'border-green-500/20 bg-green-50/50',
isDark ? 'border-green-500/30 bg-green-500/10' : 'border-green-200 bg-green-50/50',
].join(' ')}>
<span className={isDark ? 'text-slate-400' : 'text-slate-500'}>
{pickLocaleText(locale, '默认模型', 'Default Model')}

View File

@@ -155,7 +155,7 @@ export default function SubscriptionPlanCard({ plan, onSubscribe, isDark, locale
{isOpenAI && plan.defaultMappedModel && (
<div className={[
'mb-4 flex items-center justify-between rounded-lg border px-3 py-2 text-sm',
isDark ? 'border-green-500/20 bg-green-500/5' : 'border-green-500/20 bg-green-50/50',
isDark ? 'border-green-500/30 bg-green-500/10' : 'border-green-200 bg-green-50/50',
].join(' ')}>
<span className={isDark ? 'text-slate-400' : 'text-slate-500'}>
{pickLocaleText(locale, '默认模型', 'Default Model')}

View File

@@ -97,7 +97,12 @@ export default function UserSubscriptions({ subscriptions, onRenew, isDark, loca
<button
type="button"
onClick={() => onRenew(sub.group_id)}
className="rounded-lg bg-emerald-500 px-3 py-1.5 text-xs font-semibold text-white transition-colors hover:bg-emerald-600 active:bg-emerald-700"
className={[
'rounded-lg px-3 py-1.5 text-xs font-semibold text-white transition-colors',
isDark
? 'bg-emerald-500/80 hover:bg-emerald-500 active:bg-emerald-600'
: 'bg-emerald-500 hover:bg-emerald-600 active:bg-emerald-700',
].join(' ')}
>
{pickLocaleText(locale, '续费', 'Renew')}
</button>
@@ -107,13 +112,13 @@ export default function UserSubscriptions({ subscriptions, onRenew, isDark, loca
{/* Dates */}
<div className={['mb-3 grid grid-cols-2 gap-3 text-sm', isDark ? 'text-slate-400' : 'text-slate-500'].join(' ')}>
<div>
<span className="text-xs uppercase tracking-wide">{pickLocaleText(locale, '开始', 'Start')}</span>
<span className={['text-xs uppercase tracking-wide', isDark ? 'text-slate-500' : 'text-slate-400'].join(' ')}>{pickLocaleText(locale, '开始', 'Start')}</span>
<p className={['font-medium', isDark ? 'text-slate-300' : 'text-slate-700'].join(' ')}>
{formatDate(sub.starts_at)}
</p>
</div>
<div>
<span className="text-xs uppercase tracking-wide">{pickLocaleText(locale, '到期', 'Expires')}</span>
<span className={['text-xs uppercase tracking-wide', isDark ? 'text-slate-500' : 'text-slate-400'].join(' ')}>{pickLocaleText(locale, '到期', 'Expires')}</span>
<p className={['font-medium', isDark ? 'text-slate-300' : 'text-slate-700'].join(' ')}>
{formatDate(sub.expires_at)}
</p>

View File

@@ -85,7 +85,7 @@ export default function OrderTable({ orders, onRetry, onCancel, onViewDetail, da
<th className={thCls}>{text.actions}</th>
</tr>
</thead>
<tbody className={`divide-y ${dark ? 'divide-slate-700/60' : 'divide-gray-200 bg-white'}`}>
<tbody className={`divide-y ${dark ? 'divide-slate-700/60 bg-slate-900' : 'divide-gray-200 bg-white'}`}>
{orders.map((order) => {
const statusInfo = {
label: formatStatus(order.status, locale),
@@ -128,12 +128,12 @@ export default function OrderTable({ orders, onRetry, onCancel, onViewDetail, da
{order.id.slice(0, 12)}...
</button>
</td>
<td className={`whitespace-nowrap px-4 py-3 text-sm ${dark ? 'text-slate-200' : ''}`}>
<td className={`whitespace-nowrap px-4 py-3 text-sm ${dark ? 'text-slate-200' : 'text-slate-900'}`}>
{order.userName || `#${order.userId}`}
</td>
<td className={tdMuted}>{order.userEmail || '-'}</td>
<td className={tdMuted}>{order.userNotes || '-'}</td>
<td className={`whitespace-nowrap px-4 py-3 text-sm font-medium ${dark ? 'text-slate-200' : ''}`}>
<td className={`whitespace-nowrap px-4 py-3 text-sm font-medium ${dark ? 'text-slate-200' : 'text-slate-900'}`}>
{currency}
{order.amount.toFixed(2)}
</td>

View File

@@ -81,12 +81,12 @@ export default function RefundDialog({
<div className="mt-4 space-y-3">
<div className={['rounded-lg p-3', dark ? 'bg-slate-800' : 'bg-gray-50'].join(' ')}>
<div className={['text-sm', dark ? 'text-slate-400' : 'text-gray-500'].join(' ')}>{text.orderId}</div>
<div className="text-sm font-mono">{orderId}</div>
<div className={['text-sm font-mono', dark ? 'text-slate-200' : 'text-gray-900'].join(' ')}>{orderId}</div>
</div>
<div className={['rounded-lg p-3', dark ? 'bg-slate-800' : 'bg-gray-50'].join(' ')}>
<div className={['text-sm', dark ? 'text-slate-400' : 'text-gray-500'].join(' ')}>{text.amount}</div>
<div className="text-lg font-bold text-red-600">
<div className={['text-lg font-bold', dark ? 'text-red-400' : 'text-red-600'].join(' ')}>
{currency}
{amount.toFixed(2)}
</div>
@@ -127,7 +127,7 @@ export default function RefundDialog({
onChange={(e) => setForce(e.target.checked)}
className={['rounded', dark ? 'border-slate-600' : 'border-gray-300'].join(' ')}
/>
<span className="text-red-600">{text.forceRefund}</span>
<span className={dark ? 'text-red-400' : 'text-red-600'}>{text.forceRefund}</span>
</label>
)}
</div>
@@ -147,7 +147,10 @@ export default function RefundDialog({
<button
onClick={handleConfirm}
disabled={loading || (requireForce && !force)}
className="flex-1 rounded-lg bg-red-600 py-2 text-sm font-medium text-white hover:bg-red-700 disabled:cursor-not-allowed disabled:bg-gray-300"
className={[
'flex-1 rounded-lg py-2 text-sm font-medium text-white hover:bg-red-700 disabled:cursor-not-allowed',
dark ? 'bg-red-600/90 disabled:bg-slate-700 disabled:text-slate-500' : 'bg-red-600 disabled:bg-gray-300 disabled:text-gray-400',
].join(' ')}
>
{loading ? text.processing : text.confirm}
</button>