Skip to content

Commit ac95867

Browse files
committed
feat: 优化 courses 和 dashboard 页面
- 应用响应式设计系统 - 添加主题切换支持 - 统一组件风格 - 使用 CSS 变量 - 优化卡片、表格、按钮样式
1 parent c976085 commit ac95867

2 files changed

Lines changed: 84 additions & 82 deletions

File tree

platform/src/app/[locale]/courses/page.tsx

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ function CategoryFilter({
3838
const categories = ['all', 'programming', 'design', 'business', 'marketing']
3939

4040
return (
41-
<div className="flex flex-wrap gap-2 justify-center">
41+
<div className="flex flex-wrap gap-[clamp(0.5rem,1vw,1rem)] justify-center">
4242
{categories.map((category) => (
4343
<button
4444
key={category}
4545
onClick={() => onSelect(category)}
46-
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
46+
className={`px-6 py-3 rounded-lg text-responsive-small font-medium transition-all duration-200 ${
4747
selected === category
48-
? 'bg-accent text-white'
49-
: 'bg-dark-card text-gray-300 hover:bg-white/10'
48+
? 'bg-primary-container text-on-surface scale-105'
49+
: 'bg-surface-container text-on-surface-variant hover:bg-surface-container-high hover:scale-105'
5050
}`}
5151
>
5252
{labels[category]}
@@ -70,34 +70,34 @@ function CourseCard({
7070
t: (key: string) => string
7171
}) {
7272
return (
73-
<div className="bg-dark-card rounded-2xl p-6 border border-white/5 hover:border-accent/30 transition-all flex flex-col">
73+
<div className="bg-surface-container rounded-2xl p-responsive-card border border-outline-variant hover:border-primary/40 transition-all duration-200 flex flex-col hover:shadow-m3-card">
7474
<div className="flex items-start justify-between mb-3">
75-
<span className="px-3 py-1 rounded-full text-xs font-medium bg-accent/20 text-accent">
75+
<span className="px-3 py-1 rounded-full text-xs font-medium bg-primary/15 text-primary">
7676
{labels[course.category] || course.category}
7777
</span>
7878
{course.published ? (
79-
<span className="px-2 py-1 rounded text-xs bg-green-500/20 text-green-400">{t('published')}</span>
79+
<span className="px-2 py-1 rounded text-xs bg-success/20 text-success">{t('published')}</span>
8080
) : (
81-
<span className="px-2 py-1 rounded text-xs bg-yellow-500/20 text-yellow-400">{t('draft')}</span>
81+
<span className="px-2 py-1 rounded text-xs bg-warning/20 text-warning">{t('draft')}</span>
8282
)}
8383
</div>
84-
<h3 className="text-lg font-semibold text-white mb-2">{course.title}</h3>
85-
<p className="text-gray-400 text-sm mb-4 line-clamp-2 flex-grow">{course.description}</p>
84+
<h3 className="text-responsive-card font-semibold text-on-surface mb-2">{course.title}</h3>
85+
<p className="text-on-surface-variant text-responsive-small mb-4 line-clamp-2 flex-grow">{course.description}</p>
8686
<div className="flex items-center justify-between">
87-
<span className="text-gray-500 text-sm">{course.instructor}</span>
88-
<span className="text-accent font-bold text-lg">¥{course.price}</span>
87+
<span className="text-on-surface-variant text-responsive-small">{course.instructor}</span>
88+
<span className="text-primary font-bold text-lg">¥{course.price}</span>
8989
</div>
9090
{isLoggedIn ? (
9191
<button
9292
onClick={() => onPurchase(course)}
93-
className="mt-4 w-full py-3 rounded-xl bg-accent hover:bg-accent/80 text-white font-medium transition-colors cursor-pointer"
93+
className="mt-4 w-full py-3 rounded-xl bg-primary-container hover:bg-primary-container-light text-on-surface font-medium transition-all duration-200 cursor-pointer hover:scale-[1.02]"
9494
>
9595
{t('buyNow')}
9696
</button>
9797
) : (
9898
<a
9999
href="/auth"
100-
className="mt-4 w-full py-3 rounded-xl bg-gray-700 hover:bg-gray-600 text-gray-300 font-medium transition-colors text-center block"
100+
className="mt-4 w-full py-3 rounded-xl bg-surface-container-high hover:bg-surface-container-highest text-on-surface-variant font-medium transition-all duration-200 text-center block hover:scale-[1.02]"
101101
>
102102
{t('loginToBuy')}
103103
</a>
@@ -185,16 +185,16 @@ export default function CoursesPage() {
185185
}
186186

187187
return (
188-
<main className="min-h-screen bg-dark-bg">
188+
<main className="min-h-screen bg-surface">
189189
<Navigation />
190190

191-
<section className="pt-24 pb-12 px-4 sm:px-6 lg:px-8">
191+
<section className="py-responsive-section px-[clamp(1rem,3vw,2rem)]">
192192
<div className="max-w-7xl mx-auto">
193-
<div className="text-center mb-12">
194-
<h1 className="text-3xl sm:text-4xl font-bold text-white mb-4">
193+
<div className="text-center mb-16">
194+
<h1 className="text-responsive-section font-bold text-on-surface mb-4">
195195
{t('title')}
196196
</h1>
197-
<p className="text-gray-400 text-lg">
197+
<p className="text-responsive-subtitle text-on-surface-variant">
198198
{t('subtitle')}
199199
</p>
200200
</div>
@@ -208,42 +208,44 @@ export default function CoursesPage() {
208208
</div>
209209

210210
{loading ? (
211-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" aria-label={t('loading')}>
211+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-[clamp(1rem,2vw,1.5rem)]" aria-label={t('loading')}>
212212
{Array.from({ length: 6 }).map((_, i) => (
213-
<div key={i} className="bg-dark-card rounded-2xl p-6 border border-white/5 animate-shimmer">
213+
<div key={i} className="bg-surface-container rounded-2xl p-6 border border-outline-variant animate-shimmer">
214214
<div className="flex items-start justify-between mb-3">
215-
<div className="h-6 w-20 rounded-full bg-white/10" />
216-
<div className="h-5 w-14 rounded bg-white/10" />
215+
<div className="h-6 w-20 rounded-full bg-outline-variant/20" />
216+
<div className="h-5 w-14 rounded bg-outline-variant/20" />
217217
</div>
218-
<div className="h-5 w-3/4 rounded bg-white/10 mb-2" />
219-
<div className="h-4 w-full rounded bg-white/10 mb-1" />
220-
<div className="h-4 w-5/6 rounded bg-white/10 mb-4" />
218+
<div className="h-5 w-3/4 rounded bg-outline-variant/20 mb-2" />
219+
<div className="h-4 w-full rounded bg-outline-variant/20 mb-1" />
220+
<div className="h-4 w-5/6 rounded bg-outline-variant/20 mb-4" />
221221
<div className="flex items-center justify-between">
222-
<div className="h-4 w-24 rounded bg-white/10" />
223-
<div className="h-6 w-16 rounded bg-white/10" />
222+
<div className="h-4 w-24 rounded bg-outline-variant/20" />
223+
<div className="h-6 w-16 rounded bg-outline-variant/20" />
224224
</div>
225-
<div className="mt-4 h-12 rounded-xl bg-white/10" />
225+
<div className="mt-4 h-12 rounded-xl bg-outline-variant/20" />
226226
</div>
227227
))}
228228
</div>
229229
) : error ? (
230230
<div className="text-center py-12">
231-
<p className="text-red-400">{error}</p>
231+
<p className="text-danger">{error}</p>
232232
</div>
233233
) : (
234234
<>
235235
{purchaseMsg && (
236-
<div className={`mb-6 p-4 rounded-xl text-center ${purchaseMsg.type === 'success' ? 'bg-green-500/20 text-green-300' : 'bg-red-500/20 text-red-300'}`}>
236+
<div className={`mb-6 p-4 rounded-xl text-center animate-fade-in ${
237+
purchaseMsg.type === 'success' ? 'bg-success/15 text-success' : 'bg-danger/15 text-danger'
238+
}`}>
237239
{purchaseMsg.text}
238240
<button onClick={() => setPurchaseMsg(null)} className="ml-3 underline cursor-pointer">{t('close')}</button>
239241
</div>
240242
)}
241243
{filteredCourses.length === 0 ? (
242244
<div className="text-center py-12">
243-
<p className="text-gray-400">{t('noCourses')}</p>
245+
<p className="text-on-surface-variant">{t('noCourses')}</p>
244246
</div>
245247
) : (
246-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
248+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-[clamp(1rem,2vw,1.5rem)]">
247249
{filteredCourses.map((course) => (
248250
<CourseCard
249251
key={course.id}

platform/src/app/[locale]/dashboard/page.tsx

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ interface DashboardStats {
2727

2828
function StatCard({ title, value, suffix }: { title: string; value: number | string; suffix?: string }) {
2929
return (
30-
<div className="bg-dark-card rounded-2xl p-6 border border-white/5">
31-
<p className="text-gray-400 text-sm mb-2">{title}</p>
32-
<p className="text-3xl font-bold text-white">
30+
<div className="bg-surface-container rounded-2xl p-6 border border-outline-variant hover:border-primary/30 transition-all duration-200 hover:shadow-m3-card">
31+
<p className="text-on-surface-variant text-responsive-small mb-2">{title}</p>
32+
<p className="text-3xl font-bold text-on-surface">
3333
{value}
34-
{suffix && <span className="text-lg text-gray-400 ml-1">{suffix}</span>}
34+
{suffix && <span className="text-lg text-on-surface-variant ml-1">{suffix}</span>}
3535
</p>
3636
</div>
3737
)
@@ -50,16 +50,16 @@ function formatDate(dateString: string, locale: string): string {
5050

5151
function StatusBadge({ status, label }: { status: string; label: string }) {
5252
const statusStyles: Record<string, string> = {
53-
completed: 'bg-green-500/20 text-green-400',
54-
pending: 'bg-yellow-500/20 text-yellow-400',
55-
failed: 'bg-red-500/20 text-red-400',
56-
refunded: 'bg-gray-500/20 text-gray-400',
53+
completed: 'bg-success/15 text-success',
54+
pending: 'bg-warning/15 text-warning',
55+
failed: 'bg-danger/15 text-danger',
56+
refunded: 'bg-on-surface-variant/15 text-on-surface-variant',
5757
}
5858

59-
const style = statusStyles[status] || 'bg-gray-500/20 text-gray-400'
59+
const style = statusStyles[status] || 'bg-on-surface-variant/15 text-on-surface-variant'
6060

6161
return (
62-
<span className={`px-2 py-1 rounded-full text-xs font-medium ${style}`}>
62+
<span className={`px-3 py-1 rounded-full text-xs font-medium ${style}`}>
6363
{label}
6464
</span>
6565
)
@@ -95,11 +95,11 @@ export default function DashboardPage() {
9595

9696
if (loading) {
9797
return (
98-
<main className="min-h-screen bg-dark-bg pt-20">
98+
<main className="min-h-screen bg-surface pt-20">
9999
<Navigation />
100-
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
100+
<div className="max-w-7xl mx-auto px-[clamp(1rem,3vw,2rem)] py-8">
101101
<div className="flex items-center justify-center h-64">
102-
<div className="text-gray-400">{t('loading')}</div>
102+
<div className="text-on-surface-variant">{t('loading')}</div>
103103
</div>
104104
</div>
105105
</main>
@@ -108,56 +108,56 @@ export default function DashboardPage() {
108108

109109
if (error || !stats) {
110110
return (
111-
<main className="min-h-screen bg-dark-bg pt-20">
111+
<main className="min-h-screen bg-surface pt-20">
112112
<Navigation />
113-
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
113+
<div className="max-w-7xl mx-auto px-[clamp(1rem,3vw,2rem)] py-8">
114114
<div className="flex items-center justify-center h-64">
115-
<div className="text-red-400">{error || t('error')}</div>
115+
<div className="text-danger">{error || t('error')}</div>
116116
</div>
117117
</div>
118118
</main>
119119
)
120120
}
121121

122122
return (
123-
<main className="min-h-screen bg-dark-bg pt-20">
123+
<main className="min-h-screen bg-surface pt-20">
124124
<Navigation />
125-
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
126-
<h1 className="text-3xl font-bold text-white mb-8">{t('title')}</h1>
125+
<div className="max-w-7xl mx-auto px-[clamp(1rem,3vw,2rem)] py-8">
126+
<h1 className="text-responsive-section font-bold text-on-surface mb-8">{t('title')}</h1>
127127

128128
{/* Stats Grid */}
129-
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
129+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-[clamp(1rem,2vw,1.5rem)] mb-8">
130130
<StatCard title={t('totalUsers')} value={stats.totalUsers} />
131131
<StatCard title={t('totalCourses')} value={stats.totalCourses} />
132132
<StatCard title={t('totalOrders')} value={stats.totalOrders} />
133133
<StatCard title={t('totalRevenue')} value={stats.totalRevenue} suffix={t('currency')} />
134134
</div>
135135

136136
{/* Recent Users */}
137-
<div className="bg-dark-card rounded-2xl border border-white/5 mb-8 overflow-hidden">
138-
<div className="px-6 py-4 border-b border-white/5">
139-
<h2 className="text-xl font-semibold text-white">{t('recentUsers')}</h2>
137+
<div className="bg-surface-container rounded-2xl border border-outline-variant mb-8 overflow-hidden">
138+
<div className="px-6 py-4 border-b border-outline-variant">
139+
<h2 className="text-xl font-semibold text-on-surface">{t('recentUsers')}</h2>
140140
</div>
141141
<div className="overflow-x-auto">
142142
<table className="w-full">
143-
<thead className="bg-dark-bg/50">
143+
<thead className="bg-surface-container-high">
144144
<tr>
145-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">{t('tableHeaders.name')}</th>
146-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">{t('tableHeaders.email')}</th>
147-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">{t('tableHeaders.joinedAt')}</th>
145+
<th className="px-6 py-3 text-left text-xs font-medium text-on-surface-variant uppercase tracking-wider">{t('tableHeaders.name')}</th>
146+
<th className="px-6 py-3 text-left text-xs font-medium text-on-surface-variant uppercase tracking-wider">{t('tableHeaders.email')}</th>
147+
<th className="px-6 py-3 text-left text-xs font-medium text-on-surface-variant uppercase tracking-wider">{t('tableHeaders.joinedAt')}</th>
148148
</tr>
149149
</thead>
150-
<tbody className="divide-y divide-white/5">
150+
<tbody className="divide-y divide-outline-variant">
151151
{stats.recentUsers.map((user) => (
152-
<tr key={user.email} className="hover:bg-white/5 transition-colors">
153-
<td className="px-6 py-4 whitespace-nowrap text-sm text-white">{user.name}</td>
154-
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-300">{user.email}</td>
155-
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-400">{formatDate(user.createdAt, locale)}</td>
152+
<tr key={user.email} className="hover:bg-surface-container-high transition-colors">
153+
<td className="px-6 py-4 whitespace-nowrap text-sm text-on-surface">{user.name}</td>
154+
<td className="px-6 py-4 whitespace-nowrap text-sm text-on-surface-variant">{user.email}</td>
155+
<td className="px-6 py-4 whitespace-nowrap text-sm text-on-surface-variant">{formatDate(user.createdAt, locale)}</td>
156156
</tr>
157157
))}
158158
{stats.recentUsers.length === 0 && (
159159
<tr>
160-
<td colSpan={3} className="px-6 py-8 text-center text-gray-400">{t('noUsers')}</td>
160+
<td colSpan={3} className="px-6 py-8 text-center text-on-surface-variant">{t('noUsers')}</td>
161161
</tr>
162162
)}
163163
</tbody>
@@ -166,36 +166,36 @@ export default function DashboardPage() {
166166
</div>
167167

168168
{/* Recent Orders */}
169-
<div className="bg-dark-card rounded-2xl border border-white/5 overflow-hidden">
170-
<div className="px-6 py-4 border-b border-white/5">
171-
<h2 className="text-xl font-semibold text-white">{t('recentOrders')}</h2>
169+
<div className="bg-surface-container rounded-2xl border border-outline-variant overflow-hidden">
170+
<div className="px-6 py-4 border-b border-outline-variant">
171+
<h2 className="text-xl font-semibold text-on-surface">{t('recentOrders')}</h2>
172172
</div>
173173
<div className="overflow-x-auto">
174174
<table className="w-full">
175-
<thead className="bg-dark-bg/50">
175+
<thead className="bg-surface-container-high">
176176
<tr>
177-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">{t('tableHeaders.course')}</th>
178-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">{t('tableHeaders.user')}</th>
179-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">{t('tableHeaders.amount')}</th>
180-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">{t('tableHeaders.status')}</th>
181-
<th className="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">{t('tableHeaders.createdAt')}</th>
177+
<th className="px-6 py-3 text-left text-xs font-medium text-on-surface-variant uppercase tracking-wider">{t('tableHeaders.course')}</th>
178+
<th className="px-6 py-3 text-left text-xs font-medium text-on-surface-variant uppercase tracking-wider">{t('tableHeaders.user')}</th>
179+
<th className="px-6 py-3 text-left text-xs font-medium text-on-surface-variant uppercase tracking-wider">{t('tableHeaders.amount')}</th>
180+
<th className="px-6 py-3 text-left text-xs font-medium text-on-surface-variant uppercase tracking-wider">{t('tableHeaders.status')}</th>
181+
<th className="px-6 py-3 text-left text-xs font-medium text-on-surface-variant uppercase tracking-wider">{t('tableHeaders.createdAt')}</th>
182182
</tr>
183183
</thead>
184-
<tbody className="divide-y divide-white/5">
184+
<tbody className="divide-y divide-outline-variant">
185185
{stats.recentOrders.map((order) => (
186-
<tr key={order.id} className="hover:bg-white/5 transition-colors">
187-
<td className="px-6 py-4 whitespace-nowrap text-sm text-white">{order.courseName}</td>
188-
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-300">{order.userName}</td>
189-
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-300">{order.amount} {t('currency')}</td>
186+
<tr key={order.id} className="hover:bg-surface-container-high transition-colors">
187+
<td className="px-6 py-4 whitespace-nowrap text-sm text-on-surface">{order.courseName}</td>
188+
<td className="px-6 py-4 whitespace-nowrap text-sm text-on-surface-variant">{order.userName}</td>
189+
<td className="px-6 py-4 whitespace-nowrap text-sm text-on-surface-variant">{order.amount} {t('currency')}</td>
190190
<td className="px-6 py-4 whitespace-nowrap">
191191
<StatusBadge status={order.status} label={t(`statuses.${order.status}`)} />
192192
</td>
193-
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-400">{formatDate(order.createdAt, locale)}</td>
193+
<td className="px-6 py-4 whitespace-nowrap text-sm text-on-surface-variant">{formatDate(order.createdAt, locale)}</td>
194194
</tr>
195195
))}
196196
{stats.recentOrders.length === 0 && (
197197
<tr>
198-
<td colSpan={5} className="px-6 py-8 text-center text-gray-400">{t('noOrders')}</td>
198+
<td colSpan={5} className="px-6 py-8 text-center text-on-surface-variant">{t('noOrders')}</td>
199199
</tr>
200200
)}
201201
</tbody>

0 commit comments

Comments
 (0)