- 首页UI统一到三层架构 - 将所有 gray 系列替换为 slate 系列 - 统一按钮、输入框、边框颜色样式 - 使用 useThemeStyles() Hook - 创建通用组件库 - Badge 组件(支持 primary、success、warning、danger 变体) - Tag 组件(支持多种颜色,已在首页应用) - Button 组件(支持多种变体和尺寸,带加载状态) - Input 组件(支持错误状态和多种尺寸)
This commit is contained in:
parent
7dc8d2a528
commit
a604b4d488
171
app/page.tsx
171
app/page.tsx
@ -11,6 +11,7 @@ import RankingPanel from '@/components/RankingPanel'
|
||||
import AIChatWidget from '@/components/AIChatWidget'
|
||||
import ChangePasswordModal from '@/components/ChangePasswordModal'
|
||||
import MaterialPreviewModal from '@/components/MaterialPreviewModal'
|
||||
import Tag from '@/components/ui/Tag'
|
||||
import { useThemeStyles } from '@/lib/ui-styles'
|
||||
import { useDialog } from '@/components/ui/DialogProvider'
|
||||
|
||||
@ -513,8 +514,8 @@ export default function Home() {
|
||||
|
||||
if (status === 'loading' || loading) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center dark:bg-gray-900">
|
||||
<div className="text-xl text-gray-600 dark:text-gray-400">加载中...</div>
|
||||
<div className="min-h-screen flex items-center justify-center bg-slate-50 dark:bg-slate-900">
|
||||
<div className="text-xl text-slate-600 dark:text-slate-400">加载中...</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -524,7 +525,7 @@ export default function Home() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
|
||||
<div className="min-h-screen bg-slate-50 dark:bg-slate-900">
|
||||
<Header session={session} />
|
||||
|
||||
{/* 内网切换提示 */}
|
||||
@ -594,7 +595,7 @@ export default function Home() {
|
||||
className={`${styles.inputStyles.base} pl-12 pr-4`}
|
||||
/>
|
||||
<svg
|
||||
className="absolute left-4 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400"
|
||||
className="absolute left-4 top-1/2 transform -translate-y-1/2 w-5 h-5 text-slate-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
@ -609,7 +610,7 @@ export default function Home() {
|
||||
{searchKeyword && (
|
||||
<button
|
||||
onClick={() => setSearchKeyword('')}
|
||||
className="absolute right-4 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
className="absolute right-4 top-1/2 transform -translate-y-1/2 text-slate-400 hover:text-slate-600 dark:hover:text-slate-300"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
@ -640,9 +641,9 @@ export default function Home() {
|
||||
<>
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-sm font-medium text-gray-700 dark:text-gray-300">类型分页</h2>
|
||||
<h2 className="text-sm font-medium text-slate-700 dark:text-slate-300">类型分页</h2>
|
||||
{selectedMaterialType && (
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400">
|
||||
{materials.length} 个素材
|
||||
</span>
|
||||
)}
|
||||
@ -653,7 +654,7 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
||||
selectedMaterialType === ''
|
||||
? 'text-white'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={selectedMaterialType === '' ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
@ -666,7 +667,7 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
||||
selectedMaterialType === type.value
|
||||
? 'text-white'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={selectedMaterialType === type.value ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
@ -679,9 +680,9 @@ export default function Home() {
|
||||
{/* Category Filter (使用店铺) */}
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-sm font-medium text-gray-700 dark:text-gray-300">使用店铺</h2>
|
||||
<h2 className="text-sm font-medium text-slate-700 dark:text-slate-300">使用店铺</h2>
|
||||
{selectedCategory && (
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400">
|
||||
{materials.length} 个素材
|
||||
</span>
|
||||
)}
|
||||
@ -692,15 +693,15 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
||||
selectedCategory === ''
|
||||
? 'text-white'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={selectedCategory === '' ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
全部
|
||||
<span className={`ml-1.5 text-xs ${
|
||||
selectedCategory === ''
|
||||
? 'text-gray-300'
|
||||
: 'text-gray-500 dark:text-gray-400'
|
||||
? 'text-slate-300'
|
||||
: 'text-slate-500 dark:text-slate-400'
|
||||
}`}>
|
||||
({totalMaterialsCount})
|
||||
</span>
|
||||
@ -712,15 +713,15 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
||||
selectedCategory === category.name
|
||||
? 'text-white'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={selectedCategory === category.name ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
{category.name}
|
||||
<span className={`ml-1.5 text-xs ${
|
||||
selectedCategory === category.name
|
||||
? 'text-gray-300'
|
||||
: 'text-gray-500 dark:text-gray-400'
|
||||
? 'text-slate-300'
|
||||
: 'text-slate-500 dark:text-slate-400'
|
||||
}`}>
|
||||
({category.count || 0})
|
||||
</span>
|
||||
@ -732,9 +733,9 @@ export default function Home() {
|
||||
{/* Scene Filter (场景类别) */}
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-sm font-medium text-gray-700 dark:text-gray-300">场景类别</h2>
|
||||
<h2 className="text-sm font-medium text-slate-700 dark:text-slate-300">场景类别</h2>
|
||||
{selectedScene && (
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400">
|
||||
{materials.length} 个素材
|
||||
</span>
|
||||
)}
|
||||
@ -745,15 +746,15 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
||||
selectedScene === ''
|
||||
? 'text-white'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={selectedScene === '' ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
全部
|
||||
<span className={`ml-1.5 text-xs ${
|
||||
selectedScene === ''
|
||||
? 'text-gray-300'
|
||||
: 'text-gray-500 dark:text-gray-400'
|
||||
? 'text-slate-300'
|
||||
: 'text-slate-500 dark:text-slate-400'
|
||||
}`}>
|
||||
({totalMaterialsCount})
|
||||
</span>
|
||||
@ -765,15 +766,15 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
||||
selectedScene === scene.name
|
||||
? 'text-white'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={selectedScene === scene.name ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
{scene.name}
|
||||
<span className={`ml-1.5 text-xs ${
|
||||
selectedScene === scene.name
|
||||
? 'text-gray-300'
|
||||
: 'text-gray-500 dark:text-gray-400'
|
||||
? 'text-slate-300'
|
||||
: 'text-slate-500 dark:text-slate-400'
|
||||
}`}>
|
||||
({scene.count || 0})
|
||||
</span>
|
||||
@ -786,9 +787,9 @@ export default function Home() {
|
||||
{platforms.length > 0 && (
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-sm font-medium text-gray-700 dark:text-gray-300">平台分类</h2>
|
||||
<h2 className="text-sm font-medium text-slate-700 dark:text-slate-300">平台分类</h2>
|
||||
{selectedPlatform && (
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400">
|
||||
{materials.length} 个素材
|
||||
</span>
|
||||
)}
|
||||
@ -799,15 +800,15 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
||||
selectedPlatform === ''
|
||||
? 'text-white'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={selectedPlatform === '' ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
全部
|
||||
<span className={`ml-1.5 text-xs ${
|
||||
selectedPlatform === ''
|
||||
? 'text-gray-300'
|
||||
: 'text-gray-500 dark:text-gray-400'
|
||||
? 'text-slate-300'
|
||||
: 'text-slate-500 dark:text-slate-400'
|
||||
}`}>
|
||||
({totalMaterialsCount})
|
||||
</span>
|
||||
@ -819,15 +820,15 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
||||
selectedPlatform === platform.name
|
||||
? 'text-white'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={selectedPlatform === platform.name ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
{platform.name}
|
||||
<span className={`ml-1.5 text-xs ${
|
||||
selectedPlatform === platform.name
|
||||
? 'text-gray-300'
|
||||
: 'text-gray-500 dark:text-gray-400'
|
||||
? 'text-slate-300'
|
||||
: 'text-slate-500 dark:text-slate-400'
|
||||
}`}>
|
||||
({platform.count || 0})
|
||||
</span>
|
||||
@ -840,7 +841,7 @@ export default function Home() {
|
||||
{/* Month Filter (上传月份) */}
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-sm font-medium text-gray-700 dark:text-gray-300">上传月份</h2>
|
||||
<h2 className="text-sm font-medium text-slate-700 dark:text-slate-300">上传月份</h2>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button
|
||||
@ -848,15 +849,15 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
||||
selectedMonth === ''
|
||||
? 'text-white'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={selectedMonth === '' ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
全部
|
||||
<span className={`ml-1.5 text-xs ${
|
||||
selectedMonth === ''
|
||||
? 'text-gray-300'
|
||||
: 'text-gray-500 dark:text-gray-400'
|
||||
? 'text-slate-300'
|
||||
: 'text-slate-500 dark:text-slate-400'
|
||||
}`}>
|
||||
({totalMaterialsCount})
|
||||
</span>
|
||||
@ -868,15 +869,15 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
||||
selectedMonth === month.value
|
||||
? 'text-white'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={selectedMonth === month.value ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
{month.label}
|
||||
<span className={`ml-1.5 text-xs ${
|
||||
selectedMonth === month.value
|
||||
? 'text-gray-300'
|
||||
: 'text-gray-500 dark:text-gray-400'
|
||||
? 'text-slate-300'
|
||||
: 'text-slate-500 dark:text-slate-400'
|
||||
}`}>
|
||||
({month.count})
|
||||
</span>
|
||||
@ -891,7 +892,7 @@ export default function Home() {
|
||||
{/* Sort Options (排序方式) - 保留排序选项 */}
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-sm font-medium text-gray-700 dark:text-gray-300">排序方式</h2>
|
||||
<h2 className="text-sm font-medium text-slate-700 dark:text-slate-300">排序方式</h2>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button
|
||||
@ -899,7 +900,7 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
||||
sortBy === 'createdAt'
|
||||
? 'text-white'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={sortBy === 'createdAt' ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
@ -910,7 +911,7 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
||||
sortBy === 'downloadCount'
|
||||
? 'text-white'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={sortBy === 'downloadCount' ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
@ -922,69 +923,69 @@ export default function Home() {
|
||||
{/* Active Filters Display */}
|
||||
{(searchKeyword || selectedCategory || selectedScene || selectedMaterialType || selectedPlatform || selectedMonth) && (
|
||||
<div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-apple p-3 mb-4">
|
||||
<div className="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400 flex-wrap">
|
||||
<div className="flex items-center gap-2 text-sm text-slate-600 dark:text-slate-400 flex-wrap">
|
||||
<span className="font-medium text-blue-600 dark:text-blue-400">当前筛选:</span>
|
||||
{searchKeyword && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-gray-100 dark:bg-gray-800 rounded-full">
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-slate-100 dark:bg-slate-800 rounded-full">
|
||||
搜索: {searchKeyword}
|
||||
<button
|
||||
onClick={() => setSearchKeyword('')}
|
||||
className="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
|
||||
className="text-slate-500 hover:text-slate-700 dark:hover:text-slate-300"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{selectedCategory && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-gray-100 dark:bg-gray-800 rounded-full">
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-slate-100 dark:bg-slate-800 rounded-full">
|
||||
店铺: {selectedCategory}
|
||||
<button
|
||||
onClick={() => setSelectedCategory('')}
|
||||
className="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
|
||||
className="text-slate-500 hover:text-slate-700 dark:hover:text-slate-300"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{selectedScene && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-gray-100 dark:bg-gray-800 rounded-full">
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-slate-100 dark:bg-slate-800 rounded-full">
|
||||
场景类别: {selectedScene}
|
||||
<button
|
||||
onClick={() => setSelectedScene('')}
|
||||
className="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
|
||||
className="text-slate-500 hover:text-slate-700 dark:hover:text-slate-300"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{selectedMaterialType && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-gray-100 dark:bg-gray-800 rounded-full">
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-slate-100 dark:bg-slate-800 rounded-full">
|
||||
素材类型: {selectedMaterialType}
|
||||
<button
|
||||
onClick={() => setSelectedMaterialType('')}
|
||||
className="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
|
||||
className="text-slate-500 hover:text-slate-700 dark:hover:text-slate-300"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{selectedPlatform && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-gray-100 dark:bg-gray-800 rounded-full">
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-slate-100 dark:bg-slate-800 rounded-full">
|
||||
平台分类: {selectedPlatform}
|
||||
<button
|
||||
onClick={() => setSelectedPlatform('')}
|
||||
className="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
|
||||
className="text-slate-500 hover:text-slate-700 dark:hover:text-slate-300"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{selectedMonth && (
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-gray-100 dark:bg-gray-800 rounded-full">
|
||||
<span className="inline-flex items-center gap-1 px-3 py-1 bg-slate-100 dark:bg-slate-800 rounded-full">
|
||||
月份: {months.find(m => m.value === selectedMonth)?.label || selectedMonth}
|
||||
<button
|
||||
onClick={() => setSelectedMonth('')}
|
||||
className="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
|
||||
className="text-slate-500 hover:text-slate-700 dark:hover:text-slate-300"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
@ -1004,7 +1005,7 @@ export default function Home() {
|
||||
清除所有筛选
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 mt-2">
|
||||
<div className="text-xs text-slate-500 dark:text-slate-400 mt-2">
|
||||
💡 提示:可以组合使用多个筛选条件,找到更精确的素材
|
||||
</div>
|
||||
</div>
|
||||
@ -1014,11 +1015,11 @@ export default function Home() {
|
||||
{/* Materials Grid */}
|
||||
{loading ? (
|
||||
<div className="text-center py-16">
|
||||
<p className="text-gray-500 dark:text-gray-400 text-lg">加载中...</p>
|
||||
<p className="text-slate-500 dark:text-slate-400 text-lg">加载中...</p>
|
||||
</div>
|
||||
) : materials.length === 0 ? (
|
||||
<div className="text-center py-16">
|
||||
<p className="text-gray-500 dark:text-gray-400 text-lg">
|
||||
<p className="text-slate-500 dark:text-slate-400 text-lg">
|
||||
{searchKeyword || selectedCategory || selectedScene || selectedMaterialType || selectedPlatform ? '没有找到匹配的素材' : '还没有素材,快来上传第一个吧!'}
|
||||
</p>
|
||||
</div>
|
||||
@ -1064,24 +1065,16 @@ export default function Home() {
|
||||
<div className="p-4">
|
||||
{/* 店铺、场景、素材类型和渲染器 */}
|
||||
<div className="flex items-center gap-2 mb-3 flex-wrap">
|
||||
<span className="inline-flex items-center px-2 py-1 bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 rounded text-xs font-medium">
|
||||
{material.categoryName}
|
||||
</span>
|
||||
<span className="inline-flex items-center px-2 py-1 bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300 rounded text-xs font-medium">
|
||||
{material.sceneName}
|
||||
</span>
|
||||
<span className="inline-flex items-center px-2 py-1 bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300 rounded text-xs font-medium">
|
||||
{material.materialType}
|
||||
</span>
|
||||
<Tag color="blue">{material.categoryName}</Tag>
|
||||
<Tag color="green">{material.sceneName}</Tag>
|
||||
<Tag color="orange">{material.materialType}</Tag>
|
||||
{material.materialType === '3DMAX' && (
|
||||
<span className="inline-flex items-center px-2 py-1 bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 rounded text-xs font-medium">
|
||||
{material.renderer}
|
||||
</span>
|
||||
<Tag color="purple">{material.renderer}</Tag>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 作者和时间 */}
|
||||
<div className="space-y-1 text-sm text-gray-600 dark:text-gray-400 mb-3">
|
||||
<div className="space-y-1 text-sm text-slate-600 dark:text-slate-400 mb-3">
|
||||
<p className="truncate">
|
||||
<span className="font-medium">作者:</span>
|
||||
{material.authorName}
|
||||
@ -1094,13 +1087,13 @@ export default function Home() {
|
||||
<span className="font-medium">大小:</span>
|
||||
<span className="text-blue-600 dark:text-blue-400">{formatFileSize(material.zipFileSize)}</span>
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-500">
|
||||
<p className="text-xs text-slate-500 dark:text-slate-500">
|
||||
{format(new Date(material.createdAt), 'yyyy-MM-dd HH:mm')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 统计数据 */}
|
||||
<div className="flex items-center justify-between text-xs text-gray-500 dark:text-gray-400 mb-3 pb-3 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center justify-between text-xs text-slate-500 dark:text-slate-400 mb-3 pb-3 border-b border-slate-200 dark:border-slate-700">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="flex items-center gap-1">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@ -1135,7 +1128,7 @@ export default function Home() {
|
||||
)}
|
||||
<button
|
||||
onClick={() => handleShare(material.id)}
|
||||
className="flex-none px-4 py-2 rounded-apple font-medium text-sm transition-all bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"
|
||||
className={`${styles.buttonStyles.secondary} ${styles.buttonModifiers.compact} flex-none`}
|
||||
title="分享"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@ -1189,7 +1182,7 @@ export default function Home() {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}}
|
||||
disabled={currentPage === 1}
|
||||
className="px-4 py-2 rounded-lg bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
||||
className={`${styles.buttonStyles.secondary} ${styles.buttonModifiers.compact} disabled:opacity-50 disabled:cursor-not-allowed`}
|
||||
>
|
||||
首页
|
||||
</button>
|
||||
@ -1201,7 +1194,7 @@ export default function Home() {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}}
|
||||
disabled={currentPage === 1}
|
||||
className="px-4 py-2 rounded-lg bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
||||
className={`${styles.buttonStyles.secondary} ${styles.buttonModifiers.compact} disabled:opacity-50 disabled:cursor-not-allowed`}
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
@ -1232,7 +1225,7 @@ export default function Home() {
|
||||
className={`px-4 py-2 rounded-lg transition-all ${
|
||||
currentPage === pageNum
|
||||
? 'text-white font-medium'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-300 dark:border-slate-600 hover:bg-slate-50 dark:hover:bg-slate-700'
|
||||
}`}
|
||||
style={currentPage === pageNum ? { backgroundColor: themeColor } : {}}
|
||||
>
|
||||
@ -1249,7 +1242,7 @@ export default function Home() {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}}
|
||||
disabled={currentPage === totalPages}
|
||||
className="px-4 py-2 rounded-lg bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
||||
className={`${styles.buttonStyles.secondary} ${styles.buttonModifiers.compact} disabled:opacity-50 disabled:cursor-not-allowed`}
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
@ -1263,7 +1256,7 @@ export default function Home() {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}}
|
||||
disabled={currentPage === totalPages}
|
||||
className="px-4 py-2 rounded-lg bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
||||
className={`${styles.buttonStyles.secondary} ${styles.buttonModifiers.compact} disabled:opacity-50 disabled:cursor-not-allowed`}
|
||||
>
|
||||
末页
|
||||
</button>
|
||||
@ -1271,11 +1264,11 @@ export default function Home() {
|
||||
|
||||
{/* 页码跳转和信息 */}
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
第 {currentPage} / {totalPages} 页,共 {totalMaterialsCount} 条
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">跳至</span>
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">跳至</span>
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
@ -1292,10 +1285,10 @@ export default function Home() {
|
||||
}
|
||||
}
|
||||
}}
|
||||
className="w-16 px-2 py-1 text-center rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 outline-none"
|
||||
className={`${styles.inputStyles.base} w-16 px-2 py-1 text-center`}
|
||||
placeholder=""
|
||||
/>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">页</span>
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">页</span>
|
||||
<button
|
||||
onClick={() => {
|
||||
const page = parseInt(jumpPage)
|
||||
@ -1544,7 +1537,7 @@ export default function Home() {
|
||||
setSelectedPlatform('')
|
||||
setSelectedMonth('')
|
||||
}}
|
||||
className="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
||||
className={`${styles.buttonStyles.secondary} ${styles.buttonModifiers.compact}`}
|
||||
>
|
||||
清除全部
|
||||
</button>
|
||||
@ -1567,11 +1560,11 @@ export default function Home() {
|
||||
onClick={() => setShowShareModal(false)}
|
||||
>
|
||||
<div
|
||||
className="bg-white dark:bg-gray-800 rounded-xl p-6 max-w-lg w-full mx-4 shadow-2xl"
|
||||
className={`${styles.cardStyles.base} max-w-lg w-full mx-4`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<h3 className="text-lg font-semibold mb-4 text-gray-900 dark:text-white">分享链接</h3>
|
||||
<div className="bg-gray-100 dark:bg-gray-700 p-3 rounded-lg mb-4 break-all text-sm text-gray-800 dark:text-gray-200">
|
||||
<h3 className="text-lg font-semibold mb-4 text-slate-900 dark:text-white">分享链接</h3>
|
||||
<div className="bg-slate-100 dark:bg-slate-700 p-3 rounded-lg mb-4 break-all text-sm text-slate-800 dark:text-slate-200">
|
||||
{shareUrl}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
@ -1584,7 +1577,7 @@ export default function Home() {
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowShareModal(false)}
|
||||
className="flex-1 bg-gray-300 dark:bg-gray-600 hover:bg-gray-400 dark:hover:bg-gray-500 text-gray-800 dark:text-white px-4 py-2 rounded-lg transition-colors"
|
||||
className={`${styles.buttonStyles.secondary} ${styles.buttonModifiers.compact} flex-1`}
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
|
||||
40
components/ui/Badge.tsx
Normal file
40
components/ui/Badge.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
'use client'
|
||||
|
||||
import { ReactNode } from 'react'
|
||||
import { useThemeStyles, cn } from '@/lib/ui-styles'
|
||||
|
||||
interface BadgeProps {
|
||||
children: ReactNode
|
||||
variant?: 'primary' | 'success' | 'warning' | 'danger'
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
className?: string
|
||||
}
|
||||
|
||||
export default function Badge({
|
||||
children,
|
||||
variant = 'primary',
|
||||
size = 'md',
|
||||
className = '',
|
||||
}: BadgeProps) {
|
||||
const styles = useThemeStyles()
|
||||
|
||||
const sizeClasses = {
|
||||
sm: 'px-2 py-0.5 text-xs',
|
||||
md: 'px-2 py-1 text-xs',
|
||||
lg: 'px-3 py-1.5 text-sm',
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
styles.badgeStyles[variant],
|
||||
sizeClasses[size],
|
||||
'inline-flex items-center font-medium rounded',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
86
components/ui/Button.tsx
Normal file
86
components/ui/Button.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
'use client'
|
||||
|
||||
import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react'
|
||||
import { useThemeStyles, cn } from '@/lib/ui-styles'
|
||||
|
||||
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children: ReactNode
|
||||
variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'success'
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
fullWidth?: boolean
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(
|
||||
{
|
||||
children,
|
||||
variant = 'primary',
|
||||
size = 'md',
|
||||
fullWidth = false,
|
||||
loading = false,
|
||||
disabled,
|
||||
className = '',
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const styles = useThemeStyles()
|
||||
|
||||
const sizeClasses = {
|
||||
sm: styles.buttonModifiers.compactXs,
|
||||
md: '',
|
||||
lg: 'px-8 py-4 text-base',
|
||||
}
|
||||
|
||||
const variantClass = styles.buttonStyles[variant]
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
disabled={disabled || loading}
|
||||
className={cn(
|
||||
variantClass,
|
||||
sizeClasses[size],
|
||||
fullWidth && styles.buttonModifiers.block,
|
||||
loading && 'opacity-75 cursor-wait',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{loading ? (
|
||||
<span className="flex items-center gap-2">
|
||||
<svg
|
||||
className="animate-spin h-4 w-4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"
|
||||
/>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
/>
|
||||
</svg>
|
||||
<span>加载中...</span>
|
||||
</span>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
Button.displayName = 'Button'
|
||||
|
||||
export default Button
|
||||
|
||||
51
components/ui/Input.tsx
Normal file
51
components/ui/Input.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
'use client'
|
||||
|
||||
import { InputHTMLAttributes, forwardRef } from 'react'
|
||||
import { useThemeStyles, cn } from '@/lib/ui-styles'
|
||||
|
||||
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
error?: boolean
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
fullWidth?: boolean
|
||||
}
|
||||
|
||||
const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
(
|
||||
{
|
||||
error = false,
|
||||
size = 'md',
|
||||
fullWidth = true,
|
||||
className = '',
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const styles = useThemeStyles()
|
||||
|
||||
const sizeClasses = {
|
||||
sm: styles.inputModifiers.compactXs,
|
||||
md: '',
|
||||
lg: 'px-5 py-4 text-base',
|
||||
}
|
||||
|
||||
const inputClass = error ? styles.inputStyles.error : styles.inputStyles.base
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={ref}
|
||||
className={cn(
|
||||
inputClass,
|
||||
sizeClasses[size],
|
||||
!fullWidth && 'w-auto',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
Input.displayName = 'Input'
|
||||
|
||||
export default Input
|
||||
|
||||
56
components/ui/Tag.tsx
Normal file
56
components/ui/Tag.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
'use client'
|
||||
|
||||
import { ReactNode } from 'react'
|
||||
import { useThemeStyles, cn } from '@/lib/ui-styles'
|
||||
|
||||
interface TagProps {
|
||||
children: ReactNode
|
||||
color?: 'blue' | 'green' | 'orange' | 'purple' | 'indigo' | 'pink' | 'cyan'
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
className?: string
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
export default function Tag({
|
||||
children,
|
||||
color = 'blue',
|
||||
size = 'md',
|
||||
className = '',
|
||||
onClick,
|
||||
}: TagProps) {
|
||||
const styles = useThemeStyles()
|
||||
|
||||
const colorClasses = {
|
||||
blue: 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300',
|
||||
green: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
||||
orange: 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300',
|
||||
purple: 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300',
|
||||
indigo: 'bg-indigo-100 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300',
|
||||
pink: 'bg-pink-100 dark:bg-pink-900/30 text-pink-700 dark:text-pink-300',
|
||||
cyan: 'bg-cyan-100 dark:bg-cyan-900/30 text-cyan-700 dark:text-cyan-300',
|
||||
}
|
||||
|
||||
const sizeClasses = {
|
||||
sm: 'px-2 py-0.5 text-xs',
|
||||
md: 'px-2 py-1 text-xs',
|
||||
lg: 'px-3 py-1.5 text-sm',
|
||||
}
|
||||
|
||||
const baseClasses = 'inline-flex items-center font-medium rounded transition-colors'
|
||||
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
baseClasses,
|
||||
colorClasses[color],
|
||||
sizeClasses[size],
|
||||
onClick && 'cursor-pointer hover:opacity-80',
|
||||
className
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user