实现了AI助手

This commit is contained in:
2025-06-24 14:09:12 +08:00
parent 7d139b22db
commit 8a4ccd5ef2
31 changed files with 2883 additions and 252 deletions
+100 -99
View File
@@ -32,7 +32,8 @@ const componentIcons: { [key: string]: any } = {
'机箱': Box,
}
export default function BuildPage() { const [componentTypes, setComponentTypes] = useState<ComponentType[]>([])
export default function BuildPage() {
const [componentTypes, setComponentTypes] = useState<ComponentType[]>([])
const [availableComponents, setAvailableComponents] = useState<{ [key: string]: Component[] }>({})
const [buildConfig, setBuildConfig] = useState<BuildConfiguration>({})
const [isLoading, setIsLoading] = useState(true)
@@ -121,7 +122,7 @@ export default function BuildPage() { const [componentTypes, setComponentTypes]
})
})
}
window.dispatchEvent(new Event('cart-updated'))
alert('配置已添加到购物车!')
} catch (error) {
@@ -177,11 +178,11 @@ export default function BuildPage() { const [componentTypes, setComponentTypes]
}
const getFilteredComponents = (typeName: string) => {
console.log(availableComponents,typeName);
console.log(availableComponents, typeName);
const components = availableComponents[typeName] || []
if (!searchTerm) return components
return components.filter(component =>
component.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
component.brand.toLowerCase().includes(searchTerm.toLowerCase())
@@ -206,7 +207,7 @@ export default function BuildPage() { const [componentTypes, setComponentTypes]
<div className="text-center mb-12">
<h1 className="text-4xl font-bold text-gray-900 mb-4">PC装机配置</h1>
<p className="text-xl text-gray-600 mb-8">选择每种配件,组装您的专属电脑</p>
{/* Progress */}
<div className="max-w-md mx-auto">
<div className="flex justify-between text-sm text-gray-600 mb-2">
@@ -214,7 +215,7 @@ export default function BuildPage() { const [componentTypes, setComponentTypes]
<span>{getCompletionRate()}%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${getCompletionRate()}%` }}
></div>
@@ -229,7 +230,7 @@ export default function BuildPage() { const [componentTypes, setComponentTypes]
{componentTypes.map((type) => {
const Icon = componentIcons[type.name] || Package
const selectedComponent = buildConfig[type.name]
return (
<div key={type.id} className="bg-white rounded-lg shadow-sm p-6">
<div className="flex items-center justify-between mb-4">
@@ -301,7 +302,7 @@ export default function BuildPage() { const [componentTypes, setComponentTypes]
<div className="lg:col-span-1">
<div className="bg-white rounded-lg shadow-sm p-6 sticky top-4">
<h2 className="text-lg font-semibold text-gray-900 mb-4">配置总览</h2>
<div className="space-y-3 mb-6">
{componentTypes.map((type) => {
const component = buildConfig[type.name]
@@ -314,7 +315,7 @@ export default function BuildPage() { const [componentTypes, setComponentTypes]
</div>
)
})}
<div className="border-t border-gray-200 pt-3">
<div className="flex justify-between font-semibold">
<span>总价:</span>
@@ -332,7 +333,7 @@ export default function BuildPage() { const [componentTypes, setComponentTypes]
<ShoppingCart className="h-5 w-5 inline mr-2" />
加入购物车
</button>
<button
onClick={createOrder}
disabled={getCompletionRate() < 100}
@@ -351,104 +352,104 @@ export default function BuildPage() { const [componentTypes, setComponentTypes]
{/* Component Selection Modal */}
{selectedType && (
<div className="fixed inset-0 bg-black bg-opacity-30 backdrop-blur-sm flex items-center justify-center p-4 z-50"> <div className="bg-white rounded-xl max-w-5xl w-full max-h-[85vh] overflow-hidden shadow-2xl">
<div className="p-6 border-b border-gray-200 bg-gray-50">
<div className="flex justify-between items-center mb-4">
<h3 className="text-xl font-semibold text-gray-900">选择 {selectedType}</h3>
<button
onClick={() => {
setSelectedType(null)
setSearchTerm('')
}}
className="text-gray-400 hover:text-gray-600 p-2 hover:bg-gray-200 rounded-full transition-colors"
>
<X className="h-5 w-5" />
</button>
<div className="fixed inset-0 bg-opacity-30 flex items-center justify-center p-4 z-50" style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }}> <div className="bg-white rounded-xl max-w-5xl w-full max-h-[85vh] overflow-hidden shadow-2xl">
<div className="p-6 border-b border-gray-200 bg-gray-50">
<div className="flex justify-between items-center mb-4">
<h3 className="text-xl font-semibold text-gray-900">选择 {selectedType}</h3>
<button
onClick={() => {
setSelectedType(null)
setSearchTerm('')
}}
className="text-gray-400 hover:text-gray-600 p-2 hover:bg-gray-200 rounded-full transition-colors"
>
<X className="h-5 w-5" />
</button>
</div>
{/* Search Box */}
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Search className="h-5 w-5 text-gray-400" />
</div>
{/* Search Box */}
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Search className="h-5 w-5 text-gray-400" />
</div>
<input
type="text"
placeholder={`搜索${selectedType}配件...`}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="block w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors"
/>
<input
type="text"
placeholder={`搜索${selectedType}配件...`}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="block w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors"
/>
{searchTerm && (
<button
onClick={() => setSearchTerm('')}
className="absolute inset-y-0 right-0 pr-3 flex items-center"
>
<X className="h-4 w-4 text-gray-400 hover:text-gray-600" />
</button>
)}
</div>
{/* Results count */}
<div className="mt-3 text-sm text-gray-600">
找到 {getFilteredComponents(selectedType).length} 个配件
{searchTerm && ` (搜索: "${searchTerm}")`}
</div>
</div>
<div className="p-6 overflow-y-auto" style={{ maxHeight: 'calc(85vh - 200px)' }}>
{getFilteredComponents(selectedType).length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{getFilteredComponents(selectedType).map((component) => (
<div
key={component.id}
className="border border-gray-200 rounded-lg p-5 hover:shadow-lg hover:border-blue-300 cursor-pointer transition-all duration-200 bg-white"
onClick={() => selectComponent(selectedType, component)}
>
<div className="w-full h-36 bg-gray-50 rounded-lg mb-4 flex items-center justify-center overflow-hidden">
{component.imageUrl ? (
<img loading='lazy'
src={component.imageUrl}
alt={component.name}
className="max-w-full max-h-full object-contain"
/>
) : (
<Package className="h-12 w-12 text-gray-400" />
)}
</div>
<h4 className="font-medium text-gray-900 mb-2 line-clamp-2 text-sm">
{component.name}
</h4>
<p className="text-sm text-gray-600 mb-3">{component.brand}</p>
<div className="flex items-center justify-between">
<p className="text-lg font-bold text-red-600">¥{component.price}</p>
<button className="text-blue-600 hover:text-blue-800 text-sm font-medium">
选择
</button>
</div>
</div>
))}
</div>
) : (
<div className="text-center py-12">
<Package className="h-16 w-16 text-gray-300 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2">
{searchTerm ? '没有找到匹配的配件' : '暂无配件'}
</h3>
<p className="text-gray-500">
{searchTerm ? '尝试调整搜索关键词' : '此类型暂无可用配件'}
</p>
{searchTerm && (
<button
onClick={() => setSearchTerm('')}
className="absolute inset-y-0 right-0 pr-3 flex items-center"
className="mt-4 text-blue-600 hover:text-blue-800 font-medium"
>
<X className="h-4 w-4 text-gray-400 hover:text-gray-600" />
清除搜索
</button>
)}
</div>
{/* Results count */}
<div className="mt-3 text-sm text-gray-600">
找到 {getFilteredComponents(selectedType).length} 个配件
{searchTerm && ` (搜索: "${searchTerm}")`}
</div>
</div>
<div className="p-6 overflow-y-auto" style={{ maxHeight: 'calc(85vh - 200px)' }}>
{getFilteredComponents(selectedType).length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{getFilteredComponents(selectedType).map((component) => (
<div
key={component.id}
className="border border-gray-200 rounded-lg p-5 hover:shadow-lg hover:border-blue-300 cursor-pointer transition-all duration-200 bg-white"
onClick={() => selectComponent(selectedType, component)}
>
<div className="w-full h-36 bg-gray-50 rounded-lg mb-4 flex items-center justify-center overflow-hidden">
{component.imageUrl ? (
<img loading='lazy'
src={component.imageUrl}
alt={component.name}
className="max-w-full max-h-full object-contain"
/>
) : (
<Package className="h-12 w-12 text-gray-400" />
)}
</div>
<h4 className="font-medium text-gray-900 mb-2 line-clamp-2 text-sm">
{component.name}
</h4>
<p className="text-sm text-gray-600 mb-3">{component.brand}</p>
<div className="flex items-center justify-between">
<p className="text-lg font-bold text-red-600">¥{component.price}</p>
<button className="text-blue-600 hover:text-blue-800 text-sm font-medium">
选择
</button>
</div>
</div>
))}
</div>
) : (
<div className="text-center py-12">
<Package className="h-16 w-16 text-gray-300 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-900 mb-2">
{searchTerm ? '没有找到匹配的配件' : '暂无配件'}
</h3>
<p className="text-gray-500">
{searchTerm ? '尝试调整搜索关键词' : '此类型暂无可用配件'}
</p>
{searchTerm && (
<button
onClick={() => setSearchTerm('')}
className="mt-4 text-blue-600 hover:text-blue-800 font-medium"
>
清除搜索
</button>
)}
</div>
)}
</div>
)}
</div>
</div>
</div>
)}
</div>
</div>