Skip to content

Commit 2dd725a

Browse files
committed
fix: 补齐前端同步遗漏的组件与图标
1 parent 2090b9b commit 2dd725a

9 files changed

Lines changed: 676 additions & 0 deletions

File tree

src/assets/icons/apikey-fun.png

17.3 KB
Loading

src/assets/icons/claudeapi.png

17.2 KB
Loading

src/components/ui/icons.tsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,34 @@ export function IconSatellite({ size = 20, ...props }: IconProps) {
350350
);
351351
}
352352

353+
// 用量统计卡片使用的三个图标曾在同步上游时被误删,但对应页面仍保留引用。
354+
// 继续使用内联 SVG,保证管理页面构建出的单文件 HTML 不依赖外部图标资源。
355+
export function IconDiamond({ size = 20, ...props }: IconProps) {
356+
return (
357+
<svg {...baseSvgProps} width={size} height={size} {...props}>
358+
<path d="M2.7 10.3a2.41 2.41 0 0 0 0 3.41l7.59 7.59a2.41 2.41 0 0 0 3.41 0l7.59-7.59a2.41 2.41 0 0 0 0-3.41l-7.59-7.59a2.41 2.41 0 0 0-3.41 0Z" />
359+
</svg>
360+
);
361+
}
362+
363+
export function IconTrendingUp({ size = 20, ...props }: IconProps) {
364+
return (
365+
<svg {...baseSvgProps} width={size} height={size} {...props}>
366+
<path d="M16 7h6v6" />
367+
<path d="m22 7-8.5 8.5-5-5L2 17" />
368+
</svg>
369+
);
370+
}
371+
372+
export function IconChartLine({ size = 20, ...props }: IconProps) {
373+
return (
374+
<svg {...baseSvgProps} width={size} height={size} {...props}>
375+
<path d="M3 3v16a2 2 0 0 0 2 2h16" />
376+
<path d="m19 9-5 5-4-4-3 3" />
377+
</svg>
378+
);
379+
}
380+
353381
export function IconTimer({ size = 20, ...props }: IconProps) {
354382
return (
355383
<svg {...baseSvgProps} width={size} height={size} {...props}>
@@ -471,6 +499,26 @@ export function IconSidebarQuota({ size = 20, ...props }: IconProps) {
471499
);
472500
}
473501

502+
// 用量页面的侧栏入口使用独立柱状图图标,避免与配额仪表盘图标混淆。
503+
export function IconSidebarUsage({ size = 20, ...props }: IconProps) {
504+
return (
505+
<svg {...baseSvgProps} width={size} height={size} {...props}>
506+
<path d="M3.5 20h17" />
507+
<rect x="5" y="13" width="3.5" height="7" rx="0.5" />
508+
<rect
509+
x="10.25"
510+
y="7"
511+
width="3.5"
512+
height="13"
513+
rx="0.5"
514+
fill="currentColor"
515+
fillOpacity="0.12"
516+
/>
517+
<rect x="15.5" y="10" width="3.5" height="10" rx="0.5" />
518+
</svg>
519+
);
520+
}
521+
474522
export const IconSidebarLogs = IconScrollText;
475523

476524
export function IconSidebarSystem({ size = 20, ...props }: IconProps) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { ProviderKeyConfig } from '@/types';
2+
3+
export const CLAUDE_API_DISPLAY_NAME = 'ClaudeAPI';
4+
export const CLAUDE_API_BASE_URL = 'https://gw.claudeapi.com';
5+
export const CLAUDE_API_AFFILIATE_URL =
6+
'https://console.claudeapi.com/agent/register/pJq9T52Fpugrhpgo';
7+
8+
const normalizeBaseUrl = (value: string | undefined | null): string =>
9+
String(value ?? '')
10+
.trim()
11+
.toLowerCase()
12+
.replace(/\/+$/, '');
13+
14+
export const isClaudeApiProvider = (
15+
config: ProviderKeyConfig | undefined | null
16+
): boolean => {
17+
if (!config) return false;
18+
return normalizeBaseUrl(config.baseUrl) === normalizeBaseUrl(CLAUDE_API_BASE_URL);
19+
};
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
@use '../../../styles/mixins' as *;
2+
@use '../../../styles/variables' as *;
3+
4+
.panel {
5+
background: var(--bg-primary);
6+
border: 1px solid var(--border-color);
7+
border-radius: 12px;
8+
box-shadow: var(--shadow);
9+
padding: 20px;
10+
min-width: 0;
11+
display: flex;
12+
flex-direction: column;
13+
gap: 22px;
14+
}
15+
16+
.header {
17+
display: flex;
18+
flex-direction: row;
19+
align-items: flex-start;
20+
min-width: 0;
21+
}
22+
23+
.topLink {
24+
display: inline-flex;
25+
align-items: center;
26+
gap: 6px;
27+
min-width: 0;
28+
max-width: 100%;
29+
height: 30px;
30+
padding: 0 8px;
31+
border: 1px solid transparent;
32+
border-radius: var(--radius-md);
33+
background: transparent;
34+
color: var(--muted-foreground);
35+
font-size: 13px;
36+
font-weight: 600;
37+
line-height: 1;
38+
text-decoration: none;
39+
overflow-wrap: anywhere;
40+
transition:
41+
background-color $transition-fast,
42+
color $transition-fast;
43+
44+
&:hover {
45+
background: var(--bg-hover);
46+
color: var(--text-primary);
47+
}
48+
49+
&:focus-visible {
50+
outline: 2px solid var(--primary-color);
51+
outline-offset: 2px;
52+
}
53+
54+
svg {
55+
flex-shrink: 0;
56+
}
57+
}
58+
59+
.titleRow {
60+
display: flex;
61+
align-items: center;
62+
flex-wrap: wrap;
63+
gap: 12px;
64+
min-width: 0;
65+
}
66+
67+
.logo {
68+
width: 30px;
69+
height: 30px;
70+
border-radius: var(--radius-md);
71+
object-fit: contain;
72+
flex-shrink: 0;
73+
}
74+
75+
.titleText {
76+
display: flex;
77+
min-width: 0;
78+
flex-direction: column;
79+
gap: 4px;
80+
}
81+
82+
.title {
83+
margin: 0;
84+
color: var(--text-primary);
85+
font-size: 24px;
86+
font-weight: 700;
87+
line-height: 1.22;
88+
letter-spacing: 0;
89+
}
90+
91+
.footer {
92+
display: flex;
93+
justify-content: flex-end;
94+
align-items: center;
95+
gap: 8px;
96+
padding-top: 4px;
97+
}
98+
99+
.empty {
100+
border: 1px dashed var(--border-color);
101+
border-radius: var(--radius-md);
102+
padding: 32px;
103+
text-align: center;
104+
color: var(--muted-foreground);
105+
font-size: 14px;
106+
}
107+
108+
.emptyActions {
109+
display: flex;
110+
flex-wrap: wrap;
111+
align-items: center;
112+
justify-content: center;
113+
gap: 8px;
114+
margin-top: 12px;
115+
}
116+
117+
.emptyActionButton {
118+
display: inline-flex;
119+
align-items: center;
120+
gap: 6px;
121+
padding: 7px 13px;
122+
border: 1px solid var(--border-color);
123+
border-radius: var(--radius-md);
124+
background: var(--bg-primary);
125+
color: var(--text-primary);
126+
cursor: pointer;
127+
font-size: 14px;
128+
font-weight: 500;
129+
line-height: 1.4;
130+
text-decoration: none;
131+
transition:
132+
background 0.16s ease,
133+
border-color 0.16s ease,
134+
color 0.16s ease,
135+
box-shadow 0.16s ease;
136+
137+
&:hover {
138+
border-color: var(--border-hover);
139+
background: var(--bg-hover);
140+
}
141+
142+
&:focus-visible {
143+
outline: 2px solid var(--primary-color);
144+
outline-offset: 2px;
145+
}
146+
147+
&:disabled {
148+
cursor: not-allowed;
149+
opacity: 0.6;
150+
}
151+
}
152+
153+
.emptyActionButtonPrimary {
154+
border-color: var(--primary-color);
155+
background: var(--primary-color);
156+
color: var(--primary-contrast);
157+
font-weight: 600;
158+
159+
&:hover:not(:disabled) {
160+
border-color: var(--primary-hover);
161+
background: var(--primary-hover);
162+
}
163+
164+
&:focus-visible {
165+
outline-color: var(--primary-color);
166+
}
167+
}
168+
169+
.emptyActionButtonEmphasis {
170+
color: var(--amber-text);
171+
border-color: color-mix(in srgb, var(--amber-color) 48%, var(--border-color));
172+
background: color-mix(in srgb, var(--amber-color) 14%, var(--bg-primary));
173+
font-weight: 600;
174+
box-shadow: 0 1px 0 color-mix(in srgb, var(--amber-color) 18%, transparent);
175+
176+
&:hover {
177+
border-color: color-mix(in srgb, var(--amber-color) 72%, var(--border-color));
178+
background: color-mix(in srgb, var(--amber-color) 20%, var(--bg-primary));
179+
box-shadow: 0 4px 12px color-mix(in srgb, var(--amber-color) 18%, transparent);
180+
}
181+
182+
&:focus-visible {
183+
outline-color: var(--amber-color);
184+
}
185+
}
186+
187+
.primaryAction {
188+
height: 38px;
189+
font-size: 14px;
190+
font-weight: 600;
191+
line-height: 1;
192+
}
193+
194+
.spin {
195+
animation: sponsor-quick-start-spin 0.8s linear infinite;
196+
}
197+
198+
@keyframes sponsor-quick-start-spin {
199+
from {
200+
transform: rotate(0deg);
201+
}
202+
to {
203+
transform: rotate(360deg);
204+
}
205+
}
206+
207+
@media (max-width: 768px) {
208+
.panel {
209+
padding: 16px;
210+
gap: 18px;
211+
}
212+
213+
.title {
214+
font-size: 22px;
215+
line-height: 1.25;
216+
}
217+
218+
.titleRow {
219+
gap: 10px;
220+
}
221+
222+
.footer {
223+
justify-content: stretch;
224+
225+
button {
226+
width: 100%;
227+
justify-content: center;
228+
}
229+
}
230+
}

0 commit comments

Comments
 (0)