This commit is contained in:
2025-06-22 11:34:32 +08:00
parent 142992a6c9
commit 7d139b22db
55 changed files with 11035 additions and 105 deletions
+662
View File
@@ -0,0 +1,662 @@
[
{
"name": "英特尔(INTEL)INTELE31225V3122612451246123112761275V312711285V3CPU二 E31246V3带核显",
"brand": "英特尔(INTEL)",
"model": "E3-1246V3",
"price": 285.96,
"description": "带核显",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/228561/16/23683/131837/66cf4403Fcb23e4a3/33a8da8e60781001.jpg",
"stock": 30,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":8,\"baseClock\":\"3.5GHz\",\"boostClock\":\"3.9GHz\",\"socket\":\"LGA1150\",\"integratedGraphics\":\"Intel HD Graphics P4600\"}"
},
{
"name": "英特尔(Intel) i5-14600KF 酷睿14代 处理器",
"brand": "英特尔(Intel)",
"model": "i5-14600KF",
"price": 1399,
"description": "酷睿14代i5,可超频,192GB至高内存容量,兼容600/700主板芯片组,更高帧率,更快相应,游戏办公两不误!",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/293395/35/15365/120737/68552403F3dbabf43/d4b4bb8fcfd8286f.png",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"cores\":14,\"threads\":20,\"baseClock\":\"3.5GHz (P-core); 2.6GHz (E-core)\",\"boostClock\":\"5.3GHz (P-core); 4.0GHz (E-core)\",\"socket\":\"LGA1700\",\"cache\":\"24M三级缓存\"}"
},
{
"name": "英特尔(Intel)酷睿 Ultra 7 265K 台式机处理器",
"brand": "英特尔(Intel)",
"model": "酷睿 Ultra 7 265K",
"price": 2294.4,
"description": "酷睿Ultra7,睿频至高5.5GHz,核显支持,全新3D混合架构,智能功耗管理,兼容810/860/890芯片组,电竞创作两不误!",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/296128/30/4814/80017/68552415F3cc7312c/37d758b30c4aa079.png",
"stock": 27,
"typeName": "CPU",
"specifications": "{\"cores\":20,\"threads\":20,\"boostClock\":\"5.5GHz\",\"socket\":\"LGA1851 (推测)\",\"architecture\":\"全新3D混合架构\",\"integratedGraphics\":\"支持\"}"
},
{
"name": "英特尔(Intel)i5-13490F 酷睿13代 处理器",
"brand": "英特尔(Intel)",
"model": "i5-13490F",
"price": 849,
"description": "混合CPU架构,多线程高效能,流畅体验超乎想象!",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/299590/39/16486/101405/68552406F754195d2/e9c26d0d7fac6f87.png",
"stock": 15,
"typeName": "CPU",
"specifications": "{\"cores\":10,\"threads\":16,\"cache\":\"24M三级缓存\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel)酷睿3代i7-3770K/i5-3470/i5-3570K/i3-3240/3220/LGA1155/CPU i5-3570(盒)",
"brand": "英特尔(Intel)",
"model": "i5-3570(盒)",
"price": 579,
"description": "只卖正品/零售批发",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/265175/9/29671/69017/67caf6f0Fffc9e56b/f3c03d8a8ff47385.jpg",
"stock": 14,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":4,\"baseClock\":\"3.4GHz\",\"boostClock\":\"3.8GHz\",\"socket\":\"LGA1155\",\"integratedGraphics\":\"Intel HD Graphics 2500\"}"
},
{
"name": "英特尔i5 i7酷睿12/13/14代CPU处理器 12700KF 14600KF全新盒装 散片 I5 14600KF盒装",
"brand": "英特尔",
"model": "I5 14600KF盒装",
"price": 1279,
"description": "全新盒装",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/291736/21/10248/133257/6853cda6F418d8342/d398288fbd6ff1fd.png",
"stock": 26,
"typeName": "CPU",
"specifications": "{\"cores\":14,\"threads\":20,\"baseClock\":\"3.5GHz (P-core); 2.6GHz (E-core)\",\"boostClock\":\"5.3GHz (P-core); 4.0GHz (E-core)\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔(Intel) i7-14700KF 酷睿14代 处理器",
"brand": "英特尔(Intel)",
"model": "i7-14700KF",
"price": 2399,
"description": "酷睿14代i7,192GB至高内存容量,旗舰超频,可兼容600/700主板,内容设计创作更流畅!",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/314535/25/11097/120004/685523fdFcd2bc63a/985bd41061a2a352.png",
"stock": 15,
"typeName": "CPU",
"specifications": "{\"cores\":20,\"threads\":28,\"baseClock\":\"3.4GHz (P-core); 2.5GHz (E-core)\",\"boostClock\":\"5.5GHz (P-core); 4.3GHz (E-core)\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔(Intel)酷睿Ultra CPU 台式机处理器 盒装CPU Ultra 9 285K 盒装【24核24线程】",
"brand": "英特尔(Intel)",
"model": "酷睿 Ultra 9 285K",
"price": 4499,
"description": "英特尔酷睿Ultra台式机处理器(第二代)解锁AI新竞界。功耗更优化,多线程性能再提升,解锁AI+游戏新体验,超频体验再升级!爆款晒单返京豆,详情请咨询客服。",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/318828/19/10448/103437/685528c4F5eb17246/78027a05983da637.png",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"cores\":24,\"threads\":24,\"socket\":\"LGA1851 (推测)\",\"architecture\":\"第二代酷睿Ultra\"}"
},
{
"name": "英特尔(Intel)酷睿i7 X系列/i7-5820K/i7-5960X至尊版/LGA2011/X99 i7-5960X",
"brand": "英特尔(Intel)",
"model": "i7-5960X",
"price": 899,
"description": "只卖正品/零售批发",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/275584/12/4224/113524/67d68f9dFf5ac3e73/8397b31a24870483.jpg",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"cores\":8,\"threads\":16,\"baseClock\":\"3.0GHz\",\"boostClock\":\"3.5GHz\",\"socket\":\"LGA2011-3\",\"cache\":\"20MB\"}"
},
{
"name": "英特尔(Intel)酷睿 Ultra 9 285K 台式机处理器",
"brand": "英特尔(Intel)",
"model": "酷睿 Ultra 9 285K",
"price": 4340.3,
"description": "酷睿Ultra9,睿频至高5.7GHz,全新3D混合架构,支持内存超频,兼容810/860/890芯片组,更流畅!更节能!更静音!",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/314400/10/11078/94234/68552411Fa8f0998f/57b264cc7fb1e327.png",
"stock": 27,
"typeName": "CPU",
"specifications": "{\"cores\":24,\"threads\":24,\"boostClock\":\"5.7GHz\",\"socket\":\"LGA1851 (推测)\",\"architecture\":\"全新3D混合架构\",\"memoryOverclocking\":\"支持\"}"
},
{
"name": "英特尔(Intel)酷睿 Ultra 9 285 台式机处理器",
"brand": "英特尔(Intel)",
"model": "酷睿 Ultra 9 285",
"price": 4290.4,
"description": "酷睿Ultra9,36MB智能缓存,全新3D混合架构,原装风扇支持,支持内存超频,兼容810/860/890芯片组,体验再升级!",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/296514/7/17725/88895/68552412F05717cf5/cf2552e83048391a.png",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"cores\":24,\"threads\":24,\"boostClock\":\"5.6GHz\",\"socket\":\"LGA1851 (推测)\",\"cache\":\"36MB智能缓存\",\"architecture\":\"全新3D混合架构\",\"memoryOverclocking\":\"支持\"}"
},
{
"name": "英特尔(Intel) i9-14900K 酷睿14代",
"brand": "英特尔(Intel)",
"model": "i9-14900K",
"price": 3399,
"description": "酷睿14代i9,单核超高睿频,AI超频,192GB至高内存容量,内存超频至高可达8000+,可兼容600/700主板,畅玩游戏无卡顿!",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/297671/28/17012/116505/685523fbF24b22709/01d545c39f494f0e.png",
"stock": 26,
"typeName": "CPU",
"specifications": "{\"cores\":24,\"threads\":32,\"socket\":\"LGA1700\",\"memoryCapacity\":\"192GB\",\"memorySpeed\":\"8000+ MHz (超频)\",\"integratedGraphics\":\"Intel UHD Graphics 770\"}"
},
{
"name": "英特尔(Intel)酷睿8代i7-8700K/8700/8086K/i5-8600K/8600/8500/8400/LGA1151 i5-8600K(盒)",
"brand": "英特尔(Intel)",
"model": "i5-8600K(盒)",
"price": 899,
"description": "只卖正品/零售批发",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/262118/29/30839/54424/67cfa4a8F55f141c1/c2fa42c20ef55ce0.jpg",
"stock": 10,
"typeName": "CPU",
"specifications": "{\"cores\":6,\"threads\":6,\"baseClock\":\"3.6GHz\",\"boostClock\":\"4.3GHz\",\"socket\":\"LGA1151\",\"integratedGraphics\":\"Intel UHD Graphics 630\"}"
},
{
"name": "英特尔酷睿14代15代i5i7i9U5U7U9 14490F/14700KF国行盒装台式机盒装CPU 14代i9-14900KF 盒装 24核32线程",
"brand": "英特尔",
"model": "i9-14900KF",
"price": 2876,
"description": "国行盒装",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/103503/21/47232/93933/65fbe567F20663dc6/7c848967805e43ad.jpg",
"stock": 27,
"typeName": "CPU",
"specifications": "{\"cores\":24,\"threads\":32,\"socket\":\"LGA1700\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔(Intel) i5-12400F 酷睿12代 CPU处理器",
"brand": "英特尔(Intel)",
"model": "i5-12400F",
"price": 839,
"description": "6核12线程,18MB三级缓存,睿频至高可达4.40GHz,一芯多用,状态稳定,办公休闲两不误!",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/307531/24/11117/79585/6855240dF45f7e32e/7f8588b5574f26b5.png",
"stock": 27,
"typeName": "CPU",
"specifications": "{\"cores\":6,\"threads\":12,\"boostClock\":\"4.4GHz\",\"cache\":\"18MB三级缓存\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔i5 i7酷睿12/13/14代CPU处理器 12700KF 14600KF全新盒装 散片 I5 12400F 全新散片【三年店保】",
"brand": "英特尔",
"model": "I5 12400F",
"price": 649,
"description": "全新散片,三年店保",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/294353/12/14374/147106/6853cdd6F95d7988b/d47d31d355290395.png",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"cores\":6,\"threads\":12,\"baseClock\":\"2.5GHz\",\"boostClock\":\"4.4GHz\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel)4代酷睿i7/i5/i3 i7-4790K 4770 i5-4460 i5-4590K i3-4170(盒) i3-4170(盒)",
"brand": "英特尔(Intel)",
"model": "i3-4170(盒)",
"price": 479,
"description": "只卖正品/零售批发",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/266237/7/18044/78932/67a870ecFa5173024/6960e969231c37f2.jpg",
"stock": 15,
"typeName": "CPU",
"specifications": "{\"cores\":2,\"threads\":4,\"baseClock\":\"3.7GHz\",\"socket\":\"LGA1150\",\"integratedGraphics\":\"Intel HD Graphics 4400\"}"
},
{
"name": "英特尔(Intel) i5-12600KF 酷睿12代 处理器",
"brand": "英特尔(Intel)",
"model": "i5-12600KF",
"price": 1099,
"description": "10核16线程,20MB三级缓存,睿频至高可达4.90GHz,增强音频功能,玩游戏做直播尽享清晰语音!",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/315514/27/10468/100500/6855240aFe64abfe7/f7f029ca4b2f1626.png",
"stock": 26,
"typeName": "CPU",
"specifications": "{\"cores\":10,\"threads\":16,\"boostClock\":\"4.9GHz\",\"cache\":\"20MB三级缓存\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔(Intel)14代酷睿CPU处理器 台式机处理器 盒装CPU i5-14600KF 盒装【五年质保】",
"brand": "英特尔(Intel)",
"model": "i5-14600KF",
"price": 1379,
"description": "英特尔酷睿Ultra台式机处理器(第二代)解锁AI新竞界。功耗更优化,多线程性能再提升,解锁AI+游戏新体验,超频体验再升级!爆款晒单返京豆,详情请咨询客服。",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/295747/38/5632/122106/68552e0fF35ed4b9b/67d1fb7937ee6bd1.png",
"stock": 15,
"typeName": "CPU",
"specifications": "{\"cores\":14,\"threads\":20,\"baseClock\":\"3.5GHz (P-core); 2.6GHz (E-core)\",\"boostClock\":\"5.3GHz (P-core); 4.0GHz (E-core)\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔(Intel)14代酷睿CPU处理器 台式机处理器 盒装CPU i7-14700KF 盒装【五年质保】",
"brand": "英特尔(Intel)",
"model": "i7-14700KF",
"price": 2359,
"description": "英特尔酷睿Ultra台式机处理器(第二代)解锁AI新竞界。功耗更优化,多线程性能再提升,解锁AI+游戏新体验,超频体验再升级!爆款晒单返京豆,详情请咨询客服。",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/297995/15/17373/126874/68552e16Ff0984355/c28425df8a4a8f3c.png",
"stock": 27,
"typeName": "CPU",
"specifications": "{\"cores\":20,\"threads\":28,\"baseClock\":\"3.4GHz (P-core); 2.5GHz (E-core)\",\"boostClock\":\"5.5GHz (P-core); 4.3GHz (E-core)\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔(Intel)酷睿6代i7-6700K/I7-6850K/i7-6900K/6950X/i5-6600K/6500/6400 i5-6600(盒)",
"brand": "英特尔(Intel)",
"model": "i5-6600(盒)",
"price": 729,
"description": "只卖正品/零售批发",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/275667/19/1057/27491/67cfee3bF563d89f8/cae1d68670cca41d.jpg",
"stock": 27,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":4,\"baseClock\":\"3.3GHz\",\"boostClock\":\"3.9GHz\",\"socket\":\"LGA1151\",\"integratedGraphics\":\"Intel HD Graphics 530\"}"
},
{
"name": "英特尔(Intel)13代酷睿CPU处理器 台式机处理器 盒装CPU 13代i5-13400F 盒装【10核16线程】",
"brand": "英特尔(Intel)",
"model": "i5-13400F",
"price": 959,
"description": "英特尔酷睿Ultra台式机处理器(第二代)解锁AI新竞界。功耗更优化,多线程性能再提升,解锁AI+游戏新体验,超频体验再升级!爆款晒单返京豆,详情请咨询客服。",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/313697/17/11151/111685/68552e15F9e572c37/a1bba0a043ce77dc.png",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"cores\":10,\"threads\":16,\"baseClock\":\"2.5GHz (P-core); 1.8GHz (E-core)\",\"boostClock\":\"4.6GHz (P-core); 3.3GHz (E-core)\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel)E5至强处理器服务器CPU2666V3 2676 2678 2680 E31231V2拆机散片 E5-2660v3【10核心20线程】",
"brand": "英特尔(Intel)",
"model": "E5-2660v3",
"price": 39.9,
"description": "拆机散片",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/256943/7/26630/23893/67c3c8baFebbeceef/04c9e3f71b8771e4.jpg",
"stock": 10,
"typeName": "CPU",
"specifications": "{\"cores\":10,\"threads\":20,\"baseClock\":\"2.6GHz\",\"boostClock\":\"3.3GHz\",\"socket\":\"LGA2011-3\"}"
},
{
"name": "英特尔(Intel) i5-13400F 酷睿13代 处理器",
"brand": "英特尔(Intel)",
"model": "i5-13400F",
"price": 989,
"description": "13代酷睿i5,混合架构,600/700主板平台兼容,DDR4/5内存支持,游戏办公轻松胜任!",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/300763/8/16476/108482/68552408F08dbbdd2/c6dc9652210f76a0.png",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"cores\":10,\"threads\":16,\"boostClock\":\"4.6GHz\",\"cache\":\"20M三级缓存\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel)酷睿10代i9/i7/i5/i3 10700KF 10400F 10500 10600KF 10900K i3-10105F CPU",
"brand": "英特尔(Intel)",
"model": "i3-10105F",
"price": 499,
"description": "只卖正品/零售批发",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/261058/13/19674/58212/67af2fa6F67819b9e/f833fe86e5abcae2.jpg",
"stock": 30,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":8,\"baseClock\":\"3.7GHz\",\"boostClock\":\"4.4GHz\",\"socket\":\"LGA1200\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel)12代酷睿CPU处理器 台式机处理器 盒装CPU 12代i3-12100F 盒装【4核8线程】",
"brand": "英特尔(Intel)",
"model": "i3-12100F",
"price": 499,
"description": "英特尔酷睿Ultra台式机处理器(第二代)解锁AI新竞界。功耗更优化,多线程性能再提升,解锁AI+游戏新体验,超频体验再升级!爆款晒单返京豆,详情请咨询客服。",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/313144/3/11045/111968/68552e1dF701da797/737c9a759e7e7ad2.png",
"stock": 10,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":8,\"baseClock\":\"3.3GHz\",\"boostClock\":\"4.3GHz\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel) i9-14900KF 酷睿14代",
"brand": "英特尔(Intel)",
"model": "i9-14900KF",
"price": 3199,
"description": "酷睿14代i9,单核超高睿频,AI超频,192GB至高内存容量,内存超频至高可达8000+,可兼容600/700主板,更多超频选择,实现硬核突破!",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/287796/3/15834/130037/68526e79Fb9e1d5a6/b0272c2a8fad398d.png",
"stock": 14,
"typeName": "CPU",
"specifications": "{\"cores\":24,\"threads\":32,\"socket\":\"LGA1700\",\"memoryCapacity\":\"192GB\",\"memorySpeed\":\"8000+ MHz (超频)\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔(Intel)13代酷睿CPU处理器 台式机处理器 盒装CPU 13代i5-13490F 盒装【10核16线程】",
"brand": "英特尔(Intel)",
"model": "i5-13490F",
"price": 849,
"description": "英特尔酷睿Ultra台式机处理器(第二代)解锁AI新竞界。功耗更优化,多线程性能再提升,解锁AI+游戏新体验,超频体验再升级!爆款晒单返京豆,详情请咨询客服。",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/299800/12/13041/126430/68552e1aF66dca4c8/904ac6440a4294ac.png",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"cores\":10,\"threads\":16,\"socket\":\"LGA1700\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel)酷睿i9/i5/i7 11代 11400 11500 11600K i7 11700 11700K 11900K i7-11700",
"brand": "英特尔(Intel)",
"model": "i7-11700",
"price": 1762.02,
"description": "只卖正品/零售批发",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/263802/6/21325/22656/67b42d87Fb0353130/a55c8c1c53922b24.jpg",
"stock": 14,
"typeName": "CPU",
"specifications": "{\"cores\":8,\"threads\":16,\"baseClock\":\"2.5GHz\",\"boostClock\":\"4.9GHz\",\"socket\":\"LGA1200\",\"integratedGraphics\":\"Intel UHD Graphics 750\"}"
},
{
"name": "英特尔(INTEL)i5-14600kf/13400F/12600kf/12400f/10400f/9400f散片cpu处理器 14代酷睿 i5-14600KF",
"brand": "英特尔(INTEL)",
"model": "i5-14600KF",
"price": 1159,
"description": "散片",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/319467/17/10319/96779/6853c133F644568e2/c4786b7940291f3d.jpg",
"stock": 31,
"typeName": "CPU",
"specifications": "{\"cores\":14,\"threads\":20,\"baseClock\":\"3.5GHz (P-core); 2.6GHz (E-core)\",\"boostClock\":\"5.3GHz (P-core); 4.0GHz (E-core)\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔(Intel) i5-14600K 酷睿14代",
"brand": "英特尔(Intel)",
"model": "i5-14600K",
"price": 1799,
"description": "酷睿14代i5,可超频,英特尔770核显,192GB至高内存容量,兼容600/700主板芯片组,职场性能主打!",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/297594/37/16091/119216/68552405F245f963c/111ec63fe98259f6.png",
"stock": 27,
"typeName": "CPU",
"specifications": "{\"cores\":14,\"threads\":20,\"baseClock\":\"3.5GHz (P-core); 2.6GHz (E-core)\",\"boostClock\":\"5.3GHz (P-core); 4.0GHz (E-core)\",\"socket\":\"LGA1700\",\"integratedGraphics\":\"Intel UHD Graphics 770\"}"
},
{
"name": "英特尔(Intel) i3-12100F 酷睿12代 处理器",
"brand": "英特尔(Intel)",
"model": "i3-12100F",
"price": 549,
"description": "4核8线程,12MB三级缓存,睿频至高可达4.3Ghz,性能强劲,满足日常办公以及游戏娱乐需求!",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/58120/26/19560/120809/62a2fdd8E47da21f2/7691c878321b0369.jpg",
"stock": 30,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":8,\"boostClock\":\"4.3GHz\",\"cache\":\"12MB三级缓存\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "INTEL 英特尔 中央处理器 BX80684I59400F 2.9 GHz LGA 1151插槽",
"brand": "INTEL 英特尔",
"model": "i5-9400F",
"price": 1351.99,
"description": "2.9 GHz LGA 1151插槽",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/166443/24/51160/117809/672c0007F89b2a040/65b43a5f90816d80.jpg",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"cores\":6,\"threads\":6,\"baseClock\":\"2.9GHz\",\"boostClock\":\"4.1GHz\",\"socket\":\"LGA1151\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔i5 i7酷睿12/13/14代CPU处理器 12700KF 14600KF全新盒装 散片 I5 14600KF散片",
"brand": "英特尔",
"model": "I5 14600KF散片",
"price": 1219,
"description": "散片",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/302925/10/15997/147999/6853cdf2Fbc059b03/e0d2ebe95ac18fbf.png",
"stock": 15,
"typeName": "CPU",
"specifications": "{\"cores\":14,\"threads\":20,\"baseClock\":\"3.5GHz (P-core); 2.6GHz (E-core)\",\"boostClock\":\"5.3GHz (P-core); 4.0GHz (E-core)\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔(Intel) i5-12490F 酷睿12代 处理器",
"brand": "英特尔(Intel)",
"model": "i5-12490F",
"price": 749,
"description": "6核12线程,20MB三级缓存,睿频至高可达4.6Ghz,胜任主流游戏,稳定输出,畅享胜果!",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/298198/21/17457/110316/6855240cFe4ee7ea1/c7096321d44ee437.png",
"stock": 15,
"typeName": "CPU",
"specifications": "{\"cores\":6,\"threads\":12,\"boostClock\":\"4.6GHz\",\"cache\":\"20MB三级缓存\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel) 67 89代 酷睿 i3 i5 i7 i9 全系列处理器 CPU 店保一年 i3 8100散片",
"brand": "英特尔(Intel)",
"model": "i3-8100",
"price": 123,
"description": "店保一年,散片",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/3658/9/24037/91623/66dd8b9cF80cc77f8/d1d005da1dc3db72.jpg",
"stock": 15,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":4,\"baseClock\":\"3.6GHz\",\"socket\":\"LGA1151\",\"integratedGraphics\":\"Intel UHD Graphics 630\"}"
},
{
"name": "英特尔二手服务器CPU 6330,质保一年",
"brand": "英特尔",
"model": "至强金牌 6330",
"price": 2900,
"description": "二手服务器CPU,质保一年",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/283542/1/23248/179089/68060d90F77dae3af/156b984be9c8dc41.jpg",
"stock": 10,
"typeName": "CPU",
"specifications": "{\"cores\":28,\"threads\":56,\"baseClock\":\"2.0GHz\",\"boostClock\":\"3.1GHz\",\"socket\":\"LGA4189\"}"
},
{
"name": "英特尔(Intel) i5-10400F 10代 酷睿 处理器",
"brand": "英特尔(Intel)",
"model": "i5-10400F",
"price": 589,
"description": "6核12线程,12MB三级缓存,睿频至高可达4.30GHz,英特尔睿频加速技术2.0,高速内核负责关键工作负载,提高单线程性能!",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/308541/25/10799/111528/6855240eF9889db05/b76924336c0e1de1.png",
"stock": 14,
"typeName": "CPU",
"specifications": "{\"cores\":6,\"threads\":12,\"boostClock\":\"4.3GHz\",\"cache\":\"12MB三级缓存\",\"socket\":\"LGA1200\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel)酷睿 Ultra 7 265KF 台式机处理器",
"brand": "英特尔(Intel)",
"model": "酷睿 Ultra 7 265KF",
"price": 2294.4,
"description": "酷睿Ultra7,睿频至高5.5GHz,192GB至高内存容量,全新3D混合架构,智能功耗管理!兼容810/860/890芯片组,电竞创作两不误!",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/304788/10/12222/82767/68552414F978f56c7/56e9bce44cf0c2bb.png",
"stock": 27,
"typeName": "CPU",
"specifications": "{\"cores\":20,\"threads\":20,\"boostClock\":\"5.5GHz\",\"socket\":\"LGA1851 (推测)\",\"architecture\":\"全新3D混合架构\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔(Intel)4代酷睿 i3 i5 i7处理器1150针e3 1231v3 4590 4770 4790K散片cpu i7 4790【拆机散片】",
"brand": "英特尔(Intel)",
"model": "i7-4790",
"price": 229,
"description": "拆机散片",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/260378/30/26289/132960/67c29c2dF39a8afef/c12adf548438aaf2.jpg",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":8,\"baseClock\":\"3.6GHz\",\"boostClock\":\"4.0GHz\",\"socket\":\"LGA1150\",\"integratedGraphics\":\"Intel HD Graphics 4600\"}"
},
{
"name": "intel4代酷睿/4790盒装/4590/4690四核LGA1150针四代CPU K盒(95成色) i5-4590",
"brand": "Intel",
"model": "i5-4590",
"price": 311.27,
"description": "95成色",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/281294/21/20901/67206/67ffa298Fd224e88c/0aecf2d7447b9ef0.jpg",
"stock": 26,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":4,\"baseClock\":\"3.3GHz\",\"boostClock\":\"3.7GHz\",\"socket\":\"LGA1150\",\"integratedGraphics\":\"Intel HD Graphics 4600\"}"
},
{
"name": "英特尔(Intel) i5-12400 12代 酷睿 处理器",
"brand": "英特尔(Intel)",
"model": "i5-12400",
"price": 1096.8,
"description": "6核12线程,18MB三级缓存,睿频至高可达4.40GHz,支持同时连接多设备,高效输出!",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/111559/1/28543/32367/63071912Edd79ce3d/1083d989aa54e908.jpg",
"stock": 14,
"typeName": "CPU",
"specifications": "{\"cores\":6,\"threads\":12,\"boostClock\":\"4.4GHz\",\"cache\":\"18MB三级缓存\",\"socket\":\"LGA1700\",\"integratedGraphics\":\"Intel UHD Graphics 730\"}"
},
{
"name": "英特尔14代酷睿i5i7i9 14490F/14700KF/14900KS 国行全新盒装台式机CPU 14代i5-14600KF 盒装 14核20线程",
"brand": "英特尔",
"model": "i5-14600KF",
"price": 1249,
"description": "国行全新盒装",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/236950/4/22802/152261/668e4975F5d6d052f/0bc7f1d4fd52ae94.jpg",
"stock": 14,
"typeName": "CPU",
"specifications": "{\"cores\":14,\"threads\":20,\"baseClock\":\"3.5GHz (P-core); 2.6GHz (E-core)\",\"boostClock\":\"5.3GHz (P-core); 4.0GHz (E-core)\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔Intel12/13/14代CPU处理器 i5 12400f 13400f 14600kf 盒装 散片 12代 i3-12100F散片",
"brand": "英特尔Intel",
"model": "i3-12100F",
"price": 309,
"description": "散片",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/260992/33/29357/56561/67ca92eeF744f12f7/80c3c2da06443655.png",
"stock": 30,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":8,\"baseClock\":\"3.3GHz\",\"boostClock\":\"4.3GHz\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔服务器工作站多核心线程中央处理器CPU 至强金牌6430 32核心 2.1GHz",
"brand": "英特尔",
"model": "至强金牌 6430",
"price": 9800,
"description": "服务器工作站多核心线程",
"imageUrl": "",
"stock": 30,
"typeName": "CPU",
"specifications": "{\"cores\":32,\"threads\":64,\"baseClock\":\"2.1GHz\",\"socket\":\"LGA4677\"}"
},
{
"name": "英特尔(Intel)酷睿9/8/7/6代CPU处理器i3 i5 i7 i9系列9400F 9700KF拆机散片 i5 9400F【散片+赠硅脂】",
"brand": "英特尔(Intel)",
"model": "i5-9400F",
"price": 283,
"description": "拆机散片,赠硅脂",
"imageUrl": "",
"stock": 26,
"typeName": "CPU",
"specifications": "{\"cores\":6,\"threads\":6,\"baseClock\":\"2.9GHz\",\"boostClock\":\"4.1GHz\",\"socket\":\"LGA1151\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel)4代酷睿 i3 i5 i7处理器1150针e3 1231v3 4590 4770 4790K散片cpu E3-1230 V3【拆机散片】",
"brand": "英特尔(Intel)",
"model": "E3-1230 V3",
"price": 88,
"description": "拆机散片",
"imageUrl": "",
"stock": 14,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":8,\"baseClock\":\"3.3GHz\",\"boostClock\":\"3.7GHz\",\"socket\":\"LGA1150\",\"graphics\":\"无核显\"}"
},
{
"name": "英特尔(Intel)至强xeno处理器E3 1220 1231 V3 1240 1270 1285 V3 双核/四核CPU E3-1220 V5(1151针)",
"brand": "英特尔(Intel)",
"model": "E3-1220 V5",
"price": 30,
"description": "1151针",
"imageUrl": "",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":4,\"baseClock\":\"3.0GHz\",\"boostClock\":\"3.5GHz\",\"socket\":\"LGA1151\"}"
},
{
"name": "英特尔(Intel)十四代处理器14代i5 盒装华硕主板CPU板U套装 华硕ROG B760-G WIFI D4 小吹雪 14代i5-14600KF 14核20线程 无核显",
"brand": "英特尔(Intel)",
"model": "i5-14600KF",
"price": 2513,
"description": "盒装,无核显,搭配华硕ROG B760-G WIFI D4主板",
"imageUrl": "",
"stock": 11,
"typeName": "CPU套装",
"specifications": "{\"cores\":14,\"threads\":20,\"baseClock\":\"3.5GHz (P-core); 2.6GHz (E-core)\",\"boostClock\":\"5.3GHz (P-core); 4.0GHz (E-core)\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (KF系列)\",\"motherboard\":\"华硕ROG B760-G WIFI D4\"}"
},
{
"name": "英特尔(INTEL)i5-14600kf/13400F/12600kf/12400f/10400f/9400f散片cpu处理器 9代酷睿 i5-9400F",
"brand": "英特尔(INTEL)",
"model": "i5-9400F",
"price": 269,
"description": "散片",
"imageUrl": "",
"stock": 27,
"typeName": "CPU",
"specifications": "{\"cores\":6,\"threads\":6,\"baseClock\":\"2.9GHz\",\"boostClock\":\"4.1GHz\",\"socket\":\"LGA1151\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel)酷睿9/8/7/6代CPU处理器i3 i5 i7 i9系列9400F 9700KF拆机散片 i5 9600KF【散片+赠硅脂】",
"brand": "英特尔(Intel)",
"model": "i5-9600KF",
"price": 289,
"description": "拆机散片,赠硅脂",
"imageUrl": "",
"stock": 26,
"typeName": "CPU",
"specifications": "{\"cores\":6,\"threads\":6,\"baseClock\":\"3.7GHz\",\"boostClock\":\"4.6GHz\",\"socket\":\"LGA1151\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔(Intel)i5-14490F 酷睿14代 处理器",
"brand": "英特尔(Intel)",
"model": "i5-14490F",
"price": 1096.8,
"description": "酷睿14代i5,192GB至高内存容量,10核16线程,硬核动力,效率翻倍!",
"imageUrl": "",
"stock": 27,
"typeName": "CPU",
"specifications": "{\"cores\":10,\"threads\":16,\"boostClock\":\"4.9GHz\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel) 十四代处理器I9 14900KF/14900K盒装华硕主板CPU板U套装 华硕 华硕ROG B760-G WIFI D5 小吹雪S 14代i9-14900K 24核32线程 带核",
"brand": "英特尔(Intel)",
"model": "i9-14900K",
"price": 4509,
"description": "盒装,带核显,搭配华硕ROG B760-G WIFI D5主板",
"imageUrl": "",
"stock": 26,
"typeName": "CPU套装",
"specifications": "{\"cores\":24,\"threads\":32,\"socket\":\"LGA1700\",\"integratedGraphics\":\"Intel UHD Graphics 770\",\"motherboard\":\"华硕ROG B760-G WIFI D5\"}"
},
{
"name": "英特尔(Intel)酷睿 Ultra 7 265F 台式机处理器",
"brand": "英特尔(Intel)",
"model": "酷睿 Ultra 7 265F",
"price": 1995,
"description": "酷睿Ultra7,30MB智能缓存,全新3D混合架构,原装风扇支持,智能功耗管理!兼容810/860/890芯片组,电竞创作两不误!",
"imageUrl": "",
"stock": 26,
"typeName": "CPU",
"specifications": "{\"cores\":20,\"threads\":20,\"boostClock\":\"5.3GHz\",\"cache\":\"30MB智能缓存\",\"socket\":\"LGA1851 (推测)\",\"architecture\":\"全新3D混合架构\",\"graphics\":\"无核显 (F系列)\"}"
},
{
"name": "英特尔(Intel)7代酷睿i3 i5 i7 i9系列处理器7300 7500 7600 7700K拆机散片cpu i3 7100 散片",
"brand": "英特尔(Intel)",
"model": "i3-7100",
"price": 70,
"description": "拆机散片",
"imageUrl": "",
"stock": 10,
"typeName": "CPU",
"specifications": "{\"cores\":2,\"threads\":4,\"baseClock\":\"3.9GHz\",\"socket\":\"LGA1151\",\"integratedGraphics\":\"Intel HD Graphics 630\"}"
},
{
"name": "英特尔i5 i7酷睿12/13/14代CPU处理器 12700KF 14600KF全新盒装 散片 I5 12600KF全新散片(三年店保)",
"brand": "英特尔",
"model": "I5 12600KF",
"price": 975,
"description": "全新散片,三年店保",
"imageUrl": "",
"stock": 14,
"typeName": "CPU",
"specifications": "{\"cores\":10,\"threads\":16,\"baseClock\":\"3.7GHz (P-core); 2.8GHz (E-core)\",\"boostClock\":\"4.9GHz (P-core); 3.6GHz (E-core)\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (KF系列)\"}"
},
{
"name": "英特尔(Intel)PRO H610M-S DDR4台式电脑家用办公学习M-ATX主板支持CPU LGA1700",
"brand": "英特尔(Intel)",
"model": "PRO H610M-S DDR4",
"price": 605.6,
"description": "台式电脑家用办公学习M-ATX主板,支持CPU LGA1700",
"imageUrl": "",
"stock": 14,
"typeName": "主板",
"specifications": "{\"formFactor\":\"M-ATX\",\"memoryType\":\"DDR4\",\"socket\":\"LGA1700\",\"chipset\":\"H610\"}"
},
{
"name": "英特尔(Intel)酷睿 Ultra 5 225 台式机处理器",
"brand": "英特尔(Intel)",
"model": "酷睿 Ultra 5 225",
"price": 1496,
"description": "酷睿Ultra5,20MB智能缓存,全新3D混合架构,原装风扇支持,核显支持,兼容810/860/890芯片组!",
"imageUrl": "",
"stock": 31,
"typeName": "CPU",
"specifications": "{\"cores\":10,\"threads\":10,\"boostClock\":\"4.9GHz\",\"cache\":\"20MB智能缓存\",\"socket\":\"LGA1851 (推测)\",\"architecture\":\"全新3D混合架构\",\"integratedGraphics\":\"支持\"}"
},
{
"name": "英特尔(Intel)4代酷睿 i3 i5 i7处理器1150针e3 1231v3 4590 4770 4790K散片cpu i7 4790K【拆机散片】",
"brand": "英特尔(Intel)",
"model": "i7-4790K",
"price": 292,
"description": "拆机散片",
"imageUrl": "",
"stock": 26,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":8,\"baseClock\":\"4.0GHz\",\"boostClock\":\"4.4GHz\",\"socket\":\"LGA1150\",\"integratedGraphics\":\"Intel HD Graphics 4600\"}"
},
{
"name": "英特尔(Intel)CPU处理器i3 14100f 13100f 12100f 散片 盒装i313100 i312100 i3-12100F散片(无盒 店保3年) LGA1700针脚",
"brand": "英特尔(Intel)",
"model": "i3-12100F",
"price": 299,
"description": "散片,无盒,店保3年,LGA1700针脚",
"imageUrl": "",
"stock": 14,
"typeName": "CPU",
"specifications": "{\"cores\":4,\"threads\":8,\"baseClock\":\"3.3GHz\",\"boostClock\":\"4.3GHz\",\"socket\":\"LGA1700\",\"graphics\":\"无核显 (F系列)\"}"
}
]
+332
View File
@@ -0,0 +1,332 @@
[
{
"name": "AMD 锐龙7 5700X处理器",
"brand": "AMD",
"model": "锐龙7 5700X",
"price": 797.4,
"description": "8核16线程,加速频率至高4.6GHz,65W AM4接口,36MB游戏高速缓存。",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/310223/10/10985/56468/685507f0Fd4b53c55/41d0255fb243b769.jpg",
"stock": 15,
"typeName": "CPU",
"specifications": "{\"cores\":8,\"threads\":16,\"boostClock\":\"4.6GHz\",\"tdp\":\"65W\",\"socket\":\"AM4\",\"cache\":\"36MB\"}"
},
{
"name": "AMD 锐龙5 5500 处理器",
"brand": "AMD",
"model": "锐龙5 5500",
"price": 458.08,
"description": "7nm,6核12线程,加速频率至高4.2GHz,65W AM4接口,19MB游戏高速缓存。",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/317136/38/10469/55907/685507e7Fa214d175/f37bf7ce1f3f224c.jpg",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"architecture\":\"7nm\",\"cores\":6,\"threads\":12,\"boostClock\":\"4.2GHz\",\"tdp\":\"65W\",\"socket\":\"AM4\",\"cache\":\"19MB\"}"
},
{
"name": "AMD 锐龙R5 5600 CPU处理器",
"brand": "AMD",
"model": "锐龙R5 5600",
"price": 579,
"description": "AMD 锐龙R5 5600 CPU处理器。",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/280337/37/23403/52866/68085b82F2b804d49/28068d42b7e75e43.jpg",
"stock": 43,
"typeName": "CPU",
"specifications": "{\"cores\":6,\"threads\":12,\"boostClock\":\"4.4GHz\",\"socket\":\"AM4\",\"cache\":\"35MB\"}"
},
{
"name": "AMD 锐龙7 9800X3D游戏处理器",
"brand": "AMD",
"model": "锐龙7 9800X3D",
"price": 3891.2,
"description": "8核16线程,104MB游戏缓存,加速频率至高5.2GHz,TDP 120W。",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/298224/39/17213/45200/68550866F1b470153/31f478ea36ba66c1.jpg",
"stock": 46,
"typeName": "CPU",
"specifications": "{\"cores\":8,\"threads\":16,\"cache\":\"104MB\",\"boostClock\":\"5.2GHz\",\"tdp\":\"120W\"}"
},
{
"name": "AMD 锐龙R5 9600X CPU",
"brand": "AMD",
"model": "锐龙R5 9600X",
"price": 1349,
"description": "AMD 锐龙R5 9600X CPU。",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/278320/20/25464/45057/68086198F098a50c1/448a819aad8f7310.jpg",
"stock": 10,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM5\"}"
},
{
"name": "AMD锐龙9 9950X3D处理器",
"brand": "AMD",
"model": "锐龙9 9950X3D",
"price": 5288.4,
"description": "16核32线程,144MB缓存,加速频率至高5.7GHz。",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/301221/34/15138/42237/6855086eF629e9eea/c01c8e52fb4d89e2.jpg",
"stock": 58,
"typeName": "CPU",
"specifications": "{\"cores\":16,\"threads\":32,\"cache\":\"144MB\",\"boostClock\":\"5.7GHz\"}"
},
{
"name": "AMD锐龙5 5600处理器",
"brand": "AMD",
"model": "锐龙5 5600",
"price": 627.74,
"description": "7nm,6核12线程,加速频率至高4.4GHz,AM4盒装CPU,35MB游戏高速缓存。",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/321122/15/10646/55580/685507fbFa8d55853/9645509d1f3483b9.jpg",
"stock": 26,
"typeName": "CPU",
"specifications": "{\"architecture\":\"7nm\",\"cores\":6,\"threads\":12,\"boostClock\":\"4.4GHz\",\"socket\":\"AM4\",\"cache\":\"35MB\"}"
},
{
"name": "AMD 锐龙7 7800X3D 散片CPU",
"brand": "AMD",
"model": "锐龙7 7800X3D",
"price": 2319,
"description": "5nm CPU处理器,R7 7800X3D 散片。",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/243065/40/4314/71309/65bb4815F252349a2/af010b9c89333ade.jpg",
"stock": 31,
"typeName": "CPU",
"specifications": "{\"architecture\":\"5nm\",\"socket\":\"AM5\"}"
},
{
"name": "AMD 锐龙R5 7500F CPU",
"brand": "AMD",
"model": "锐龙R5 7500F",
"price": 899,
"description": "AMD 锐龙R5 7500F CPU。",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/281428/28/25695/34533/68086491Fbf29e922/fbc187866354e26d.jpg",
"stock": 27,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM5\"}"
},
{
"name": "AMD 锐龙5 7500F处理器",
"brand": "AMD",
"model": "锐龙5 7500F",
"price": 997,
"description": "5nm,6核12线程,加速频率至高5GHz,AM5盒装CPU,自带风扇。",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/313586/19/11007/66884/6855081bF33831959/711c362d92ef497b.jpg",
"stock": 46,
"typeName": "CPU",
"specifications": "{\"architecture\":\"5nm\",\"cores\":6,\"threads\":12,\"boostClock\":\"5GHz\",\"socket\":\"AM5\"}"
},
{
"name": "AMD 锐龙R9 9950X3D盒装CPU",
"brand": "AMD",
"model": "锐龙R9 9950X3D",
"price": 5299,
"description": "AMD 锐龙 9代处理器,AM5接口,盒装。",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/276863/10/1862/49949/67d15213Fe7355a7b/398b00edb2ff64fd.jpg",
"stock": 30,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM5\"}"
},
{
"name": "AMD 锐龙R9 9950X3D散片CPU",
"brand": "AMD",
"model": "锐龙R9 9950X3D",
"price": 4799,
"description": "AMD 锐龙 9代处理器,AM5接口,散片。",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/276863/10/1862/49949/67d15213Fe7355a7b/398b00edb2ff64fd.jpg",
"stock": 47,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM5\"}"
},
{
"name": "AMD Threadripper PRO 7995WX",
"brand": "AMD",
"model": "Threadripper PRO 7995WX",
"price": 78899,
"description": "Threadripper线程撕裂者PRO盒装CPU,工作站台式机处理器sTR5 AI。",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/230736/2/38435/43912/67d38dbfF4a2ad259/4a547f79c1e7cb8b.jpg",
"stock": 15,
"typeName": "CPU",
"specifications": "{\"socket\":\"sTR5\"}"
},
{
"name": "AMD锐龙 R7 9800X3D散片",
"brand": "AMD",
"model": "锐龙R7 9800X3D",
"price": 3349,
"description": "AMD锐龙 9000系列,AM5接口CPU处理器,散片。",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/183665/36/55076/65760/6758f8e6F249081c4/61e1f88f24c76e08.jpg",
"stock": 31,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM5\"}"
},
{
"name": "AMD 锐龙R5 5600X 散片CPU",
"brand": "AMD",
"model": "锐龙R5 5600X",
"price": 599,
"description": "AMD 锐龙 CPU 台式机处理器,R5 5600X 散片。",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/91655/7/46255/72546/65386d2bF379748bc/b8e2bffcace0f9b3.jpg",
"stock": 30,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM4\"}"
},
{
"name": "AMD 锐龙R7 5700X 散片CPU",
"brand": "AMD",
"model": "锐龙R7 5700X",
"price": 749,
"description": "AMD 锐龙 CPU 台式机处理器,R7 5700X 散片。",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/193184/40/40489/70107/65386d2aF00588cc1/80258e4bbd8911f4.jpg",
"stock": 42,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM4\"}"
},
{
"name": "AMD 锐龙R5 7500F(散片)CPU套装",
"brand": "AMD",
"model": "锐龙R5 7500F",
"price": 1839,
"description": "AMD七代锐龙CPU处理器,搭华硕TUF GAMING B650M-E WIFI主板CPU套装。",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/178546/17/40673/113479/653b6944F30fd3073/8256ae616fa63469.jpg",
"stock": 43,
"typeName": "CPU套装",
"specifications": "{\"cpuModel\":\"R5 7500F\",\"motherboardModel\":\"华硕TUF GAMING B650M-E WIFI\",\"socket\":\"AM5\"}"
},
{
"name": "AMD 锐龙R7 9700X散片",
"brand": "AMD",
"model": "锐龙R7 9700X",
"price": 1879,
"description": "AMD 锐龙 9代处理器,AM5接口,散片。",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/233879/26/25160/54945/6711c7a9Feee14f82/91fd72e95c7cdc68.jpg",
"stock": 15,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM5\"}"
},
{
"name": "AMD锐龙7 7800X3D游戏处理器",
"brand": "AMD",
"model": "锐龙7 7800X3D",
"price": 2693.6,
"description": "8核16线程,104MB游戏缓存,加速频率至高5.0GHz,盒装CPU。",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/292409/29/14415/49255/68550813Fe9d960ba/3e9968bb3a8b94fa.jpg",
"stock": 42,
"typeName": "CPU",
"specifications": "{\"cores\":8,\"threads\":16,\"cache\":\"104MB\",\"boostClock\":\"5.0GHz\",\"socket\":\"AM5\"}"
},
{
"name": "AMD锐龙 R7 9700X散片",
"brand": "AMD",
"model": "锐龙R7 9700X",
"price": 1879,
"description": "AMD锐龙 9000系列,AM5接口CPU处理器,散片。",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/87538/31/47649/66232/66b6d555F22e05e91/17c32968a7142bc8.jpg",
"stock": 26,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM5\"}"
},
{
"name": "AMD B650M 主板",
"brand": "AMD",
"model": "CVN B650M D5 WIFI",
"price": 899,
"description": "AMDB650M WiFi主板,支持AMD锐龙7000/8000/9000系列CPU。",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/304781/37/12415/179214/685617f3Fa85a9603/a355bb0ee42a404d.jpg",
"stock": 42,
"typeName": "主板",
"specifications": "{\"chipset\":\"B650M\",\"socket\":\"AM5\",\"features\":\"WIFI, DDR5\"}"
},
{
"name": "AMD 锐龙R5 5600散片b2",
"brand": "AMD",
"model": "锐龙R5 5600",
"price": 559,
"description": "AMDamdcpu五代锐龙散片CPU,质保三年。",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/290706/16/11030/68564/683d5208F9b4d948d/989d3d4527ca8613.jpg",
"stock": 26,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM4\"}"
},
{
"name": "AMD 锐龙R5 7500F散片",
"brand": "AMD",
"model": "锐龙R5 7500F",
"price": 899,
"description": "AMD 锐龙 7代处理器,AM5接口,散片。",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/163404/17/49900/53058/6711ef2cF90a244de/9b37f46783b2890f.jpg",
"stock": 11,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM5\"}"
},
{
"name": "AMD 锐龙R5 7500F 散片CPU",
"brand": "AMD",
"model": "锐龙R5 7500F",
"price": 899,
"description": "AMD 锐龙 CPU 台式机处理器,R5 7500F 散片。",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/192360/22/35577/66381/65bb4817Fcdd5547e/eaa5704157d88fdd.jpg",
"stock": 26,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM5\"}"
},
{
"name": "AMD TUF B850M-PLUS WIFI主板",
"brand": "AMD",
"model": "TUF B850M-PLUS WIFI",
"price": 1319,
"description": "AMD主板 TUF GAMING B850M-PLUS WIFI重炮手主板,支持7800X3D/9800X3D/9600X CPU。",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/289585/28/6233/140005/685617e1Fcb80642f/faeaf6d02c2b8ed9.jpg",
"stock": 31,
"typeName": "主板",
"specifications": "{\"chipset\":\"B850M\",\"socket\":\"AM5\",\"features\":\"WIFI\"}"
},
{
"name": "AMD 锐龙5 7600X处理器",
"brand": "AMD",
"model": "锐龙5 7600X",
"price": 1246.5,
"description": "6核12线程,加速频率至高5.3GHz,105W AM5接口,32MB三级缓存。",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/307552/27/11181/46632/68550803Fd9fe1e75/0a11fa017e2f9c1b.jpg",
"stock": 31,
"typeName": "CPU",
"specifications": "{\"cores\":6,\"threads\":12,\"boostClock\":\"5.3GHz\",\"tdp\":\"105W\",\"socket\":\"AM5\",\"cache\":\"32MB\"}"
},
{
"name": "AMD 锐龙R5 5600(散片)",
"brand": "AMD",
"model": "锐龙R5 5600",
"price": 579,
"description": "AMD 锐龙 CPU,7nm 65W AM4接口处理器,散片。",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/92031/5/52947/56852/67120df2F6b82696a/fe963d87909c3e49.jpg",
"stock": 10,
"typeName": "CPU",
"specifications": "{\"architecture\":\"7nm\",\"tdp\":\"65W\",\"socket\":\"AM4\"}"
},
{
"name": "AMD 锐龙R5 5500盒装",
"brand": "AMD",
"model": "锐龙R5 5500",
"price": 409,
"description": "AMDamdcpu五代锐龙盒装CPU,质保三年。",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/308247/29/6012/68485/683d52deFf04c7baa/01559d74871a373d.jpg",
"stock": 42,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM4\"}"
},
{
"name": "AMD Threadripper 7980X",
"brand": "AMD",
"model": "Threadripper 7980X",
"price": 27999,
"description": "AMD Threadripper线程撕裂者盒装高端工作站台式机CPU处理器,适用于科学计算深度学习。",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/273409/11/2819/58288/67d39c8fF99d81cf7/fed7b0fc4b24b665.jpg",
"stock": 31,
"typeName": "CPU",
"specifications": "{\"socket\":\"sTR5\"}"
},
{
"name": "AMD 锐龙R7 7800X3D散片",
"brand": "AMD",
"model": "锐龙R7 7800X3D",
"price": 2319,
"description": "AMD 锐龙 7代处理器,AM5接口,散片。",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/225111/31/26777/55001/6711f2b0F9d380aa7/4533b21fbf6dd0b7.jpg",
"stock": 15,
"typeName": "CPU",
"specifications": "{\"socket\":\"AM5\"}"
}
]
+662
View File
@@ -0,0 +1,662 @@
[
{
"name": "英伟达(NVIDIA)Tesla T4/A10/A30/A40/L4/L40S显卡人工智能服务器GPU推理显卡 Tesla A2 16G",
"brand": "英伟达(NVIDIA)",
"model": "Tesla A2",
"price": 3189,
"description": "人工智能服务器GPU推理显卡 Tesla A2 16G,不开发票",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/307817/10/11064/84061/685609c9F2f2a23e3/642c5cc95c710a24.png",
"stock": 46,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"Tesla\",\"memory\":\"16GB\",\"memory_type\":\"不详\",\"purpose\":\"AI推理\",\"interface\":\"PCIe\"}"
},
{
"name": "英伟达(NVIDIA)RTX4090系列 英伟达RTX4060 Ti 16G 涡轮版",
"brand": "英伟达(NVIDIA)",
"model": "RTX 4060 Ti",
"price": 3653,
"description": "公版系列原装定制涡轮AI深度学习训练推理大模型专业显卡,内容创作卡,顶级游戏卡,16G显存,京采质保3年",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/284589/20/10886/58185/67e6755dF258de9f7/140c61650c896457.jpg",
"stock": 46,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 40系列\",\"memory\":\"16GB\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习,内容创作,游戏\",\"cooling\":\"涡轮散热\"}"
},
{
"name": "英伟达P1000 P400 2GB",
"brand": "英伟达",
"model": "Quadro P400",
"price": 249,
"description": "盒装P400,2GB显存,适用于图形设计建模渲染,多屏炒股,工业包装",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/279405/11/18624/87867/67f9d276Fc374d51c/5cb67af266a4d9ba.jpg",
"stock": 42,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"Quadro P系列\",\"memory\":\"2GB\",\"memory_type\":\"不详\",\"purpose\":\"图形设计,建模,渲染,多屏\"}"
},
{
"name": "NVIDIA英伟达Quadro K2200 4G",
"brand": "NVIDIA英伟达",
"model": "Quadro K2200",
"price": 439,
"description": "专业渲染建模图形剪辑人工智能设计师绘图工业显卡,4G显存,工包",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/231461/36/11255/91304/659796a5Fa40a211a/4cad801723b6b259.jpg",
"stock": 11,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"Quadro K系列\",\"memory\":\"4GB\",\"memory_type\":\"不详\",\"purpose\":\"渲染,建模,图形剪辑,AI设计,绘图\"}"
},
{
"name": "英伟达【企业专享价】RTX A2000 12G",
"brand": "英伟达",
"model": "RTX A2000",
"price": 3859,
"description": "专业设计显卡,12G显存,建模渲染/AI推理/视频剪辑,单槽低功耗,工包(全新)",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/254368/27/20220/51009/67ad8aa0Fadd37605/aac43712e53ecbde.jpg",
"stock": 15,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX A系列\",\"memory\":\"12GB\",\"memory_type\":\"不详\",\"purpose\":\"设计,建模,渲染,AI推理,视频剪辑\",\"form_factor\":\"单槽\",\"power_consumption\":\"低功耗\"}"
},
{
"name": "耕升GeForce RTX 5060 Ti 踏雪X3 OC 16G",
"brand": "耕升",
"model": "GeForce RTX 5060 Ti 踏雪X3 OC",
"price": 4040.9,
"description": "16G游戏显卡,支持DLSS 4,适用于电竞游戏/设计剪辑/AI本地部署/直播娱乐",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/298040/12/8542/95247/682af59aF2061b01c/83397a2560de22b2.jpg",
"stock": 47,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"16GB\",\"memory_type\":\"不详\",\"features\":\"DLSS 4,OC\",\"purpose\":\"电竞游戏,设计剪辑,AI本地部署,直播娱乐\"}"
},
{
"name": "英伟达RTX3060 12G公版涡轮",
"brand": "英伟达",
"model": "RTX 3060",
"price": 1769,
"description": "全新定制涡轮AI深度学习大模型专业显卡,12G显存,公版涡轮",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/229514/10/37240/76928/67b9b319F2ba4ec17/63dc07fbbac2b781.jpg",
"stock": 26,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 30系列\",\"memory\":\"12GB\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习,大模型\",\"cooling\":\"涡轮散热\",\"design\":\"公版\"}"
},
{
"name": "NVIDIA英伟达Quadro RTX4000 8G",
"brand": "NVIDIA英伟达",
"model": "Quadro RTX 4000",
"price": 2289,
"description": "专业渲染建模图形剪辑人工智能设计师绘图工业显卡,8G显存,工包",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/249699/2/2117/63383/6597991aF7a60fdde/c64c74f24086619d.jpg",
"stock": 31,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"Quadro RTX系列\",\"memory\":\"8GB\",\"memory_type\":\"不详\",\"purpose\":\"渲染,建模,图形剪辑,AI设计,绘图\"}"
},
{
"name": "英伟达(NVIDIA)Tesla A100显卡80G 原版",
"brand": "英伟达(NVIDIA)",
"model": "Tesla A100",
"price": 168890,
"description": "定制PCIE人工智能大模型加速GPU服务器显卡,80G显存,原版,不开发票",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/318143/1/10580/61481/685609b5Ffc45fe82/52d0d3d5597f0ff7.jpg",
"stock": 10,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"Tesla\",\"memory\":\"80GB\",\"memory_type\":\"不详\",\"purpose\":\"人工智能,大模型加速,GPU服务器\",\"interface\":\"PCIe\"}"
},
{
"name": "英伟达(NVIDIA)RTX4090系列 英伟达RTX4070 SUPER 12G公版涡轮",
"brand": "英伟达(NVIDIA)",
"model": "RTX 4070 SUPER",
"price": 5034,
"description": "公版系列原装定制涡轮AI深度学习训练推理大模型专业显卡,内容创作卡,顶级游戏卡,12G显存,京采质保3年",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/274809/37/12964/69082/67e9f31dFe30b13b9/3bda82292adfc13d.jpg",
"stock": 59,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 40系列\",\"memory\":\"12GB\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习,内容创作,游戏\",\"cooling\":\"涡轮散热\"}"
},
{
"name": "英伟达 NVIDIA RTX5090 32G 公版",
"brand": "英伟达 NVIDIA",
"model": "RTX 5090",
"price": 25959,
"description": "独立显卡,32G显存,适用于大模型算力,深度学习,电竞游戏,专业设计,AI渲染,公版,不开发票",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/320143/29/9528/81858/68511713F92ffc35d/bec0b929e0c0789d.jpg",
"stock": 42,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"32GB\",\"memory_type\":\"不详\",\"purpose\":\"大模型算力,深度学习,电竞游戏,专业设计,AI渲染\",\"design\":\"公版\"}"
},
{
"name": "NVIDIA英伟达Quadro M4000 8G",
"brand": "NVIDIA英伟达",
"model": "Quadro M4000",
"price": 1099,
"description": "专业渲染建模图形剪辑人工智能设计师绘图工业显卡,8G显存,工包",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/247506/5/2047/64039/6597970eF9d763915/0dd2bbf83683d698.jpg",
"stock": 43,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"Quadro M系列\",\"memory\":\"8GB\",\"memory_type\":\"不详\",\"purpose\":\"渲染,建模,图形剪辑,AI设计,绘图\"}"
},
{
"name": "NVIDIA英伟达Quadro M2000 4G",
"brand": "NVIDIA英伟达",
"model": "Quadro M2000",
"price": 559,
"description": "专业渲染建模图形剪辑人工智能设计师绘图工业显卡,4G显存,工包",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/234046/27/11489/81101/659796faF7878eb6c/8759e0a00eefed56.jpg",
"stock": 27,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"Quadro M系列\",\"memory\":\"4GB\",\"memory_type\":\"不详\",\"purpose\":\"渲染,建模,图形剪辑,AI设计,绘图\"}"
},
{
"name": "影驰 GeForce RTX 5060 Ti 经典大将 8GB GDDR7",
"brand": "影驰",
"model": "GeForce RTX 5060 Ti 经典大将",
"price": 3192.6,
"description": "8GB GDDR7显存,支持DLSS 4,电竞光追游戏/AI本地部署显卡",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/298179/27/16038/131916/68512f28Fafd94934/a3ba958813802dc0.jpg",
"stock": 15,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"8GB\",\"memory_type\":\"GDDR7\",\"features\":\"DLSS 4,光线追踪\",\"purpose\":\"电竞游戏,AI本地部署\"}"
},
{
"name": "英伟达RTX4060Ti 16G涡轮",
"brand": "英伟达",
"model": "RTX 4060 Ti",
"price": 3659,
"description": "全新定制涡轮AI深度学习训练推理大模型专业显卡,16G显存",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/253592/13/25767/52653/67bdced9Fcefbbd86/d9ebfcf765e10b78.jpg",
"stock": 26,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 40系列\",\"memory\":\"16GB\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习,训练,推理,大模型\",\"cooling\":\"涡轮散热\"}"
},
{
"name": "盈通(yeston)GeForce RTX 5060 8G GDDR7 萌宠",
"brand": "盈通(yeston)",
"model": "GeForce RTX 5060 萌宠",
"price": 2643.7,
"description": "8G GDDR7显存,电竞光追游戏AI智能学习电脑独立显卡",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/299054/4/16964/76693/6855122bF85e10635/f9608349b60269aa.jpg",
"stock": 26,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"8GB\",\"memory_type\":\"GDDR7\",\"features\":\"光线追踪,AI智能学习\",\"purpose\":\"电竞游戏,AI智能学习\"}"
},
{
"name": "英伟达(NVIDIA)Tesla H100 80G",
"brand": "英伟达(NVIDIA)",
"model": "Tesla H100",
"price": 211999,
"description": "DeepSeeK部署显卡,人工智能深度学习高性能计算GPU,AI推理训练卡,80G显存",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/256593/23/19578/47861/67b602f4F17b542e7/0ea2b1d7e6ccc2b9.jpg",
"stock": 46,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"Tesla\",\"memory\":\"80GB\",\"memory_type\":\"不详\",\"purpose\":\"AI部署,深度学习,高性能计算,AI推理训练\"}"
},
{
"name": "耕升GeForce RTX 5060 Ti 追风 白 OC 16G",
"brand": "耕升",
"model": "GeForce RTX 5060 Ti 追风 白 OC",
"price": 3941.1,
"description": "16G游戏显卡,支持DLSS 4,适用于电竞游戏/设计剪辑/AI本地部署/直播娱乐",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/303153/31/7138/86921/682af5a1Fe8a17e6e/32391f33f6a62778.jpg",
"stock": 10,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"16GB\",\"memory_type\":\"不详\",\"features\":\"DLSS 4,OC\",\"purpose\":\"电竞游戏,设计剪辑,AI本地部署,直播娱乐\",\"color\":\"白色\"}"
},
{
"name": "英伟达RTX3080 10G 涡轮版",
"brand": "英伟达",
"model": "RTX 3080",
"price": 2629,
"description": "涡轮显卡,原厂公版双宽,AI模型绘图服务器GPU,10G显存,不开发票",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/305451/10/12494/65718/6856064eF985f9d61/cd5ebac2fbef5170.jpg",
"stock": 47,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 30系列\",\"memory\":\"10GB\",\"memory_type\":\"不详\",\"purpose\":\"AI模型绘图,服务器GPU\",\"cooling\":\"涡轮散热\",\"design\":\"公版\",\"width\":\"双宽\"}"
},
{
"name": "英伟达RTX3060ti 8G 公版涡轮",
"brand": "英伟达",
"model": "RTX 3060 Ti",
"price": 1979,
"description": "全新定制涡轮AI深度学习大模型专业显卡,8G显存,公版涡轮",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/311614/28/9065/62875/684a95dfF6610da67/cbac2d8badda016b.jpg",
"stock": 26,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 30系列\",\"memory\":\"8GB\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习,大模型\",\"cooling\":\"涡轮散热\",\"design\":\"公版\"}"
},
{
"name": "英伟达NVIDIA RTX 4500 Ada 24G DDR6",
"brand": "英伟达NVIDIA",
"model": "RTX 4500 Ada",
"price": 12999,
"description": "3D建模渲染设计绘图专业图形显卡,24G DDR6显存,工包(全新)",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/208603/27/48624/44037/673aafd1Fa656fdb4/699ccf6a6e557e8b.jpg",
"stock": 10,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX Ada系列\",\"memory\":\"24GB\",\"memory_type\":\"GDDR6\",\"purpose\":\"3D建模,渲染,设计绘图\"}"
},
{
"name": "英伟达RTX3060 12G公版涡轮",
"brand": "英伟达",
"model": "RTX 3060",
"price": 1769,
"description": "全新定制涡轮AI深度学习大模型专业显卡,12G显存,公版涡轮",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/305217/35/9785/68666/684a95dbFa16c4705/e3e3055a86556924.jpg",
"stock": 42,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 30系列\",\"memory\":\"12GB\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习,大模型\",\"cooling\":\"涡轮散热\",\"design\":\"公版\"}"
},
{
"name": "华硕七彩虹技嘉微星RX580 8G",
"brand": "华硕,七彩虹,技嘉,微星",
"model": "RX 580",
"price": 369,
"description": "台式电脑独立显卡,游戏设计师办公二手拆机卡,8G显存,618年中大促预计到手369元",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/320441/16/10281/111856/68536cacF3d3b0c15/77721a9daafbba26.jpg",
"stock": 27,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RX 500系列\",\"memory\":\"8GB\",\"memory_type\":\"不详\",\"purpose\":\"游戏,设计,办公\",\"condition\":\"二手拆机卡\"}"
},
{
"name": "英伟达(NVIDIA)Quadro K620 2G",
"brand": "英伟达(NVIDIA)",
"model": "Quadro K620",
"price": 260,
"description": "专业图形设计建模渲染显卡,2G显存,全新工业包装,开发票联系客服",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/191802/32/35612/79768/65e549b4Fe3b9ae52/1a0fbfeaf98b2887.jpg",
"stock": 42,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"Quadro K系列\",\"memory\":\"2GB\",\"memory_type\":\"不详\",\"purpose\":\"图形设计,建模,渲染\"}"
},
{
"name": "英伟达RTX4060Ti 16G涡轮",
"brand": "英伟达",
"model": "RTX 4060 Ti",
"price": 3659,
"description": "全新定制涡轮AI深度学习大模型专业显卡,16G显存",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/302327/36/13101/45200/684a9616Fc4ed25b2/501957d90178e7c7.jpg",
"stock": 15,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 40系列\",\"memory\":\"16GB\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习,大模型\",\"cooling\":\"涡轮散热\"}"
},
{
"name": "英伟达(NVIDIA)RTX3080 10G涡轮公版",
"brand": "英伟达(NVIDIA)",
"model": "RTX 3080",
"price": 2629,
"description": "涡轮公版,适用于4/8卡服务器工作站AI推理训练专业图形显卡,10G显存,现货,不开发票",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/314523/4/11024/82630/685609aeF87931a15/d49584f4d8a97af0.png",
"stock": 11,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 30系列\",\"memory\":\"10GB\",\"memory_type\":\"不详\",\"purpose\":\"AI推理训练,专业图形,服务器工作站\",\"cooling\":\"涡轮散热\",\"design\":\"公版\"}"
},
{
"name": "宽科 NVIDIA A6000 48G",
"brand": "宽科",
"model": "NVIDIA A6000",
"price": 41999,
"description": "专业图形显卡,48G显存,工包,高性能GPU显卡,可按需定制支持对公专票/13%增值税/Ubuntu/Centos/Win",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/258497/19/971/52452/6765749fF5f2ba950/1580ade88b428c97.jpg",
"stock": 14,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX A系列\",\"memory\":\"48GB\",\"memory_type\":\"不详\",\"purpose\":\"专业图形\"}"
},
{
"name": "英伟达(NVIDIA)GeForce RTX 5070 Founders Edition 12g",
"brand": "英伟达(NVIDIA)",
"model": "GeForce RTX 5070 Founders Edition",
"price": 5699,
"description": "全新国行公版显卡,12G显存",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/281649/36/10745/41220/67e54099F52979978/dfdc1795bfaba778.png",
"stock": 30,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"12GB\",\"memory_type\":\"不详\",\"design\":\"Founders Edition(公版)\"}"
},
{
"name": "铭瑄(MAXSUN)GeForce MS-RTX3050 TR 6G DLSS",
"brand": "铭瑄(MAXSUN)",
"model": "GeForce MS-RTX3050 TR",
"price": 1296.4,
"description": "6G GDDR6显存,性能提升,太极双9CM刀锋扇叶,强化散热,支持DLSS AI加速,家用办公娱乐装机优选!三年质保,支持个人送保!",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/314823/6/11194/121567/685519bdF5f516fea/ede7f83132677c86.jpg",
"stock": 26,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 30系列\",\"memory\":\"6GB\",\"memory_type\":\"GDDR6\",\"features\":\"DLSS AI加速\",\"cooling\":\"双风扇\",\"purpose\":\"游戏,专业设计,直播,家用办公娱乐\"}"
},
{
"name": "耕升GeForce RTX5070踏雪 OC",
"brand": "耕升",
"model": "GeForce RTX 5070 踏雪 OC",
"price": 4919,
"description": "支持DLSS4,支持DeepSeek,电竞游戏台式机电脑GM游戏显卡",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/277659/5/19271/82285/67f8ac6fF8429a183/623b87cfd69c05f9.jpg",
"stock": 59,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"不详\",\"memory_type\":\"不详\",\"features\":\"DLSS4,DeepSeek,OC\",\"purpose\":\"电竞游戏\"}"
},
{
"name": "华硕七彩虹技嘉微星RX580 8G",
"brand": "华硕,七彩虹,技嘉,微星",
"model": "RX 580",
"price": 369,
"description": "台式电脑独立显卡,游戏设计师办公二手拆机卡,8G显存,618年中大促预计到手369元",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/320441/16/10281/111856/68536cacF3d3b0c15/77721a9daafbba26.jpg",
"stock": 26,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RX 500系列\",\"memory\":\"8GB\",\"memory_type\":\"不详\",\"purpose\":\"游戏,设计,办公\",\"condition\":\"二手拆机卡\"}"
},
{
"name": "英伟达RTX4060Ti 16G涡轮",
"brand": "英伟达",
"model": "RTX 4060 Ti",
"price": 3659,
"description": "全新定制涡轮AI深度学习大模型专业显卡,16G显存",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/302327/36/13101/45200/684a9616Fc4ed25b2/501957d90178e7c7.jpg",
"stock": 46,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 40系列\",\"memory\":\"16GB\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习,大模型\",\"cooling\":\"涡轮散热\"}"
},
{
"name": "英伟达(NVIDIA)RTX4060TI 16G 涡轮版",
"brand": "英伟达(NVIDIA)",
"model": "RTX 4060 Ti",
"price": 3653,
"description": "16G显存,涡轮版,现货",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/284589/20/10886/58185/67e6755dF258de9f7/140c61650c896457.jpg",
"stock": 10,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 40系列\",\"memory\":\"16GB\",\"memory_type\":\"不详\",\"cooling\":\"涡轮散热\"}"
},
{
"name": "英伟达(NVIDIA)RTX 3080TI 12G 涡轮版",
"brand": "英伟达(NVIDIA)",
"model": "RTX 3080 Ti",
"price": 3709,
"description": "原厂公版双宽,适用于4卡/8卡服务器工作站GPU,12G显存,涡轮版,不开发票",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/288083/3/15554/55900/68564feeF651232ac/5dfdcccc62dde191.jpg",
"stock": 42,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 30系列\",\"memory\":\"12GB\",\"memory_type\":\"不详\",\"purpose\":\"服务器工作站GPU\",\"cooling\":\"涡轮散热\",\"design\":\"公版\",\"width\":\"双宽\"}"
},
{
"name": "英伟达RTX3080 20G公版涡轮",
"brand": "英伟达",
"model": "RTX 3080",
"price": 3499,
"description": "全新定制涡轮AI深度学习大模型专业显卡,20G显存,公版涡轮",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/316702/31/8516/48302/684a95fcFc2f0ae8e/2f2c9ba9cd33b34d.jpg",
"stock": 42,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 30系列\",\"memory\":\"20GB\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习,大模型\",\"cooling\":\"涡轮散热\",\"design\":\"公版\"}"
},
{
"name": "英伟达(NVIDIA)RTX A400 4GB GDDR6",
"brand": "英伟达(NVIDIA)",
"model": "RTX A400",
"price": 1246.5,
"description": "4GB GDDR6显存,平面设计,多屏输出,专业图形显卡,工业包装",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/129608/8/47496/69102/670f832eFbbf5ce0a/59dd665d134d3cbf.jpg",
"stock": 31,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX A系列\",\"memory\":\"4GB\",\"memory_type\":\"GDDR6\",\"purpose\":\"平面设计,多屏输出,专业图形\"}"
},
{
"name": "英伟达 NVIDAI RTX T1000 8G 原厂盒装",
"brand": "英伟达 NVIDAI",
"model": "RTX T1000",
"price": 2799,
"description": "3D建模设计绘图台式机专业图形显卡,8G显存,原厂盒装(全新)",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/168328/16/51437/71397/673ae49dF3dbee7bd/40558aa5d49adf54.jpg",
"stock": 27,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX T系列\",\"memory\":\"8GB\",\"memory_type\":\"不详\",\"purpose\":\"3D建模,设计绘图,专业图形\"}"
},
{
"name": "影驰 GeForce RTX 5060 Ti 经典大将 8GB GDDR7",
"brand": "影驰",
"model": "GeForce RTX 5060 Ti 经典大将",
"price": 3192.6,
"description": "8GB GDDR7显存,支持DLSS 4,电竞光追游戏/AI本地部署显卡",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/298179/27/16038/131916/68512f28Fafd94934/a3ba958813802dc0.jpg",
"stock": 47,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"8GB\",\"memory_type\":\"GDDR7\",\"features\":\"DLSS 4,光线追踪\",\"purpose\":\"电竞游戏,AI本地部署\"}"
},
{
"name": "万竞 GTX1060 6G 百战-至龙",
"brand": "万竞",
"model": "GTX 1060",
"price": 788,
"description": "高端独立显卡,全新盒装,适用于电脑黑神话悟空游戏,设计渲染学习台式显卡,6G显存",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/273641/39/22652/148775/6801f82fF8bfa62d5/bf69574555e4f402.jpg",
"stock": 30,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"GTX 10系列\",\"memory\":\"6GB\",\"memory_type\":\"不详\",\"purpose\":\"游戏,设计,渲染,学习\"}"
},
{
"name": "英伟达(NVIDIA)RTX A1000 8GB GDDR6",
"brand": "英伟达(NVIDIA)",
"model": "RTX A1000",
"price": 2993,
"description": "8GB GDDR6显存,视频剪辑,建模渲染,专业图形显卡,工业包装",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/96026/11/45380/72560/670f89b2Fa2f18ac2/cc3ba86ddf32d153.jpg",
"stock": 14,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX A系列\",\"memory\":\"8GB\",\"memory_type\":\"GDDR6\",\"purpose\":\"视频剪辑,建模,渲染,专业图形\"}"
},
{
"name": "英伟达(NVIDIA) P400 2G",
"brand": "英伟达(NVIDIA)",
"model": "P400",
"price": 256,
"description": "2G显存,现货",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/273370/40/16973/75655/67f4cb3bF2cc60132/cae804fc089cb8a2.jpg",
"stock": 15,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"Quadro P系列\",\"memory\":\"2GB\",\"memory_type\":\"不详\"}"
},
{
"name": "英伟达RTX3080 20G公版涡轮",
"brand": "英伟达",
"model": "RTX 3080",
"price": 3499,
"description": "全新定制涡轮AI深度学习大模型专业显卡,20G显存,公版涡轮",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/280498/35/6001/54260/67dbe352F0223e061/cda01b396f228073.jpg",
"stock": 26,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 30系列\",\"memory\":\"20GB\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习,大模型\",\"cooling\":\"涡轮散热\",\"design\":\"公版\"}"
},
{
"name": "英伟达(NVIDIA)RTX 2000 Ada 16GB GDDR6",
"brand": "英伟达(NVIDIA)",
"model": "RTX 2000 Ada",
"price": 5117.74,
"description": "16GB GDDR6显存,专业显卡,工业包装",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/11671/9/24414/34958/66dfc776Fec2f18d0/6ccc24633289eddb.jpg",
"stock": 15,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX Ada系列\",\"memory\":\"16GB\",\"memory_type\":\"GDDR6\",\"purpose\":\"专业显卡\"}"
},
{
"name": "NVIDIA英伟达 NVIDIA RTX4090 涡轮显卡 AI深度学习 显存48G定制",
"brand": "NVIDIA英伟达",
"model": "RTX 4090",
"price": 3600,
"description": "24G升级置换48G(升级费),涡轮显卡,AI深度学习,工业包装定制",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/279637/24/16558/53741/67f35745F784e0439/b76ddaec4521c860.png",
"stock": 26,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 40系列\",\"memory\":\"48GB(升级后)\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习\",\"cooling\":\"涡轮散热\",\"customization\":\"显存升级\"}"
},
{
"name": "英伟达珑京 NVIDIA TESLA L40 48G",
"brand": "英伟达珑京 NVIDIA",
"model": "TESLA L40",
"price": 49999,
"description": "AI深度学习训练推理GPU服务器配件,专业计算显卡,48G显存",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/221707/10/43823/39580/673ae857Fa0e9e0e9/601ee3533ebe6b44.jpg",
"stock": 47,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"Tesla\",\"memory\":\"48GB\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习,训练,推理,服务器配件,专业计算\"}"
},
{
"name": "英伟达P1000 4G",
"brand": "英伟达",
"model": "Quadro P1000",
"price": 788,
"description": "专业显卡,4G显存,工业包装全新,不含发票",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/190933/35/52794/38916/673ead61Fe18c9294/a0273a7215e908b1.jpg",
"stock": 10,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"Quadro P系列\",\"memory\":\"4GB\",\"memory_type\":\"不详\",\"purpose\":\"专业显卡\"}"
},
{
"name": "微星(MSI)幻影师 GeForce RTX 5060 Ti 8G SHADOW 2X OC PLUS",
"brand": "微星(MSI)",
"model": "GeForce RTX 5060 Ti 幻影师 SHADOW 2X OC PLUS",
"price": 3192.6,
"description": "8G显存,电竞游戏设计专业电脑显卡",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/287994/2/10420/70695/6837f286F556dbf2c/ea7eb0fdb1d41bd5.png",
"stock": 43,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"8GB\",\"memory_type\":\"不详\",\"features\":\"OC PLUS\",\"purpose\":\"电竞游戏,设计专业\"}"
},
{
"name": "盈通(yeston))GeForce RTX 5060 8GB GDDR7 大地之神 GA",
"brand": "盈通(yeston)",
"model": "GeForce RTX 5060 大地之神 GA",
"price": 2543.9,
"description": "8GB GDDR7显存,电竞光追游戏AI智能学习电脑独立显卡",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/320405/23/6913/71982/68429145Fc92a087c/29d4fd849a9b5393.jpg",
"stock": 14,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"8GB\",\"memory_type\":\"GDDR7\",\"features\":\"光线追踪,AI智能学习\",\"purpose\":\"电竞游戏,AI智能学习\"}"
},
{
"name": "英伟达(NVIDIA) RTX5000 16G",
"brand": "英伟达(NVIDIA)",
"model": "RTX 5000",
"price": 5637,
"description": "16G显存,现货",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/279710/20/14310/60834/67ecfa60F34a6ff7f/be1dd3f5e277c4e9.jpg",
"stock": 31,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX系列\",\"memory\":\"16GB\",\"memory_type\":\"不详\"}"
},
{
"name": "电竞叛客 RTX5060 X2W OC 8G",
"brand": "电竞叛客",
"model": "RTX 5060 X2W OC",
"price": 2599,
"description": "新品,8G显存,全新架构,白色甜品电竞游戏台式电脑独立显卡",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/307753/20/2202/103414/682ae0b2F7cf830a0/858c05238bea3500.jpg",
"stock": 30,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"8GB\",\"memory_type\":\"不详\",\"features\":\"OC\",\"purpose\":\"电竞游戏\",\"architecture\":\"全新架构\",\"color\":\"白色\"}"
},
{
"name": "英伟达RTX3060ti 8G 公版涡轮",
"brand": "英伟达",
"model": "RTX 3060 Ti",
"price": 1979,
"description": "全新定制涡轮AI深度学习大模型专业显卡,8G显存,公版涡轮",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/256193/39/23995/71305/67b9b319F39021ef4/6c96d653d9259404.jpg",
"stock": 10,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 30系列\",\"memory\":\"8GB\",\"memory_type\":\"不详\",\"purpose\":\"AI深度学习,大模型\",\"cooling\":\"涡轮散热\",\"design\":\"公版\"}"
},
{
"name": "电竞叛客 RTX 5060 Ti X3W 8G",
"brand": "电竞叛客",
"model": "RTX 5060 Ti X3W",
"price": 3449,
"description": "8G显存,全新架构,白色甜品电竞游戏台式电脑独立显卡,RTX5060Ti新品首发",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/304731/10/7953/111660/68410a84Fea969450/9c66969584c535d6.jpg",
"stock": 31,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"8GB\",\"memory_type\":\"不详\",\"features\":\"OC\",\"purpose\":\"电竞游戏\",\"architecture\":\"全新架构\",\"color\":\"白色\"}"
},
{
"name": "英伟达NVIDIA RTX PRO 6000 96G",
"brand": "英伟达NVIDIA",
"model": "RTX 6000 Ada",
"price": 58999,
"description": "顶级专业显卡,96G显存,支持vGPU软件,适用于AI训练/8K渲染/深度学习,工作站服务器专用,工包",
"imageUrl": "",
"stock": 46,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX Ada系列\",\"memory\":\"96GB\",\"memory_type\":\"不详\",\"features\":\"vGPU支持\",\"purpose\":\"AI训练,8K渲染,深度学习,工作站服务器专用\"}"
},
{
"name": "英伟达(Nvidia)NVIDIA 4060TI-8G公版FE",
"brand": "英伟达(Nvidia)",
"model": "NVIDIA 4060 Ti Founders Edition",
"price": 3499,
"description": "8G显存,公版原厂FE涡轮显卡",
"imageUrl": "",
"stock": 10,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 40系列\",\"memory\":\"8GB\",\"memory_type\":\"不详\",\"design\":\"Founders Edition(公版)\",\"cooling\":\"涡轮散热\"}"
},
{
"name": "电竞叛客 GeForce RTX 5060 Ti X3W 8GB",
"brand": "电竞叛客",
"model": "GeForce RTX 5060 Ti X3W",
"price": 3449,
"description": "8G显存,台式机电脑显卡,支持DLSS 4,电竞游戏AI运算,deepseek人工智能独立显卡",
"imageUrl": "",
"stock": 14,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 50系列\",\"memory\":\"8GB\",\"memory_type\":\"不详\",\"features\":\"DLSS 4,deepseek AI运算\",\"purpose\":\"电竞游戏,AI运算,人工智能\"}"
},
{
"name": "七彩虹4060ultra 双风扇",
"brand": "七彩虹",
"model": "RTX 4060 Ultra",
"price": 2099,
"description": "电竞游戏显卡,质保一年,只换不修,拆机显卡,双风扇",
"imageUrl": "",
"stock": 30,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 40系列\",\"memory\":\"不详\",\"memory_type\":\"不详\",\"cooling\":\"双风扇\",\"warranty\":\"质保一年,只换不修\",\"condition\":\"拆机显卡\"}"
},
{
"name": "英伟达(NVIDIA)RTX A6000 48G",
"brand": "英伟达(NVIDIA)",
"model": "RTX A6000",
"price": 42806,
"description": "48G显存,现货",
"imageUrl": "",
"stock": 46,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX A系列\",\"memory\":\"48GB\",\"memory_type\":\"不详\"}"
},
{
"name": "耕升(GAINWARD) GeForce RTX 4060 Ti 踏雪 8G",
"brand": "耕升(GAINWARD)",
"model": "GeForce RTX 4060 Ti 踏雪",
"price": 2698,
"description": "8G GDDR6显存,支持DLSS 3,专业设计绘画AI制图电竞游戏电脑台式机显卡",
"imageUrl": "",
"stock": 10,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 40系列\",\"memory\":\"8GB\",\"memory_type\":\"GDDR6\",\"features\":\"DLSS 3\",\"purpose\":\"专业设计,绘画,AI制图,电竞游戏\"}"
},
{
"name": "七彩虹(Colorful)RTX 4060 U W DUO OC 8G",
"brand": "七彩虹(Colorful)",
"model": "RTX 4060 U W DUO OC",
"price": 2599,
"description": "8G GDDR6显存,白色,战斧系列,电竞台式机游戏显卡",
"imageUrl": "",
"stock": 11,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 40系列\",\"memory\":\"8GB\",\"memory_type\":\"GDDR6\",\"features\":\"OC\",\"cooling\":\"双风扇\",\"color\":\"白色\",\"purpose\":\"电竞游戏\"}"
},
{
"name": "七彩虹 RTX2060 6G",
"brand": "七彩虹",
"model": "RTX 2060",
"price": 975,
"description": "游戏显卡,6G显存,拒绝矿卡,拆机精品",
"imageUrl": "",
"stock": 10,
"typeName": "显卡",
"specifications": "{\"GPU_series\":\"RTX 20系列\",\"memory\":\"6GB\",\"memory_type\":\"不详\",\"purpose\":\"游戏\",\"condition\":\"拆机精品,非矿卡\"}"
}
]
+398
View File
@@ -0,0 +1,398 @@
[
{
"name": "雷克沙(Lexar) 雷神铠 DDR4台式机内存条 8G 3200 3600频率 马甲条 3200 皓月白 8GB 1条",
"brand": "雷克沙(Lexar)",
"model": "雷神铠",
"price": 199,
"description": "DDR4台式机内存条,8G,3200/3600频率,马甲条,皓月白",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/47315/39/25669/79542/66ab7034F816dd189/6e50eadbe86976ea.jpg",
"stock": 42,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"8GB\",\"frequency\":\"3200MHz\",\"formFactor\":\"台式机\",\"color\":\"皓月白\"}"
},
{
"name": "七彩虹(Colorful)DDR3L 8G 1600 笔记本内存条",
"brand": "七彩虹(Colorful)",
"model": "DDR3L",
"price": 44.91,
"description": "DDR3L笔记本内存条,8G,1600频率",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/279183/1/545/97174/67ceb4f8F8833714c/e0975d50472bf7ba.jpg",
"stock": 10,
"typeName": "内存",
"specifications": "{\"type\":\"DDR3L\",\"capacity\":\"8GB\",\"frequency\":\"1600MHz\",\"formFactor\":\"笔记本\"}"
},
{
"name": "联想(Lenovo)16GB DDR4 3200 笔记本内存条 助力AI 适配黑神话悟空",
"brand": "联想(Lenovo)",
"model": "",
"price": 228.54,
"description": "16GB DDR4 3200笔记本内存条,助力AI,适配黑神话悟空",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/243597/39/4822/86937/65e1a1daF3b8aa636/8070623b0a1046d8.jpg",
"stock": 14,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"16GB\",\"frequency\":\"3200MHz\",\"formFactor\":\"笔记本\"}"
},
{
"name": "七彩虹(Colorful)16GB DDR4 3200 台式机内存条 战斧系列·钛金灰 CL18",
"brand": "七彩虹(Colorful)",
"model": "战斧系列·钛金灰",
"price": 198.6,
"description": "16GB DDR4 3200台式机内存条,CL18,升级大容量,铝制马甲,散热稳定,3200XMP高频",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/91655/4/48798/88364/66b2effcFe0380d3e/b8e2bffcace0f9b3.jpg",
"stock": 15,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"16GB\",\"frequency\":\"3200MHz\",\"formFactor\":\"台式机\",\"casLatency\":\"CL18\",\"color\":\"钛金灰\",\"features\":\"铝制马甲,XMP高频\"}"
},
{
"name": "雷克沙(Lexar) DDR4台式机电脑内存条8g 16g 32G 3200 DDR4 3200 8GB 1条",
"brand": "雷克沙(Lexar)",
"model": "",
"price": 139,
"description": "DDR4台式机电脑内存条,8GB,3200频率",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/132145/6/23419/116266/6200dd47E767f5c2d/ce3aefac7effa2b8.jpg",
"stock": 11,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"8GB\",\"frequency\":\"3200MHz\",\"formFactor\":\"台式机\"}"
},
{
"name": "Crucial英睿达 16GB DDR5 5600频率 笔记本内存条 美光(原镁光)原厂颗粒 AI电脑配件",
"brand": "Crucial英睿达",
"model": "",
"price": 298.4,
"description": "16GB DDR5 5600频率笔记本内存条,美光原厂颗粒,AI电脑配件,Deepseek硬件,AI大模型硬件,稳定兼容,即插即用",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/295996/8/14031/192479/685521b5Ff5610f6b/58a38d973f6c9736.jpg",
"stock": 47,
"typeName": "内存",
"specifications": "{\"type\":\"DDR5\",\"capacity\":\"16GB\",\"frequency\":\"5600MHz\",\"formFactor\":\"笔记本\",\"chipBrand\":\"美光\"}"
},
{
"name": "三星 SAMSUNG 笔记本内存条 16G DDR5 5600频率",
"brand": "三星 SAMSUNG",
"model": "",
"price": 378.24,
"description": "笔记本内存条,16G DDR5 5600频率",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/301698/23/16241/162476/68558330F22ecf9d2/c61b042395e0f801.jpg",
"stock": 46,
"typeName": "内存",
"specifications": "{\"type\":\"DDR5\",\"capacity\":\"16GB\",\"frequency\":\"5600MHz\",\"formFactor\":\"笔记本\"}"
},
{
"name": "光威(Gloway)8GB DDR3 1600 台式机内存条 战将系列",
"brand": "光威(Gloway)",
"model": "战将系列",
"price": 48.9,
"description": "8GB DDR3 1600台式机内存条,国产存储品牌,七天免费试用,终身质保,稳定兼容,以换代修!",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/225216/31/27171/30806/6708c2f8Fc474cd58/299b5ac28dbfc23c.jpg",
"stock": 31,
"typeName": "内存",
"specifications": "{\"type\":\"DDR3\",\"capacity\":\"8GB\",\"frequency\":\"1600MHz\",\"formFactor\":\"台式机\"}"
},
{
"name": "2017/19/20款苹果一体机内存条",
"brand": "苹果",
"model": "",
"price": 199,
"description": "适用于2017/19/20款苹果一体机内存条",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/187604/9/28251/98223/63213aaaEc705a73e/d00d9bec81925125.jpg",
"stock": 30,
"typeName": "内存",
"specifications": "{\"compatibleModels\":\"2017/2019/2020款苹果一体机\"}"
},
{
"name": "七彩虹(Colorful)16GB DDR5 5600 笔记本内存条",
"brand": "七彩虹(Colorful)",
"model": "",
"price": 254.49,
"description": "16GB DDR5 5600笔记本内存条,精选优质颗粒,DDR5良好兼容性,16G大容量,5600频率强悍性能",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/246047/6/1734/82415/6593b9ecF9b9e471e/0ee7492a6c312569.jpg",
"stock": 15,
"typeName": "内存",
"specifications": "{\"type\":\"DDR5\",\"capacity\":\"16GB\",\"frequency\":\"5600MHz\",\"formFactor\":\"笔记本\"}"
},
{
"name": "金百达(KINGBANK)8GB DDR3 1600 台式机内存条",
"brand": "金百达(KINGBANK)",
"model": "",
"price": 58.88,
"description": "8GB DDR3 1600台式机内存条,企业购优选款,高性价比,助您电脑运行速度快人一步",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/304879/28/11595/189098/68537b29Ff82a6ebe/a8d770c64e1ee12b.jpg",
"stock": 26,
"typeName": "内存",
"specifications": "{\"type\":\"DDR3\",\"capacity\":\"8GB\",\"frequency\":\"1600MHz\",\"formFactor\":\"台式机\"}"
},
{
"name": "金百达(KINGBANK)16GB DDR5 5600 笔记本内存条 适配迷你主机 可组双通道",
"brand": "金百达(KINGBANK)",
"model": "",
"price": 238.52,
"description": "16GB DDR5 5600笔记本内存条,适配迷你主机,可组双通道",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/300989/36/16282/198808/68537c7dFfaf1e090/2d40356779513589.jpg",
"stock": 14,
"typeName": "内存",
"specifications": "{\"type\":\"DDR5\",\"capacity\":\"16GB\",\"frequency\":\"5600MHz\",\"formFactor\":\"笔记本\",\"features\":\"支持双通道,适配迷你主机\"}"
},
{
"name": "金泰克kimtigo【12期免息】8G/16G DDR4 3200MHz 台式机电脑内存条磐虎系列 16G 3200MHz",
"brand": "金泰克kimtigo",
"model": "磐虎系列",
"price": 259,
"description": "16G DDR4 3200MHz台式机电脑内存条,磐虎系列",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/303762/23/6563/131186/6839a9bdF8f0e63db/ec08f8117daff86f.jpg",
"stock": 30,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"16GB\",\"frequency\":\"3200MHz\",\"formFactor\":\"台式机\"}"
},
{
"name": "金百达(KINGBANK)32GB(16GBX2)套装 DDR4 3200 台式机内存条 银爵 C16 适配黑神话悟空",
"brand": "金百达(KINGBANK)",
"model": "银爵 C16",
"price": 517.96,
"description": "32GB(16GBX2)套装 DDR4 3200台式机内存条,银爵 C16,适配黑神话悟空,电竞京选,高效散热片,强悍性能,电竞游戏一爽到底",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/303165/40/15188/159962/6853c04fFa3cc0b6b/817331a42f6e2c1a.jpg",
"stock": 26,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"32GB (16GBx2)\",\"frequency\":\"3200MHz\",\"formFactor\":\"台式机\",\"casLatency\":\"C16\",\"features\":\"高效散热片,适配黑神话悟空\"}"
},
{
"name": "Kingston金士顿8g 1600 4g 16Gb 2666台式机3/4代DDR3/4内存条95新 金士顿DDR3-4G-1600 DDR3【三年保】",
"brand": "Kingston金士顿",
"model": "",
"price": 13.7,
"description": "金士顿DDR3-4G-1600台式机内存条,95新,三年保",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/253594/19/1234/73799/6762ccf0F8a56f911/35a62eaa0927d076.jpg",
"stock": 31,
"typeName": "内存",
"specifications": "{\"type\":\"DDR3\",\"capacity\":\"4GB\",\"frequency\":\"1600MHz\",\"formFactor\":\"台式机\",\"condition\":\"95新\"}"
},
{
"name": "金百达(KINGBANK)16GB(8GBX2)套装 DDR4 3200 台式机内存条 银爵 C16 适配黑神话悟空",
"brand": "金百达(KINGBANK)",
"model": "银爵 C16",
"price": 278.44,
"description": "16GB(8GBX2)套装 DDR4 3200台式机内存条,银爵 C16,适配黑神话悟空,电竞京选,高效散热片,强悍性能,电竞游戏一爽到底",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/311461/17/10782/158725/6853a4fdF25dc2e4e/ae851f75de6f780b.jpg",
"stock": 11,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"16GB (8GBx2)\",\"frequency\":\"3200MHz\",\"formFactor\":\"台式机\",\"casLatency\":\"C16\",\"features\":\"高效散热片,适配黑神话悟空\"}"
},
{
"name": "倍控倍控ikuaios软路由N5105工控机N100电脑迷你整机服务器云桌面2.5G网卡2500M防火墙7505 准系统+电源 G31黑N100六网226-DDR5内存M.2接口",
"brand": "倍控",
"model": "ikuaios软路由N5105工控机N100",
"price": 840,
"description": "准系统+电源 G31黑N100六网226-DDR5内存M.2接口",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/186259/36/41641/80590/6556d355F527fa076/bbe11cdad293fd34.jpg",
"stock": 47,
"typeName": "其他",
"specifications": "{\"CPU\":\"N100\",\"networkPorts\":\"六网226\",\"memoryType\":\"DDR5\",\"storageInterface\":\"M.2\",\"formFactor\":\"迷你整机\"}"
},
{
"name": "威刚(ADATA)8GB DDR4 3200 台式机内存 万紫千红",
"brand": "威刚(ADATA)",
"model": "万紫千红",
"price": 138.72,
"description": "8GB DDR4 3200台式机内存,品质铸就经典,3200高频,万紫千红,严选颗粒、稳定兼容!",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/298599/39/12677/70486/683e8278F29fca227/edb538867f3a58c3.jpg",
"stock": 14,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"8GB\",\"frequency\":\"3200MHz\",\"formFactor\":\"台式机\"}"
},
{
"name": "联想(Lenovo)8GB DDR3 1600 笔记本内存条 低电压版",
"brand": "联想(Lenovo)",
"model": "",
"price": 58.88,
"description": "8GB DDR3 1600笔记本内存条,低电压版",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/198038/22/52302/51999/6752a045F21ab1fe8/0eef83af1bd83b6a.jpg",
"stock": 30,
"typeName": "内存",
"specifications": "{\"type\":\"DDR3\",\"capacity\":\"8GB\",\"frequency\":\"1600MHz\",\"formFactor\":\"笔记本\",\"voltage\":\"低电压\"}"
},
{
"name": "联想(Lenovo) 拯救者Y系列16GB DDR5 5600频率 笔记本内存条 三星原厂颗粒 AI电脑配件",
"brand": "联想(Lenovo)",
"model": "拯救者Y系列",
"price": 298.4,
"description": "16GB DDR5 5600频率笔记本内存条,三星原厂颗粒,AI电脑配件",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/314906/3/11328/133170/68561c0bFceb6af27/ece52de63f9f6de5.jpg",
"stock": 11,
"typeName": "内存",
"specifications": "{\"type\":\"DDR5\",\"capacity\":\"16GB\",\"frequency\":\"5600MHz\",\"formFactor\":\"笔记本\",\"chipBrand\":\"三星\"}"
},
{
"name": "金士顿(Kingston) ddr4内存条 Beast野兽系列 Fury骇客神条 台式机电脑内存条 DDR4 2666 8G 台式内存",
"brand": "金士顿(Kingston)",
"model": "Beast野兽系列 Fury骇客神条",
"price": 199,
"description": "DDR4 2666 8G台式机内存条",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/113571/27/45104/57542/65e17e36F01edc0da/d08de790bad94acf.jpg",
"stock": 10,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"8GB\",\"frequency\":\"2666MHz\",\"formFactor\":\"台式机\"}"
},
{
"name": "Crucial英睿达 16GB DDR5 4800频率 笔记本内存条 美光(原镁光)原厂颗粒 AI电脑配件",
"brand": "Crucial英睿达",
"model": "",
"price": 278.44,
"description": "16GB DDR5 4800频率笔记本内存条,美光原厂颗粒,AI电脑配件,Deepseek硬件,AI大模型硬件,稳定兼容,即插即用",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/306154/20/12168/187990/685521b7Fb557a4fc/0b85bfb5485f4abf.jpg",
"stock": 42,
"typeName": "内存",
"specifications": "{\"type\":\"DDR5\",\"capacity\":\"16GB\",\"frequency\":\"4800MHz\",\"formFactor\":\"笔记本\",\"chipBrand\":\"美光\"}"
},
{
"name": "金百达(KINGBANK)32GB(16G×2)套装 DDR4 3600 台式机内存条 海力士原装CJR/DJR颗粒 银爵 C18",
"brand": "金百达(KINGBANK)",
"model": "银爵 C18",
"price": 537.92,
"description": "32GB(16G×2)套装 DDR4 3600台式机内存条,海力士原装CJR/DJR颗粒,银爵 C18",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/291398/27/11831/161925/6853a5e7F23c98867/e7ba3dd1633c5739.jpg",
"stock": 11,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"32GB (16GBx2)\",\"frequency\":\"3600MHz\",\"formFactor\":\"台式机\",\"casLatency\":\"C18\",\"chipBrand\":\"海力士 (CJR/DJR)\"}"
},
{
"name": "阿斯加特(Asgard)32GB(16GBx2) DDR5 6000 台式机内存条 女武神·瓦尔基里II代 RGB灯条 海力士A-die CL28 极夜黑",
"brand": "阿斯加特(Asgard)",
"model": "女武神·瓦尔基里II代",
"price": 977.04,
"description": "32GB(16GBx2) DDR5 6000台式机内存条,RGB灯条,海力士A-die CL28,极夜黑,AMDX3D黄金搭档,海力士A-die颗粒,6000C28低时序,10层PCB加强散热,支持Intel-XMP3.0/AMD平台!",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/303516/30/13712/151928/68529d27F8fef93e7/09ba9a95acc22686.jpg",
"stock": 59,
"typeName": "内存",
"specifications": "{\"type\":\"DDR5\",\"capacity\":\"32GB (16GBx2)\",\"frequency\":\"6000MHz\",\"formFactor\":\"台式机\",\"casLatency\":\"CL28\",\"chipBrand\":\"海力士 A-die\",\"features\":\"RGB灯条,10层PCB,支持XMP3.0\"}"
},
{
"name": "雷克沙(Lexar) 内存条DDR4 3200笔记本电脑内存8g 16g 32G 可选 DDR4 3200 16GB 1条",
"brand": "雷克沙(Lexar)",
"model": "",
"price": 239,
"description": "DDR4 3200笔记本电脑内存条,16GB",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/20331/16/19176/95764/6347808fEe5b3e83e/907c7bd9e89e7ff1.jpg",
"stock": 42,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"16GB\",\"frequency\":\"3200MHz\",\"formFactor\":\"笔记本\"}"
},
{
"name": "三星(SAMSUNG) 内存条16g/32g DDR5 4800/5600 频率 五代笔记本内存条原厂 三星DDR5 16G 笔记本内存条 DDR5 5600",
"brand": "三星(SAMSUNG)",
"model": "",
"price": 329,
"description": "三星DDR5 16G笔记本内存条,DDR5 5600频率,支持12代英特尔cpu,1.1V工作电压,ECC技术,保持数据完整,持续工作稳定",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/113633/37/36443/100448/64632309Fcce3d958/15aeade9178acca3.jpg",
"stock": 30,
"typeName": "内存",
"specifications": "{\"type\":\"DDR5\",\"capacity\":\"16GB\",\"frequency\":\"5600MHz\",\"formFactor\":\"笔记本\",\"voltage\":\"1.1V\",\"features\":\"支持ECC技术\",\"compatibleCPU\":\"12代英特尔CPU\"}"
},
{
"name": "金士顿/华硕/品牌拆机 2GB 4GB 8GB 3代4代内存 1600 2400 内存条台式机内存 骇客神条 DDR3 8G 1600",
"brand": "金士顿/华硕",
"model": "骇客神条",
"price": 49.9,
"description": "DDR3 8G 1600台式机内存条,拆机件",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/220807/18/22227/88952/63eaeecfF4f221398/838005d27d235cb2.jpg",
"stock": 59,
"typeName": "内存",
"specifications": "{\"type\":\"DDR3\",\"capacity\":\"8GB\",\"frequency\":\"1600MHz\",\"formFactor\":\"台式机\",\"condition\":\"拆机件\"}"
},
{
"name": "宏碁掠夺者(PREDATOR)48G(24G×2)套装 DDR5 6000频率 台式机内存条 Hermes冰刃系列 RGB灯条(C28) 石耀黑 AI电脑配件",
"brand": "宏碁掠夺者(PREDATOR)",
"model": "Hermes冰刃系列",
"price": 1096.8,
"description": "48G(24G×2)套装 DDR5 6000频率台式机内存条,RGB灯条(C28),石耀黑,AI电脑配件",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/283871/1/24943/77867/68085621Fca8e1628/b40d730366fe6454.jpg",
"stock": 42,
"typeName": "内存",
"specifications": "{\"type\":\"DDR5\",\"capacity\":\"48GB (24GBx2)\",\"frequency\":\"6000MHz\",\"formFactor\":\"台式机\",\"casLatency\":\"C28\",\"features\":\"RGB灯条\",\"color\":\"石耀黑\"}"
},
{
"name": "海力士 8G DDR3L 1600 低电压笔记本内存",
"brand": "海力士",
"model": "",
"price": 80,
"description": "8G DDR3L 1600低电压笔记本内存",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/191103/21/17752/774694/61122699Eb1a077ea/368d3a0f71eef4eb.jpg",
"stock": 26,
"typeName": "内存",
"specifications": "{\"type\":\"DDR3L\",\"capacity\":\"8GB\",\"frequency\":\"1600MHz\",\"formFactor\":\"笔记本\",\"voltage\":\"低电压\"}"
},
{
"name": "七彩虹(Colorful)8GB DDR3 1600 台式机内存 普条系列 1.35V 低压",
"brand": "七彩虹(Colorful)",
"model": "普条系列",
"price": 54.89,
"description": "8GB DDR3 1600台式机内存,1.35V低压,精选优质颗粒,DDR3良好兼容性,增容好帮手,8G增容,1600良好频率",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/260168/26/21775/100817/67b59b90F64d3a9b6/093912a6785b3adc.jpg",
"stock": 42,
"typeName": "内存",
"specifications": "{\"type\":\"DDR3\",\"capacity\":\"8GB\",\"frequency\":\"1600MHz\",\"formFactor\":\"台式机\",\"voltage\":\"1.35V\",\"features\":\"低压\"}"
},
{
"name": "长城(Great Wall)16GB DDR4 3200频率 台式机内存条马甲条 Intel专用条龙鳞黑金系列",
"brand": "长城(Great Wall)",
"model": "龙鳞黑金系列",
"price": 158.68,
"description": "16GB DDR4 3200频率台式机内存条,马甲条,Intel专用条",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/149717/12/47222/70232/67189a9dF79d90f06/4e1dba4f76088121.jpg",
"stock": 26,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"16GB\",\"frequency\":\"3200MHz\",\"formFactor\":\"台式机\",\"features\":\"马甲条,Intel专用\"}"
},
{
"name": "光威(Gloway)8GB DDR3L 1600 笔记本内存条 战将系列 低电压版",
"brand": "光威(Gloway)",
"model": "战将系列",
"price": 54.89,
"description": "8GB DDR3L 1600笔记本内存条,低电压版,国产存储品牌,七天免费试用,终身质保,稳定兼容,以换代修!",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/249595/31/20688/42201/6708bf71Fba8f2edb/f5771c5fa69f0709.jpg",
"stock": 26,
"typeName": "内存",
"specifications": "{\"type\":\"DDR3L\",\"capacity\":\"8GB\",\"frequency\":\"1600MHz\",\"formFactor\":\"笔记本\",\"voltage\":\"低电压\"}"
},
{
"name": "倍控倍控ikuaios软路由N5105工控机N100电脑迷你整机服务器云桌面2.5G网卡2500M防火墙7505 准系统+电源 G31黑N100六网226-DDR5内存M.2接口",
"brand": "倍控",
"model": "ikuaios软路由N5105工控机N100",
"price": 840,
"description": "准系统+电源 G31黑N100六网226-DDR5内存M.2接口",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/186259/36/41641/80590/6556d355F527fa076/bbe11cdad293fd34.jpg",
"stock": 10,
"typeName": "其他",
"specifications": "{\"CPU\":\"N100\",\"networkPorts\":\"六网226\",\"memoryType\":\"DDR5\",\"storageInterface\":\"M.2\",\"formFactor\":\"迷你整机\"}"
},
{
"name": "金百达(KINGBANK)32GB(16G×2)套装 DDR4 3600 台式机内存条 海力士原装CJR/DJR颗粒 银爵 C18",
"brand": "金百达(KINGBANK)",
"model": "银爵 C18",
"price": 537.92,
"description": "32GB(16G×2)套装 DDR4 3600台式机内存条,海力士原装CJR/DJR颗粒,银爵 C18",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/291398/27/11831/161925/6853a5e7F23c98867/e7ba3dd1633c5739.jpg",
"stock": 46,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"32GB (16GBx2)\",\"frequency\":\"3600MHz\",\"formFactor\":\"台式机\",\"casLatency\":\"C18\",\"chipBrand\":\"海力士 (CJR/DJR)\"}"
},
{
"name": "七彩虹(Colorful)16GB DDR5 4800 笔记本内存条",
"brand": "七彩虹(Colorful)",
"model": "",
"price": 238.52,
"description": "16GB DDR5 4800笔记本内存条,精选优质颗粒,DDR5良好兼容性,16G大容量,4800频率强悍性能",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/237632/5/9544/82472/6593b9d5Fa97f182d/3b753fe1e3c21b72.jpg",
"stock": 26,
"typeName": "内存",
"specifications": "{\"type\":\"DDR5\",\"capacity\":\"16GB\",\"frequency\":\"4800MHz\",\"formFactor\":\"笔记本\"}"
},
{
"name": "金百达(KINGBANK)32GB(16GBX2)套装 DDR4 3200 台式机内存条 银爵 C16 适配黑神话悟空",
"brand": "金百达(KINGBANK)",
"model": "银爵 C16",
"price": 517.96,
"description": "32GB(16GBX2)套装 DDR4 3200台式机内存条,银爵 C16,适配黑神话悟空,电竞京选,高效散热片,强悍性能,电竞游戏一爽到底",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/303165/40/15188/159962/6853c04fFa3cc0b6b/817331a42f6e2c1a.jpg",
"stock": 30,
"typeName": "内存",
"specifications": "{\"type\":\"DDR4\",\"capacity\":\"32GB (16GBx2)\",\"frequency\":\"3200MHz\",\"formFactor\":\"台式机\",\"casLatency\":\"C16\",\"features\":\"高效散热片,适配黑神话悟空\"}"
}
]
+178
View File
@@ -0,0 +1,178 @@
[
{
"name": "希捷酷鱼固态硬盘1T SATA3.0接口",
"brand": "希捷",
"model": "酷鱼",
"price": 279,
"description": "酷鱼固态硬盘1T SATA3.0接口 台式机笔记本电脑硬盘SSD SATA3接口 512G",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/208931/2/50574/63352/674ec887Fc8d497c5/bd9ea594e4a34578.jpg",
"stock": 15,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"1TB\",\"interface\":\"SATA3.0\",\"type\":\"SSD\",\"formFactor\":\"2.5英寸\"}"
},
{
"name": "长城(Great Wall) 512GB SSD固态硬盘 SATA3.0接口 GW560系列",
"brand": "长城",
"model": "GW560系列",
"price": 188.62,
"description": "512GB SSD固态硬盘 SATA3.0接口 读速540MB/S台式机/笔记本通用",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/319159/21/10201/83910/685286fbFfb80d404/680fab1aed34ff41.jpg",
"stock": 31,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"512GB\",\"interface\":\"SATA3.0\",\"readSpeed\":\"540MB/s\",\"type\":\"SSD\",\"formFactor\":\"2.5英寸\"}"
},
{
"name": "长城(Great Wall) 128GB SSD固态硬盘 SATA3.0接口 GW560系列",
"brand": "长城",
"model": "GW560系列",
"price": 68.86,
"description": "128GB SSD固态硬盘 SATA3.0接口 读速540MB/S台式机/笔记本通用",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/298612/14/13194/78595/685286bfF8fdb0429/62d7fd70a5f1acfc.jpg",
"stock": 47,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"128GB\",\"interface\":\"SATA3.0\",\"readSpeed\":\"540MB/s\",\"type\":\"SSD\",\"formFactor\":\"2.5英寸\"}"
},
{
"name": "西数/希捷企业级氦气硬盘 500GB",
"brand": "西部数据/希捷",
"model": "企业级氦气硬盘",
"price": 31.8,
"description": "8T 10T 12T 14T 16T 18T企业级氦气硬盘监控录像NAS阵列游戏储存 500GB 西数/希捷随机发货",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/223373/27/31325/91985/6646a42bF6b1963e3/b49443b744fcfe27.jpg",
"stock": 14,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"500GB\",\"type\":\"HDD\",\"interface\":\"SATA\",\"features\":\"氦气盘,企业级,监控,NAS\"}"
},
{
"name": "西部数据(WD)SSD固态硬盘 SA510 SATA M.2 Blue系列 500G",
"brand": "西部数据(WD)",
"model": "SA510 Blue",
"price": 339,
"description": "笔记本台式机电脑 SSD固态硬盘 SA510 SATA M.2 Blue系列 3D技术 高速读写 WD Blue SATA | 性能款 500G",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/304134/15/12361/155883/6856a8a0F47d73a5d/2be842b4e9dd85ae.jpg",
"stock": 42,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"500GB\",\"interface\":\"SATA M.2\",\"type\":\"SSD\",\"technology\":\"3D NAND\"}"
},
{
"name": "七彩虹(Colorful) 1TB SSD固态硬盘 SATA3.0接口 SL500系列",
"brand": "七彩虹(Colorful)",
"model": "SL500系列",
"price": 358.28,
"description": "1TB SSD固态硬盘 SATA3.0接口 SL500系列 标准版。出色性能,高性价比,静音运行。大容量大满足,新增盘推荐,游戏畅快无阻",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/114380/15/24069/344213/625fc2acEd61f5899/ed6f86abaca42556.jpg",
"stock": 10,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"1TB\",\"interface\":\"SATA3.0\",\"type\":\"SSD\",\"formFactor\":\"2.5英寸\"}"
},
{
"name": "西部数据(WD)台式机械硬盘 WD Blue 西数蓝盘 2TB",
"brand": "西部数据(WD)",
"model": "WD Blue WD20EZBX",
"price": 458.08,
"description": "台式机械硬盘 WD Blue 西数蓝盘 2TB 7200转 256MB SATA 电脑硬盘 3.5英寸 WD20EZBX。机械硬盘,超值爆款天天抢。DIY装机升级理想选择,低功耗、经久耐用",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/315254/1/11219/140818/68557e2cF209b54e8/6fc913d11b9ce990.jpg",
"stock": 31,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"2TB\",\"interface\":\"SATA\",\"rotationalSpeed\":\"7200RPM\",\"cache\":\"256MB\",\"type\":\"HDD\",\"formFactor\":\"3.5英寸\"}"
},
{
"name": "希捷(SEAGATE)台式机硬盘 2TB 希捷酷鱼系列",
"brand": "希捷(SEAGATE)",
"model": "酷鱼 ST2000DM008",
"price": 448.1,
"description": "台式机硬盘 2TB 7200转 256MB 机械硬盘 SATA 希捷酷鱼系列 电脑硬盘 3.5英寸 ST2000DM008",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/310221/39/11203/123121/68551ad2F577277af/54e628420b8968b8.jpg",
"stock": 47,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"2TB\",\"interface\":\"SATA\",\"rotationalSpeed\":\"7200RPM\",\"cache\":\"256MB\",\"type\":\"HDD\",\"formFactor\":\"3.5英寸\"}"
},
{
"name": "HUWEI通用固态移动硬盘 2TB",
"brand": "HUWEI",
"model": "标准版",
"price": 123.45,
"description": "通用固态移动硬盘1t高速ssd移动固态u盘大容量存储颗粒固态硬盘4t 黑色-标准版 2TB",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/313002/18/1555/94187/68278f08F4dc102ff/c6f1a44b60cf7f11.jpg",
"stock": 15,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"2TB\",\"type\":\"移动固态硬盘\",\"interface\":\"USB\"}"
},
{
"name": "西数企业级监控氦气硬盘 1TB",
"brand": "西部数据",
"model": "企业级监控氦气硬盘",
"price": 127.49,
"description": "1T 8T 10T 12T 14T 16T 18T企业级监控氦气硬盘控录像NAS阵列储存 1TB 西数1tb机械硬盘",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/272632/39/5181/113985/67d8d5acF667120d2/8804d86bc54a1a67.jpg",
"stock": 27,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"1TB\",\"type\":\"HDD\",\"interface\":\"SATA\",\"features\":\"氦气盘,企业级,监控,NAS\"}"
},
{
"name": "七彩虹(Colorful) 512GB SSD固态硬盘 SATA3.0接口 SL500系列",
"brand": "七彩虹(Colorful)",
"model": "SL500系列",
"price": 194.61,
"description": "512GB SSD固态硬盘 SATA3.0接口 SL500系列。爆款力荐,主流容量,疾速开机游戏。三年质保,多平台兼容,可靠稳定耐用,低耗节能",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/126750/28/28531/348087/625fc27cE063cc028/0544e3b1519b5de5.jpg",
"stock": 43,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"512GB\",\"interface\":\"SATA3.0\",\"type\":\"SSD\",\"formFactor\":\"2.5英寸\"}"
},
{
"name": "西部数据(WD)SSD固态硬盘 SN5000 1TB",
"brand": "西部数据(WD)",
"model": "SN5000",
"price": 449,
"description": "SSD固态硬盘 NVMe M.2接口 AI电脑配件 笔记本电脑游戏硬盘 原厂颗粒不虚标不掉速 SN5000 | PCle4x4 5150MB/s 大容量",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/286485/35/16717/170972/68553164F3aeb2bb8/4362d3e4766f0e05.jpg",
"stock": 47,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"1TB\",\"interface\":\"NVMe M.2\",\"protocol\":\"PCIe 4.0 x4\",\"readSpeed\":\"5150MB/s\",\"type\":\"SSD\"}"
},
{
"name": "TANLR 探路人128GB SSD固态硬盘 SATA3.0接口 镭豹T560系列",
"brand": "TANLR 探路人",
"model": "镭豹T560系列",
"price": 68.86,
"description": "128GB SSD固态硬盘 SATA3.0接口高速TLC颗粒 读速560MB/s 台式机笔记本电脑存储配件",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/292774/17/15991/211279/6853657aF3c50cb29/e52a4c6300ad6f89.jpg",
"stock": 42,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"128GB\",\"interface\":\"SATA3.0\",\"readSpeed\":\"560MB/s\",\"type\":\"SSD\",\"flashType\":\"TLC\"}"
},
{
"name": "光威(Gloway)512GB SSD固态硬盘 SATA3.0接口 悍将系列",
"brand": "光威(Gloway)",
"model": "悍将系列",
"price": 168.66,
"description": "512GB SSD固态硬盘 SATA3.0接口 悍将系列。为国产存储崛起而努力。专为提升电脑运行速度设计,大容量高速存储升级优选方案。三年质保!",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/291606/3/14242/107856/6850fac7F6290d7e2/1e74bac4a3823695.jpg",
"stock": 14,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"512GB\",\"interface\":\"SATA3.0\",\"type\":\"SSD\",\"formFactor\":\"2.5英寸\"}"
},
{
"name": "长城(Great Wall) 1TB SSD固态硬盘 SATA3.0接口 GW560系列",
"brand": "长城",
"model": "GW560系列",
"price": 338.32,
"description": "1TB SSD固态硬盘 SATA3.0接口 读速550MB/S台式机/笔记本通用",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/274756/18/4588/79068/67d78c6aF6ea41c45/65f1eac32b30f764.jpg",
"stock": 15,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"1TB\",\"interface\":\"SATA3.0\",\"readSpeed\":\"550MB/s\",\"type\":\"SSD\",\"formFactor\":\"2.5英寸\"}"
},
{
"name": "长城(Great Wall) 256GB SSD固态硬盘 SATA3.0接口 GW560系列",
"brand": "长城",
"model": "GW560系列",
"price": 108.78,
"description": "256GB SSD固态硬盘 SATA3.0接口 读速540MB/S台式机/笔记本通用",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/312376/12/10675/78986/685286e7F5541039c/9877e42ecf903836.jpg",
"stock": 27,
"typeName": "硬盘",
"specifications": "{\"capacity\":\"256GB\",\"interface\":\"SATA3.0\",\"readSpeed\":\"540MB/s\",\"type\":\"SSD\",\"formFactor\":\"2.5英寸\"}"
}
]
+519
View File
@@ -0,0 +1,519 @@
[
{
"name": "长城(Great Wall)本色K13升级版电脑游戏机箱",
"brand": "长城(Great Wall)",
"model": "本色K13升级版",
"price": 99.7,
"description": "ATX主板/五硬盘位/U3/玻璃侧透/宽体/8风扇位/5080显卡",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/182653/25/43199/136528/65fc0336Ff00dea1d/39132cd40cab781d.jpg",
"stock": 27,
"typeName": "机箱",
"specifications": "{\"formFactor\":\"ATX\",\"driveBays\":5,\"usbPort\":\"USB3.0\",\"sidePanel\":\"钢化玻璃\",\"fanSlots\":8,\"gpuLength\":\"5080mm\"}"
},
{
"name": "先马(SAMA)朱雀air 台式电脑主机箱",
"brand": "先马(SAMA)",
"model": "朱雀air",
"price": 97.8,
"description": "前板铁网/双面散热孔/6个风扇位/宽体五金/支持ATX主板240水冷竖装显卡,高性价比散热机箱",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/308763/30/10893/163454/6853c6f1F7a96f43a/034b73d9649c2182.jpg",
"stock": 30,
"typeName": "机箱",
"specifications": "{\"frontPanel\":\"铁网\",\"cooling\":\"双面散热孔\",\"fanSlots\":6,\"formFactor\":\"ATX\",\"liquidCooling\":\"240mm\",\"gpuMount\":\"竖装\"}"
},
{
"name": "长城(Great Wall)冰霜X3黑色电脑机箱",
"brand": "长城(Great Wall)",
"model": "冰霜X3",
"price": 159.58,
"description": "MATX/360水冷位/U3/9风扇位/磁吸翻门/钢网面板/五槽PCI/兼容5080,超高性价比360水冷机箱,金属面板冲细网孔,磁吸翻门,轻便装机有一手",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/221102/11/24782/132919/64daea83F97f5fa72/da3372be063413d9.jpg",
"stock": 31,
"typeName": "机箱",
"specifications": "{\"formFactor\":\"MATX\",\"liquidCooling\":\"360mm\",\"usbPort\":\"USB3.0\",\"fanSlots\":9,\"doorType\":\"磁吸翻门\",\"frontPanel\":\"钢网\",\"pciSlots\":5,\"gpuLength\":\"5080mm\"}"
},
{
"name": "AOCCG391W白色 游戏电脑主机海景房机箱",
"brand": "AOC",
"model": "CG391W",
"price": 118.76,
"description": "双面玻璃/支持MATX主板/240水冷/7风扇位/270°全景/左右分仓",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t20270806/3905/18/24725/111944/66b17b21F70b6c877/752fbecca417a925.jpg",
"stock": 27,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"sidePanel\":\"双面玻璃\",\"formFactor\":\"MATX\",\"liquidCooling\":\"240mm\",\"fanSlots\":7,\"viewAngle\":\"270°\",\"layout\":\"左右分仓\"}"
},
{
"name": "鑫谷(Segotep)卓灵1白色机箱",
"brand": "鑫谷(Segotep)",
"model": "卓灵1",
"price": 128.74,
"description": "MATX主板位/玻璃侧透/小型风冷散热家用办公游戏桌面台式电脑主机箱",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/151346/33/28488/130586/64c504acFb1ecb93c/a0121dc5275d92ee.jpg",
"stock": 14,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"formFactor\":\"MATX\",\"sidePanel\":\"钢化玻璃\",\"coolingType\":\"风冷\"}"
},
{
"name": "AOCCG391B黑色 游戏电脑主机海景房机箱",
"brand": "AOC",
"model": "CG391B",
"price": 108.78,
"description": "双面玻璃/支持MATX主板/240水冷/7风扇位/270°全景/左右分仓",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/48415/20/26622/94266/66cee052Fa99d431f/982b9ad1fa55b63e.jpg",
"stock": 26,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"sidePanel\":\"双面玻璃\",\"formFactor\":\"MATX\",\"liquidCooling\":\"240mm\",\"fanSlots\":7,\"viewAngle\":\"270°\",\"layout\":\"左右分仓\"}"
},
{
"name": "EVESKY 玄武MINI 无立柱海景房机箱",
"brand": "EVESKY",
"model": "玄武MINI",
"price": 59.9,
"description": "M-ATX主板/270°广角玻璃侧透 240水冷游戏电脑主机箱 支持4090显卡 玄武MINI黑色【支持M-ATX主板/240水冷】",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t20270511/247000/38/8833/14197/663f3fb9Fb06c601a/835919ab578449b6.jpg",
"stock": 30,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"formFactor\":\"M-ATX\",\"viewAngle\":\"270°\",\"sidePanel\":\"钢化玻璃\",\"liquidCooling\":\"240mm\",\"gpuLength\":\"4090mm\",\"pillarless\":\"是\"}"
},
{
"name": "长城(Great Wall)冰霜X3W白色电脑机箱",
"brand": "长城(Great Wall)",
"model": "冰霜X3W",
"price": 159.58,
"description": "M-ATX/360水冷位/U3/9风扇位/磁吸翻门/五槽PCI/兼容5080,金属面板冲细网孔,360水冷位+磁吸翻门,轻便装机有一手",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/135879/1/46247/112617/67064bbbF1b63f346/23207ae3e69083db.jpg",
"stock": 15,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"formFactor\":\"M-ATX\",\"liquidCooling\":\"360mm\",\"usbPort\":\"USB3.0\",\"fanSlots\":9,\"doorType\":\"磁吸翻门\",\"pciSlots\":5,\"gpuLength\":\"5080mm\",\"frontPanel\":\"金属冲孔网\"}"
},
{
"name": "半岛铁盒应龙360水冷海景房机箱",
"brand": "半岛铁盒",
"model": "应龙",
"price": 179,
"description": "ATX/MATX无A柱台式主机台式机电脑全景房 白色/ATX/360水冷/9扇位",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/168019/25/49437/396917/6712486eF641bf024/a3630a7a6e7d3d52.jpg",
"stock": 26,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"formFactor\":\"ATX/MATX\",\"liquidCooling\":\"360mm\",\"fanSlots\":9,\"pillarless\":\"是\",\"design\":\"海景房\"}"
},
{
"name": "AOCCG392B黑色 MATX游戏台式电脑主机箱",
"brand": "AOC",
"model": "CG392B",
"price": 138.72,
"description": "支持360水冷/无立柱270°海景房/9风扇位/左右分仓",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/192796/25/50897/107588/6729e04eF7cdc74c7/699d00e76ea0ad7d.jpg",
"stock": 10,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"formFactor\":\"MATX\",\"liquidCooling\":\"360mm\",\"pillarless\":\"是\",\"viewAngle\":\"270°\",\"design\":\"海景房\",\"fanSlots\":9,\"layout\":\"左右分仓\"}"
},
{
"name": "大水牛(BUBALUS)追风M 台式电脑游戏主机侧透机箱",
"brand": "大水牛(BUBALUS)",
"model": "追风M",
"price": 79.74,
"description": "支持ATX主板/240水冷位/电源仓/背线/U3,【侧透爆款】支持ATX/M-ATX/ITX主板,支持120/240水冷,独立电源仓",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/288994/4/8882/88826/6854d769F612872cb/c65df59ddfcff65e.jpg",
"stock": 47,
"typeName": "机箱",
"specifications": "{\"sidePanel\":\"侧透\",\"formFactor\":\"ATX/M-ATX/ITX\",\"liquidCooling\":\"240mm\",\"psuShroud\":\"独立电源仓\",\"cableManagement\":\"支持背线\",\"usbPort\":\"USB3.0\"}"
},
{
"name": "爱国者V60 海景房机箱",
"brand": "爱国者",
"model": "V60",
"price": 108,
"description": "全景无立柱MATX高颜值240水冷台式电脑台式机主机箱 黑色(显卡限长305mm)",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/10917/4/24871/89836/66b6e2c5Ffac157b6/889ef266a62c3c1a.jpg",
"stock": 47,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"design\":\"海景房\",\"pillarless\":\"是\",\"formFactor\":\"MATX\",\"liquidCooling\":\"240mm\",\"gpuLength\":\"305mm\"}"
},
{
"name": "爱国者YOGO T21大机箱",
"brand": "爱国者",
"model": "YOGO T21",
"price": 139,
"description": "台式ATX大板360水冷",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/231520/34/26887/137294/66dc06bbFdc104e1f/867d19e7ee8dcc87.jpg",
"stock": 31,
"typeName": "机箱",
"specifications": "{\"formFactor\":\"ATX\",\"liquidCooling\":\"360mm\"}"
},
{
"name": "EVESKY积至(EVESKY)灵悦迷你小机箱",
"brand": "EVESKY积至(EVESKY)",
"model": "灵悦",
"price": 29.9,
"description": "办公家用电脑机箱(拉丝面板/M-ATX小主板/背线/USB2.0) 刺客,支持中小主板,拉丝面板,迷你小机箱!",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/102551/16/50674/46407/66dd80d6F32afbd86/363f22f382534f62.jpg",
"stock": 58,
"typeName": "机箱",
"specifications": "{\"color\":\"刺客\",\"frontPanel\":\"拉丝面板\",\"formFactor\":\"M-ATX\",\"cableManagement\":\"支持背线\",\"usbPort\":\"USB2.0\",\"size\":\"迷你\"}"
},
{
"name": "AOCCG392W白色 MATX游戏台式电脑主机箱",
"brand": "AOC",
"model": "CG392W",
"price": 148.7,
"description": "支持360水冷/无立柱270°海景房/9风扇位/左右分仓",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/228037/2/29040/116823/6729e092Fc091a6e3/aef5d454d0af35a7.jpg",
"stock": 14,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"formFactor\":\"MATX\",\"liquidCooling\":\"360mm\",\"pillarless\":\"是\",\"viewAngle\":\"270°\",\"design\":\"海景房\",\"fanSlots\":9,\"layout\":\"左右分仓\"}"
},
{
"name": "AOC CG381W海景房机箱",
"brand": "AOC",
"model": "CG381W/CG381B",
"price": 78.55,
"description": "matx无立柱全景侧透ITX电脑主机箱高颜值DIY台式机游戏电竞水冷白色主机箱小型 CG381B黑【M-ATX双面全景侧透】",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/267739/8/19997/53053/67b036b1F84b1b140/e4251cc7958caa0f.jpg",
"stock": 11,
"typeName": "机箱",
"specifications": "{\"color\":\"白色/黑色\",\"formFactor\":\"MATX/ITX\",\"pillarless\":\"是\",\"sidePanel\":\"全景侧透\",\"liquidCooling\":\"支持\",\"design\":\"海景房\"}"
},
{
"name": "航嘉(Huntkey)V320初恋 全景版海景房机箱",
"brand": "航嘉(Huntkey)",
"model": "V320初恋",
"price": 109,
"description": "台式电脑机箱(双面钢化玻璃/左右分区/240水冷/7风扇位/M-ATX主板 ) V320初恋全景版-白色",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/249069/5/1350/66296/658ff8c4F91d9b107/35aa6ffa36ac3c26.jpg",
"stock": 26,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"design\":\"海景房\",\"sidePanel\":\"双面钢化玻璃\",\"layout\":\"左右分区\",\"liquidCooling\":\"240mm\",\"fanSlots\":7,\"formFactor\":\"M-ATX\"}"
},
{
"name": "瓦尔基里(VALKYRIE)VK03 WHITE 白色 ATX 游戏电脑台式机箱",
"brand": "瓦尔基里(VALKYRIE)",
"model": "VK03 WHITE",
"price": 448.1,
"description": "支持360水冷 6.2吋触摸屏 270°海景房",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/308187/6/4960/116283/6836ee73F90ad7198/cc9bf2906bc6b103.jpg",
"stock": 27,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"formFactor\":\"ATX\",\"liquidCooling\":\"360mm\",\"display\":\"6.2吋触摸屏\",\"viewAngle\":\"270°\",\"design\":\"海景房\"}"
},
{
"name": "航嘉(Huntkey) 暗夜猎手5 电脑机箱",
"brand": "航嘉(Huntkey)",
"model": "暗夜猎手5",
"price": 85,
"description": "台式DIY全侧透游戏水冷ATX大板背线机箱 暗夜猎手5-黑色,支持大主板/32cm长显卡/支持16.5cm高散热器/宽体五金/240水冷/USB3.0/支持背线!!",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/197941/1/18578/190839/61a1de8dE8cec8e4d/5b6427da506d1fd9.jpg",
"stock": 26,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"sidePanel\":\"全侧透\",\"formFactor\":\"ATX\",\"gpuLength\":\"320mm\",\"cpuCoolerHeight\":\"165mm\",\"liquidCooling\":\"240mm\",\"usbPort\":\"USB3.0\",\"cableManagement\":\"支持背线\"}"
},
{
"name": "爱国者A15电脑机箱",
"brand": "爱国者",
"model": "A15",
"price": 99,
"description": "台式机MATX/ATX大机箱240水冷风冷台式主机箱 黑色(铁板非侧透)",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/237784/35/23257/17509/669f2c28F4b937036/758300f30e92733e.jpg",
"stock": 27,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"formFactor\":\"MATX/ATX\",\"liquidCooling\":\"240mm\",\"coolingType\":\"风冷/水冷\",\"sidePanel\":\"非侧透\"}"
},
{
"name": "爱国者星璨岚 小岚海景房机箱",
"brand": "爱国者",
"model": "星璨岚 小岚",
"price": 199,
"description": "无立柱MATX/240水冷",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/224659/37/15954/96455/6639c2d5F57e56112/74e0f15f232144ca.jpg",
"stock": 31,
"typeName": "机箱",
"specifications": "{\"design\":\"海景房\",\"pillarless\":\"是\",\"formFactor\":\"MATX\",\"liquidCooling\":\"240mm\"}"
},
{
"name": "长城(Great Wall)阿基米德9 PRO V2灰色电脑机箱",
"brand": "长城(Great Wall)",
"model": "阿基米德9 PRO V2",
"price": 234.53,
"description": "360水冷/磁吸玻璃翻门/0.8mm厚侧板/5硬盘位/10风扇位/5090显卡,专为高端电竞设计,前部和顶部均支持360mm水冷位,10风扇位",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/108222/34/21897/104492/64828040F57df1447/9a243731d013ace8.jpg",
"stock": 15,
"typeName": "机箱",
"specifications": "{\"color\":\"灰色\",\"liquidCooling\":\"360mm\",\"doorType\":\"磁吸玻璃翻门\",\"sidePanelThickness\":\"0.8mm\",\"driveBays\":5,\"fanSlots\":10,\"gpuLength\":\"5090mm\"}"
},
{
"name": "长城(Great Wall)冰霜X7黑色电脑机箱",
"brand": "长城(Great Wall)",
"model": "冰霜X7",
"price": 228.54,
"description": "背插主板/磁吸金属面板/0.75mm厚五金/四面便拆装/360水冷位/9风扇位/5090",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/314907/1/8615/133150/6848e136F393b7e28/3ad916ac31e5fe7c.jpg",
"stock": 43,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"motherboardMount\":\"背插\",\"frontPanel\":\"磁吸金属面板\",\"materialThickness\":\"0.75mm\",\"disassembly\":\"四面便拆装\",\"liquidCooling\":\"360mm\",\"fanSlots\":9,\"gpuLength\":\"5090mm\"}"
},
{
"name": "EVESKY 棱镜MINI海景房电脑机箱",
"brand": "EVESKY",
"model": "棱镜MINI",
"price": 63,
"description": "无立柱台式主机m-atx白色侧透240水冷游戏机箱非暴风雪S920 棱镜海景房-黑色【支持M-ATX主板/240水冷】,全景无立柱海景房机箱、双面玻璃侧透!",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/232756/18/23591/75208/66dd8123Fb5924027/c7c741c0826440c8.jpg",
"stock": 42,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"design\":\"海景房\",\"pillarless\":\"是\",\"formFactor\":\"M-ATX\",\"sidePanel\":\"双面玻璃侧透\",\"liquidCooling\":\"240mm\"}"
},
{
"name": "长城(Great Wall)冰霜X3B黑色电脑机箱",
"brand": "长城(Great Wall)",
"model": "冰霜X3B",
"price": 158.68,
"description": "MATX主板/细钢网面板/360水冷位/9风扇位/USB3.0/细网孔铁侧板/5080显卡,金属面板侧板冲细网孔,素雅时尚散热好,360水冷位+9风扇位电竞佳选",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/144290/31/42339/99270/65fb91e9F1efd2088/7fa6fc9e7cffba61.jpg",
"stock": 43,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"formFactor\":\"MATX\",\"frontPanel\":\"细钢网\",\"liquidCooling\":\"360mm\",\"fanSlots\":9,\"usbPort\":\"USB3.0\",\"sidePanel\":\"细网孔铁侧板\",\"gpuLength\":\"5080mm\"}"
},
{
"name": "EVESKY 棱镜MINI海景房电脑机箱",
"brand": "EVESKY",
"model": "棱镜MINI",
"price": 73,
"description": "无立柱台式主机m-atx白色侧透240水冷游戏机箱非暴风雪S920 棱镜海景房-白色【支持M-ATX主板/240水冷】,全景无立柱海景房机箱、双面玻璃侧透!",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/223607/38/43180/364479/663e00b0F84108c21/9d302013bc08d1c2.png",
"stock": 27,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"design\":\"海景房\",\"pillarless\":\"是\",\"formFactor\":\"M-ATX\",\"sidePanel\":\"双面玻璃侧透\",\"liquidCooling\":\"240mm\"}"
},
{
"name": "AOC机箱CG420B黑色",
"brand": "AOC",
"model": "CG420B",
"price": 148.7,
"description": "游戏办公台式电脑侧透主机箱支持ATX主板/兼容360水冷/10风扇位/7槽PCI/兼容5080",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/307927/18/9116/126859/684b7cbaF807982b0/6279dcc0c31d2811.jpg",
"stock": 15,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"sidePanel\":\"侧透\",\"formFactor\":\"ATX\",\"liquidCooling\":\"360mm\",\"fanSlots\":10,\"pciSlots\":7,\"gpuLength\":\"5080mm\"}"
},
{
"name": "爱国者(aigo)A15 黑色 台式电脑主机箱",
"brand": "爱国者(aigo)",
"model": "A15",
"price": 108.78,
"description": "支持ATX主板/USB3.0/左侧透/240冷排/宽体机箱,星璨岚屏显版机箱来袭,6寸高清屏,1480*720高清分辨率,晒单即有礼!",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/124153/36/2293/352919/5ec49e49E8553ba21/7020ba2fa45b34fc.jpg",
"stock": 11,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"formFactor\":\"ATX\",\"usbPort\":\"USB3.0\",\"sidePanel\":\"左侧透\",\"liquidCooling\":\"240mm\",\"bodyType\":\"宽体\",\"displayScreen\":\"6寸高清屏\"}"
},
{
"name": "未知玩家幻翼LCD版 MATX海景房机箱",
"brand": "未知玩家",
"model": "幻翼LCD版",
"price": 697.6,
"description": "360水冷主机外壳 Tpye-C接口",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/312400/37/6475/193510/683eb392F112389da/9e1c481b5857ddd8.jpg",
"stock": 42,
"typeName": "机箱",
"specifications": "{\"design\":\"海景房\",\"formFactor\":\"MATX\",\"liquidCooling\":\"360mm\",\"usbPort\":\"Type-C\",\"displayType\":\"LCD\"}"
},
{
"name": "长城(Great Wall)马里亚纳MT01电脑机箱",
"brand": "长城(Great Wall)",
"model": "马里亚纳MT01",
"price": 158.68,
"description": "E-ATX主板/360水冷位/7风扇位/0.8mm优质侧板/防尘网/5080显卡,高性价比顶置360水冷位机箱,E-ATX大板,面板隐藏式散热",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/97543/7/41166/120362/65210845Fb4630e3e/043bd8128e7f13b8.jpg",
"stock": 10,
"typeName": "机箱",
"specifications": "{\"formFactor\":\"E-ATX\",\"liquidCooling\":\"360mm\",\"fanSlots\":7,\"sidePanelThickness\":\"0.8mm\",\"dustFilter\":\"有\",\"gpuLength\":\"5080mm\",\"coolingDesign\":\"面板隐藏式散热\"}"
},
{
"name": "爱国者(aigo)A15 黑色 台式电脑主机箱",
"brand": "爱国者(aigo)",
"model": "A15",
"price": 108.78,
"description": "支持ATX主板/USB3.0/左侧透/240冷排/宽体机箱,星璨岚屏显版机箱来袭,6寸高清屏,1480*720高清分辨率,晒单即有礼!",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/124153/36/2293/352919/5ec49e49E8553ba21/7020ba2fa45b34fc.jpg",
"stock": 31,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"formFactor\":\"ATX\",\"usbPort\":\"USB3.0\",\"sidePanel\":\"左侧透\",\"liquidCooling\":\"240mm\",\"bodyType\":\"宽体\",\"displayScreen\":\"6寸高清屏\"}"
},
{
"name": "长城(Great Wall)马里亚纳MT01电脑机箱",
"brand": "长城(Great Wall)",
"model": "马里亚纳MT01",
"price": 158.68,
"description": "E-ATX主板/360水冷位/7风扇位/0.8mm优质侧板/防尘网/5080显卡,高性价比顶置360水冷位机箱,E-ATX大板,面板隐藏式散热",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/97543/7/41166/120362/65210845Fb4630e3e/043bd8128e7f13b8.jpg",
"stock": 31,
"typeName": "机箱",
"specifications": "{\"formFactor\":\"E-ATX\",\"liquidCooling\":\"360mm\",\"fanSlots\":7,\"sidePanelThickness\":\"0.8mm\",\"dustFilter\":\"有\",\"gpuLength\":\"5080mm\",\"coolingDesign\":\"面板隐藏式散热\"}"
},
{
"name": "航嘉(Huntkey)V320初恋 全景版海景房机箱",
"brand": "航嘉(Huntkey)",
"model": "V320初恋",
"price": 99,
"description": "台式电脑机箱(双面钢化玻璃/左右分区/240水冷/7风扇位/M-ATX主板 ) V320初恋全景版-黑色",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/234566/28/11262/70267/658ff9bbFd21ace61/11fa9c7c07743ed7.jpg",
"stock": 14,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"design\":\"海景房\",\"sidePanel\":\"双面钢化玻璃\",\"layout\":\"左右分区\",\"liquidCooling\":\"240mm\",\"fanSlots\":7,\"formFactor\":\"M-ATX\"}"
},
{
"name": "爱国者V60 海景房机箱",
"brand": "爱国者",
"model": "V60",
"price": 118,
"description": "全景无立柱MATX高颜值240水冷台式电脑台式机主机箱 白色(显卡限长305mm)",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/80410/17/26715/94744/66b6e2c5F38b04894/e402ec72a1e70bfc.jpg",
"stock": 42,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"design\":\"海景房\",\"pillarless\":\"是\",\"formFactor\":\"MATX\",\"liquidCooling\":\"240mm\",\"gpuLength\":\"305mm\"}"
},
{
"name": "航嘉(Huntkey)S900沙尘暴全景版海景房机箱",
"brand": "航嘉(Huntkey)",
"model": "S900沙尘暴",
"price": 109,
"description": "台式电脑机箱(双面钢化玻璃/左右分区/240水冷/7风扇位/M-ATX主板 ) S900沙尘暴-全视版-白色,【新品机箱上市】M-ATX主板兼容,240冷排,支持4090显卡,7个风扇位,桌面海景房机箱!!",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t20280221/254766/5/23672/55169/67b88d6fFfad84b96/a03d5126d869be3d.jpg",
"stock": 46,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"design\":\"海景房\",\"sidePanel\":\"双面钢化玻璃\",\"layout\":\"左右分区\",\"liquidCooling\":\"240mm\",\"fanSlots\":7,\"formFactor\":\"M-ATX\",\"gpuLength\":\"4090mm\"}"
},
{
"name": "EVESKY 玄武MINI 无立柱海景房机箱",
"brand": "EVESKY",
"model": "玄武MINI",
"price": 69,
"description": "M-ATX主板/270°广角玻璃侧透 240水冷游戏电脑主机箱 支持4090显卡 玄武MINI白色【支持M-ATX主板/240水冷】",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/229596/28/21181/36641/668e5fb3F974555b0/45243653b7d88d9a.jpg",
"stock": 47,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"formFactor\":\"M-ATX\",\"viewAngle\":\"270°\",\"sidePanel\":\"钢化玻璃\",\"liquidCooling\":\"240mm\",\"gpuLength\":\"4090mm\",\"pillarless\":\"是\"}"
},
{
"name": "积至(EVESKY) 泰坦PRO 电脑机箱",
"brand": "积至(EVESKY)",
"model": "泰坦PRO",
"price": 69,
"description": "(侧透/支持ATX/宽体游戏电脑机箱/支持长显卡/背线) 泰坦PRO-黑色,家用/办公精选,侧透简约造型,独立电源包仓,宽体背部走线,多种散热方案,性价比优选!",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/138741/36/44890/87581/66dd8165Fed4b2b55/16bc856daa4e5573.jpg",
"stock": 10,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"sidePanel\":\"侧透\",\"formFactor\":\"ATX\",\"bodyType\":\"宽体\",\"gpuLength\":\"支持长显卡\",\"cableManagement\":\"支持背线\",\"psuShroud\":\"独立电源包仓\"}"
},
{
"name": "玩嘉孤勇者MINI黑色电脑机箱",
"brand": "玩嘉",
"model": "孤勇者MINI",
"price": 100.8,
"description": "台式机MATX海景房240一体水冷全侧透ITX桌面暴风S920",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/75802/27/22521/109554/637f3105Eda2f28a2/43ff63a5e94d2a33.jpg",
"stock": 43,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"formFactor\":\"MATX/ITX\",\"design\":\"海景房\",\"liquidCooling\":\"240mm一体水冷\",\"sidePanel\":\"全侧透\"}"
},
{
"name": "大水牛(BUBALUS) 商悦 中塔商务台式电脑主机机箱",
"brand": "大水牛(BUBALUS)",
"model": "商悦",
"price": 59.9,
"description": "支持ATX主板/支持29cm显卡/背线 商悦-黑色,支持29cm显卡,支持背线,支持ATX主板",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/226677/18/24789/37265/66dd816fFea7f7967/daf11eb3774dbfa9.jpg",
"stock": 31,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"formFactor\":\"中塔/ATX\",\"gpuLength\":\"290mm\",\"cableManagement\":\"支持背线\"}"
},
{
"name": "Thermaltake(Tt)钢影 透EX 海景房机箱",
"brand": "Thermaltake(Tt)",
"model": "钢影 透EX",
"price": 268.46,
"description": "电脑主机 白色(ATX主板/Type-c/支持360水冷/10风扇位/4090显卡),【年度限定ip“瑶小喵”系列新品-机电散全覆盖】【一站式搭配组合,好看又省心】",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/300698/3/16662/143843/685521d4Fac88fd2c/103d18113d6e182f.jpg",
"stock": 27,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"design\":\"海景房\",\"formFactor\":\"ATX\",\"usbPort\":\"Type-C\",\"liquidCooling\":\"360mm\",\"fanSlots\":10,\"gpuLength\":\"4090mm\"}"
},
{
"name": "大水牛(BUBALUS)瑞博 黑色 台式电脑主机中塔机箱",
"brand": "大水牛(BUBALUS)",
"model": "瑞博",
"price": 79.74,
"description": "支持ATX主板/带光驱位/多硬盘兼容/长显卡支持/背部走线,优质拉丝工艺,悬浮式前板,双片式后窗,打造稳固宽敞箱体",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/300916/30/16611/66319/6854d76fF7e70e311/875bc553eca7415f.jpg",
"stock": 42,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"formFactor\":\"中塔/ATX\",\"opticalDriveBay\":\"有\",\"driveBays\":\"多硬盘位\",\"gpuLength\":\"支持长显卡\",\"cableManagement\":\"支持背线\",\"frontPanel\":\"拉丝工艺/悬浮式\"}"
},
{
"name": "爱国者(aigo)JV13黑色 电脑台式主机箱",
"brand": "爱国者(aigo)",
"model": "JV13",
"price": 99.7,
"description": "桌面matx小机箱(透明侧板/ITX主板/240水冷/RGB灯条),星璨岚屏显版机箱来袭,6寸高清屏,1480*720高清分辨率,晒单即有礼!",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/167713/30/39624/43376/64eefadaFa8b8b37e/7cab53b318088710.jpg",
"stock": 42,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"formFactor\":\"MATX/ITX\",\"sidePanel\":\"透明侧板\",\"liquidCooling\":\"240mm\",\"lighting\":\"RGB灯条\",\"displayScreen\":\"6寸高清屏\"}"
},
{
"name": "先马(SAMA)平头哥M2 电脑机箱",
"brand": "先马(SAMA)",
"model": "平头哥M2",
"price": 89.72,
"description": "台式机matx小机箱 玻璃侧透/支持240水冷/背线/USB3.0/独立电源仓/防尘易清洗,支持M-ATX主板,240水冷,下置电源位,背线空间【270°海景房机箱,10风扇位机箱】",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/305734/11/11959/122807/6853c468Fe3491263/b47bdc163de96213.jpg",
"stock": 42,
"typeName": "机箱",
"specifications": "{\"formFactor\":\"MATX\",\"sidePanel\":\"钢化玻璃侧透\",\"liquidCooling\":\"240mm\",\"cableManagement\":\"支持背线\",\"usbPort\":\"USB3.0\",\"psuLocation\":\"下置\",\"dustFilter\":\"防尘易清洗\",\"design\":\"海景房\",\"viewAngle\":\"270°\",\"fanSlots\":10}"
},
{
"name": "玩嘉 风琴黑色电脑机箱",
"brand": "玩嘉",
"model": "风琴",
"price": 91.82,
"description": "台式机matx小机箱 玻璃侧透/支持240水冷/背线/USB3.0/独立电源仓/防尘易清洗",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/101062/25/43328/85363/64e4664dFdb3d98a3/40db9a0f88e26b0d.jpg",
"stock": 11,
"typeName": "机箱",
"specifications": "{\"color\":\"黑色\",\"formFactor\":\"MATX\",\"sidePanel\":\"钢化玻璃侧透\",\"liquidCooling\":\"240mm\",\"cableManagement\":\"支持背线\",\"usbPort\":\"USB3.0\",\"psuShroud\":\"独立电源仓\",\"dustFilter\":\"防尘易清洗\"}"
},
{
"name": "大水牛(BUBALUS)追风M+冰晶L3风扇套装 台式电脑游戏主机侧透机箱",
"brand": "大水牛(BUBALUS)",
"model": "追风M+冰晶L3",
"price": 99.7,
"description": "支持ATX主板/240水冷位/电源仓/背线/U3,【机箱+风扇套装】配3个冰晶L风扇,亚克力侧透不易碎",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/302977/7/16443/95648/6854d767Fe054d47e/741d0eea21331b4e.jpg",
"stock": 26,
"typeName": "机箱",
"specifications": "{\"bundle\":\"机箱+风扇套装\",\"fanModel\":\"冰晶L3\",\"sidePanel\":\"亚克力侧透\",\"formFactor\":\"ATX\",\"liquidCooling\":\"240mm\",\"psuShroud\":\"电源仓\",\"cableManagement\":\"支持背线\",\"usbPort\":\"USB3.0\"}"
},
{
"name": "爱国者(aigo)星璨岚 小岚白色 游戏电脑台式主机箱",
"brand": "爱国者(aigo)",
"model": "星璨岚 小岚",
"price": 198.6,
"description": "垂直风道风冷/M-ATX主板/240水冷/270°海景房/5090显卡,星璨岚屏显版机箱来袭,6寸高清屏,1480*720高清分辨率,晒单即有礼!",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/90316/14/40395/109679/6540d89bFabf6da14/cb9c970edd6f2aa9.jpg",
"stock": 10,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"coolingDesign\":\"垂直风道风冷\",\"formFactor\":\"M-ATX\",\"liquidCooling\":\"240mm\",\"viewAngle\":\"270°\",\"design\":\"海景房\",\"gpuLength\":\"5090mm\",\"displayScreen\":\"6寸高清屏\"}"
},
{
"name": "爱国者(aigo)A15 白色 台式电脑主机箱",
"brand": "爱国者(aigo)",
"model": "A15",
"price": 118.76,
"description": "支持ATX主板/USB3.0/左侧透/240冷排/宽体机箱,星璨岚屏显版机箱来袭,6寸高清屏,1480*720高清分辨率,晒单即有礼!",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/125106/36/35548/86257/64b6604dF0b15b573/2a7f726338585998.jpg",
"stock": 15,
"typeName": "机箱",
"specifications": "{\"color\":\"白色\",\"formFactor\":\"ATX\",\"usbPort\":\"USB3.0\",\"sidePanel\":\"左侧透\",\"liquidCooling\":\"240mm\",\"bodyType\":\"宽体\",\"displayScreen\":\"6寸高清屏\"}"
}
]
+475
View File
@@ -0,0 +1,475 @@
[
{
"name": "ROG STRIX X870E-E GAMING WIFI主板",
"brand": "ROG",
"model": "STRIX X870E-E GAMING WIFI",
"price": 4010.96,
"description": "支持 CPU 9900X3D/9950X3D/9800X3D (AMD X870E/socket AM5)",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/109669/39/51302/152827/6709d073Fd69cb470/d5b9fb001b9c1c52.png",
"stock": 47,
"typeName": "主板",
"specifications": "{\"chipset\":\"AMD X870E\",\"socket\":\"AM5\",\"memoryType\":\"DDR5\",\"wifi\":\"WiFi 6E\"}"
},
{
"name": "华硕TX GAMING B760M WIFI D4 天选主板",
"brand": "华硕",
"model": "TX GAMING B760M WIFI D4 天选",
"price": 1196.6,
"description": "支持 CPU 13700K/13600KF/13400F(Intel B760/LGA 1700),扫码注册,一年换新,三年上门服务,白条6期免息,12+1供电模组,WIFI6+2.5G网卡,双向AI降噪。",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/209757/32/29594/161779/63fd62c4F9f5d4bf9/ca0d22d42c3b6848.jpg",
"stock": 26,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\",\"powerPhase\":\"12+1\",\"network\":\"WIFI6+2.5G网卡\"}"
},
{
"name": "华硕(ASUS)B760M-AYW WIFI D4 II 哎呦喂主板",
"brand": "华硕(ASUS)",
"model": "B760M-AYW WIFI D4 II 哎呦喂",
"price": 887.22,
"description": "支持 CPU 13600KF/13400F12600KF(Intel B760/LGA 1700)",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/313407/31/510/122533/68232bb7F0686dde7/200f3673b0147fec.jpg",
"stock": 31,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\",\"wifi\":\"WIFI\"}"
},
{
"name": "华硕(ASUS)TUF GAMING B760M-PLUS WIFI II重炮手二代 DDR5主板",
"brand": "华硕(ASUS)",
"model": "TUF GAMING B760M-PLUS WIFI II 重炮手二代",
"price": 1316.36,
"description": "支持 CPU 14600KF/14700KF(Intel B760/LGA 1700),扫码注册,一年换新,三年上门服务,白条6期免息,12+1+1供电模组,显卡易拆键,WIFI6E+易拆式天线。",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/233680/30/12928/126467/65aa1906F72d5c545/8f5efa97e28b6406.jpg",
"stock": 11,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR5\",\"powerPhase\":\"12+1+1\",\"network\":\"WIFI6E\"}"
},
{
"name": "技嘉(GIGABYTE)小雕Z890 AORUS ELITE WIFI7 DDR5主板",
"brand": "技嘉(GIGABYTE)",
"model": "Z890 AORUS ELITE WIFI7",
"price": 2394.2,
"description": "支持CPU Ultra U9-285K U7-265K LGA 1851",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/273561/7/16490/166950/67f33bafFcff26f8f/c0689277b3a9a14e.jpg",
"stock": 43,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel Z890\",\"socket\":\"LGA 1851\",\"memoryType\":\"DDR5\",\"wifi\":\"WiFi 7\"}"
},
{
"name": "华硕TUF GAMING B850M-PLUS WIFI重炮手主板",
"brand": "华硕",
"model": "TUF GAMING B850M-PLUS WIFI 重炮手",
"price": 1416.16,
"description": "支持 CPU 7800X3D/9800X3D/9600X(AMD B850/socket AM5),扫码注册,一年换新,三年上门服务,白条6期免息,14+2+1供电模组,3*M.2接口,WIFI6E&2.5G网卡。",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/259603/21/8698/100403/677b4cfaFa69fd526/23fdc1c33c263212.png",
"stock": 31,
"typeName": "主板",
"specifications": "{\"chipset\":\"AMD B850\",\"socket\":\"AM5\",\"powerPhase\":\"14+2+1\",\"storage\":\"3*M.2\",\"network\":\"WIFI6E&2.5G网卡\"}"
},
{
"name": "华硕(ASUS)TX GAMING B760M WIFI 天选主板",
"brand": "华硕(ASUS)",
"model": "TX GAMING B760M WIFI 天选",
"price": 1296.4,
"description": "支持DDR5 CPU 13700K/13600KF/13400F(Intel B760/LGA 1700),扫码注册,一年换新,三年上门服务,白条6期免息,12+1供电模组,支持DDR5,双PCIe4.0·M.2插槽。",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/158653/36/37462/145304/644104e2F3eddeda6/f3a6d98ae262b228.jpg",
"stock": 10,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR5\",\"powerPhase\":\"12+1\",\"storage\":\"双PCIe4.0·M.2\"}"
},
{
"name": "微星(MSI)B760M BOMBER DDR4爆破弹电脑主板",
"brand": "微星(MSI)",
"model": "B760M BOMBER DDR4 爆破弹",
"price": 677.64,
"description": "支持CPU 12600KF/14400F/13490F/13400F (INTEL B760/LGA 1700)",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/297061/11/14774/163743/684bd41dFe85c3168/fffba768d543b3e8.jpg",
"stock": 58,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\"}"
},
{
"name": "微星(MSI)B650M GAMING WIFI主板",
"brand": "微星(MSI)",
"model": "B650M GAMING WIFI",
"price": 877.24,
"description": "支持CPU 7800X3D/9600X/9700X/7500F (AMD B650/AM5接口)",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/291814/6/12019/137871/6852571aFbf2fff3c/7639ddf25de5d1ff.jpg",
"stock": 47,
"typeName": "主板",
"specifications": "{\"chipset\":\"AMD B650\",\"socket\":\"AM5\",\"wifi\":\"WIFI\"}"
},
{
"name": "微星(MSI)MAG B760M MORTAR WIFI II DDR5迫击炮主板",
"brand": "微星(MSI)",
"model": "MAG B760M MORTAR WIFI II 迫击炮",
"price": 1196.6,
"description": "支持CPU14600K/14600KF/14700KF (Intel B760/LGA 1700)",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/317182/12/8852/137359/684bd20fF3c633368/1cff09a8ae63cdd0.png",
"stock": 47,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR5\",\"wifi\":\"WIFI\"}"
},
{
"name": "七彩虹(Colorful)BATTLE-AX B760M-WHITE WIFI V20 DDR4 冷钢主板",
"brand": "七彩虹(Colorful)",
"model": "BATTLE-AX B760M-WHITE WIFI V20 冷钢",
"price": 727.54,
"description": "支持 12600KF/13490F/14600KF(Intel B760/LGA 1700)",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/211190/24/37027/126153/65338602Fc10d08e4/aa603865c7ee5874.jpg",
"stock": 42,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\",\"wifi\":\"WIFI\"}"
},
{
"name": "微星(MSI)MAG B850M MORTAR WIFI 迫击炮主板",
"brand": "微星(MSI)",
"model": "MAG B850M MORTAR WIFI 迫击炮",
"price": 1496,
"description": "支持CPU AMD 9950X/9800X3D/9700X/9600X(AMD B850/AM5接口)",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/317646/37/8894/136025/684bf1fdFc451a57a/6b0f2a628e4c3350.jpg",
"stock": 43,
"typeName": "主板",
"specifications": "{\"chipset\":\"AMD B850\",\"socket\":\"AM5\",\"wifi\":\"WIFI\"}"
},
{
"name": "ROG STRIX X870-A GAMING WIFI主板+AMD 锐龙7 9800X3D CPU 主板+CPU套装",
"brand": "ROG",
"model": "STRIX X870-A GAMING WIFI",
"price": 6698,
"description": "ROG STRIX X870-A GAMING WIFI主板与AMD 锐龙7 9800X3D CPU的套装",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/221684/23/40781/154649/672350a0F34fad613/dcd914ea252df80e.jpg",
"stock": 30,
"typeName": "主板",
"specifications": "{\"chipset\":\"AMD X870\",\"socket\":\"AM5\",\"cpuIncluded\":\"AMD Ryzen 7 9800X3D\",\"wifi\":\"WIFI\"}"
},
{
"name": "华硕B760M-AYW WIFI D4 哎呦喂主板",
"brand": "华硕",
"model": "B760M-AYW WIFI D4 哎呦喂",
"price": 867.26,
"description": "支持 CPU 13600KF/13400F(Intel B760/LGA 1700),扫码注册,一年换新,三年上门服务,白条6期免息,8供电模组,2个M.2插槽,M,2便捷卡扣。",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/201173/9/30199/128829/64535d52F55d69b18/a2c7e2afc80ca3b7.jpg",
"stock": 14,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\",\"powerPhase\":\"8\",\"storage\":\"2*M.2\"}"
},
{
"name": "华硕B650M-AYW WIFI 哎呦喂主板",
"brand": "华硕",
"model": "B650M-AYW WIFI 哎呦喂",
"price": 897.2,
"description": "支持DDR5 CPU 7700X/7600X/7500F (AMD B650/socket AM5),扫码注册,一年换新,三年上门服务,白条6期免息,WIFI6&2.5G网卡,M.2便捷卡扣,双向AI降噪",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/263491/18/7470/81805/6777ac63F52a6ba13/eba8d1a9b824e07c.png",
"stock": 15,
"typeName": "主板",
"specifications": "{\"chipset\":\"AMD B650\",\"socket\":\"AM5\",\"memoryType\":\"DDR5\",\"network\":\"WIFI6&2.5G网卡\"}"
},
{
"name": "七彩虹B760M D5 冷钢战斧",
"brand": "七彩虹",
"model": "B760M-WHITE WIFI D4 冷钢战斧",
"price": 704.44,
"description": "支持英特尔 酷睿12代 13代 14代CPU 12600KF/12490F/13600KF/14600K,又便宜又好,撩客服MM、享更多优惠~七彩虹助力您生产力提升!游戏畅玩不卡顿~",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/293105/25/13583/676121/685672efFbc763f65/1c6e395d98c5833d.png",
"stock": 47,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\",\"wifi\":\"WIFI\"}"
},
{
"name": "长城 GREAT WALL新款H610M-A主板+i5-12400F+4管祥龙散热器套装",
"brand": "长城 GREAT WALL",
"model": "H610M-A",
"price": 1108,
"description": "台式机电脑1700针12代酷睿CPU处理器i3-12100F/i5-12400F套装搭长城散热器双DDR4 H610M-A+i5-12400F+4管祥龙",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/254668/11/27191/178257/67c4183aF8206334b/14a411c46121dde4.jpg",
"stock": 11,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel H610\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\",\"cpuIncluded\":\"Intel Core i5-12400F\"}"
},
{
"name": "华南金牌H610M-K 单主板",
"brand": "华南金牌",
"model": "H610M-K",
"price": 299,
"description": "华南金牌H610/B760M主板CPU套装搭英特尔i5 12400F/13400F/13490F/i3 12100F板U套装黑神话悟空游戏电脑,3/6期免息/全新3年质保,H610主板,B760主板,LGA1700酷睿12100F套装,12400F套装,12600K等套装~",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/265962/23/3352/149414/676cc693Fee3d33a2/5364e7d522d922ea.jpg",
"stock": 14,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel H610\",\"socket\":\"LGA 1700\"}"
},
{
"name": "技嘉/华硕H61 M小板 二手主板",
"brand": "技嘉/华硕/鲲鹄",
"model": "H61 M小板",
"price": 65,
"description": "技嘉/华硕/鲲鹄主板H61/B85/ H81 3/4/7/9/10代英特尔CPU 台式机主板二手主板 8成新",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/107613/39/48531/98161/66c5a414F11ebf38c/b2d7a1d5327cdf4f.jpg",
"stock": 46,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel H61\",\"socket\":\"LGA 1155\",\"condition\":\"二手\"}"
},
{
"name": "微星(MSI)A520M-A PRO DDR4电脑主板",
"brand": "微星(MSI)",
"model": "A520M-A PRO",
"price": 378.24,
"description": "支持CPU 5600/5600GT/5700X(AMD A520/AM4接口)",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/302402/38/13853/163308/68525b2aF3d6566e5/9645e6000616c926.jpg",
"stock": 43,
"typeName": "主板",
"specifications": "{\"chipset\":\"AMD A520\",\"socket\":\"AM4\",\"memoryType\":\"DDR4\"}"
},
{
"name": "色技师 H470-VH台式机电脑主板",
"brand": "色技师",
"model": "H470-VH",
"price": 299,
"description": "H470台式机电脑主板",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/321363/24/7465/141213/684698d2Fee7c4f90/445a1f4d055eeb60.jpg",
"stock": 31,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel H470\"}"
},
{
"name": "铭瑄(MAXSUN)终结者 B850M PRO WIFI电脑主板",
"brand": "铭瑄(MAXSUN)",
"model": "终结者 B850M PRO WIFI",
"price": 897.1,
"description": "支持 CPU AMD 9950X/9800X3D/9700X/9600X/7800X3D(AMD B850/AM5接口)",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/303106/18/13979/106778/684a912aF4bcaa02b/7edb7d77c8222637.jpg",
"stock": 31,
"typeName": "主板",
"specifications": "{\"chipset\":\"AMD B850\",\"socket\":\"AM5\",\"wifi\":\"WIFI\"}"
},
{
"name": "ROG STRIX Z790-A GAMING WIFI S 吹雪主板",
"brand": "ROG",
"model": "STRIX Z790-A GAMING WIFI S 吹雪",
"price": 2813.36,
"description": "支持DDR5 CPU 14900K/14700K(Intel Z790/LGA 1700),扫码注册,一年换新,三年上门服务,白条6期免息,16+1+2供电模组,2.5G网卡&WIFI7,AI智能超频。",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/163508/26/39623/183736/677a2b63F49738128/28726f5e450b2c5c.jpg",
"stock": 27,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel Z790\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR5\",\"powerPhase\":\"16+1+2\",\"network\":\"2.5G网卡&WIFI7\"}"
},
{
"name": "长城H610M-A(单主板 国货之光)",
"brand": "长城 GREAT WALL",
"model": "H610M-A",
"price": 348,
"description": "新款H610M-A主板台式机电脑1700针12代酷睿CPU处理器i3-12100F/i5-12400F套装搭长城散热器双DDR4",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/256309/26/28111/201703/67c41831F652b5ebc/ff9a97953dc12cdd.jpg",
"stock": 27,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel H610\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\"}"
},
{
"name": "ROG STRIX X870-A GAMING WIFI吹雪主板",
"brand": "ROG",
"model": "STRIX X870-A GAMING WIFI 吹雪",
"price": 2913.16,
"description": "支持 CPU 9900X3D/9950X3D/9800X3D (AMD X870/socket AM5)",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/285128/40/18062/187050/67f8eab9F45b36274/0f8e08dfed139bea.png",
"stock": 43,
"typeName": "主板",
"specifications": "{\"chipset\":\"AMD X870\",\"socket\":\"AM5\",\"wifi\":\"WIFI\"}"
},
{
"name": "华硕TX B760M WIFI D4天选 I5 14600KF盒装主板CPU套装",
"brand": "英特尔(Intel)",
"model": "TX B760M WIFI D4 天选",
"price": 2099,
"description": "英特尔 14代i5 主板CPU套装,华硕TX B760M WIFI D4天选,I5 14600KF盒装",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/267390/25/20811/120689/67b2a1a9F014903be/0fff2bf76257117d.jpg",
"stock": 11,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"cpuIncluded\":\"Intel Core i5-14600KF\",\"memoryType\":\"DDR4\",\"wifi\":\"WIFI\"}"
},
{
"name": "七彩虹战斧B760M-P PRO D4主板",
"brand": "七彩虹",
"model": "B760M-P PRO D4 战斧",
"price": 540,
"description": "支持英特尔12600KF 14600KF 12/13/14代CPU D4 D5 游戏家用台式机电脑主板,暑期放假抢满400-10,满400减50,战斧B760M-PPROV20官方补贴到手价538元",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/174191/16/43871/162239/666190a4Fca201d94/c63261abbc2c1928.jpg",
"stock": 30,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\"}"
},
{
"name": "华硕天选B760M WIFI 单主板无CPU",
"brand": "华硕",
"model": "天选B760M WIFI",
"price": 1099,
"description": "华硕H610M/B760M D4/D5天选/吹雪/TUF 台式机电脑主板CPU套装英特尔i5,D4版本单主板无CPU",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/230238/23/26750/183668/66cd6236F121c000c/9e3db2ea88982aed.jpg",
"stock": 10,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\",\"wifi\":\"WIFI\"}"
},
{
"name": "华南金牌B760M-K 搭I5-13400F散片主板CPU套装",
"brand": "华南金牌",
"model": "B760M-K",
"price": 1098,
"description": "华南金牌H610/B760M主板CPU套装搭英特尔i5 12400F/13400F/13490F/i3 12100F板U套装黑神话悟空游戏电脑,3/6期免息/全新3年质保,H610主板,B760主板,LGA1700酷睿12100F套装,12400F套装,12600K等套装~",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/254540/35/4396/110221/676cc6baF6fc01aad/870f5b0125817d9c.jpg",
"stock": 46,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"cpuIncluded\":\"Intel Core i5-13400F\"}"
},
{
"name": "七彩虹(Colorful)H610M-E-WIFI D4【战斧】主板",
"brand": "七彩虹(Colorful)",
"model": "H610M-E-WIFI D4 战斧",
"price": 439,
"description": "H610M 主板台式机电脑支持酷睿12代 13代CPU 12400/12490F游戏家用台式机电脑主板",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/317745/27/10586/200762/68560d2bFdd72d55e/2dc5e4ca47d0852d.jpg",
"stock": 42,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel H610\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\",\"wifi\":\"WIFI\"}"
},
{
"name": "华硕天选B760M WIFI 单主板无CPU",
"brand": "华硕",
"model": "天选B760M WIFI",
"price": 1099,
"description": "华硕H610M/B760M D4/D5天选/吹雪/TUF 台式机电脑主板CPU套装英特尔i5,D4版本单主板无CPU",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/230238/23/26750/183668/66cd6236F121c000c/9e3db2ea88982aed.jpg",
"stock": 27,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\",\"wifi\":\"WIFI\"}"
},
{
"name": "ROG STRIX X870E-E GAMING WIFI主板",
"brand": "ROG",
"model": "STRIX X870E-E GAMING WIFI",
"price": 4010.96,
"description": "支持 CPU 9900X3D/9950X3D/9800X3D (AMD X870E/socket AM5)",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/109669/39/51302/152827/6709d073Fd69cb470/d5b9fb001b9c1c52.png",
"stock": 27,
"typeName": "主板",
"specifications": "{\"chipset\":\"AMD X870E\",\"socket\":\"AM5\",\"memoryType\":\"DDR5\",\"wifi\":\"WiFi 6E\"}"
},
{
"name": "i5-4590+技嘉/华硕B85M 小板二手主板CPU套装",
"brand": "技嘉/华硕/鲲鹄",
"model": "B85M 小板",
"price": 245,
"description": "技嘉/华硕/鲲鹄主板H61/B85/ H81 3/4/7/9/10代英特尔CPU 台式机主板二手主板,搭配i5-4590处理器",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/247642/23/16871/96414/66c5a46eF37a9bdec/1cf26002c64dbf18.jpg",
"stock": 47,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B85\",\"socket\":\"LGA 1150\",\"cpuIncluded\":\"Intel Core i5-4590\",\"condition\":\"二手\"}"
},
{
"name": "七彩虹(Colorful)BATTLE-AX B760M-G WIFI V20A 小黑刃DDR4主板",
"brand": "七彩虹(Colorful)",
"model": "BATTLE-AX B760M-G WIFI V20A 小黑刃",
"price": 697.6,
"description": "支持14600K/14600KF(Intel B760/LGA 1700)",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/317275/12/10148/180527/6853c72fFbc4adf2b/4851ec2210920337.png",
"stock": 11,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\",\"wifi\":\"WIFI\"}"
},
{
"name": "华硕B250 M小板二手主板",
"brand": "技嘉/华硕/鲲鹄",
"model": "B250 M小板",
"price": 153,
"description": "技嘉/华硕/鲲鹄主板H61/B85/ H81 3/4/7/9/10代英特尔CPU 台式机主板二手主板 9成新",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/277016/7/14229/192585/67eb873cFb7cc8f9c/4ad2561d5f5a6a61.jpg",
"stock": 27,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B250\",\"socket\":\"LGA 1151\",\"condition\":\"二手\"}"
},
{
"name": "技嘉(GIGABYTE)小雕Z890 AORUS ELITE WIFI7 DDR5主板",
"brand": "技嘉(GIGABYTE)",
"model": "Z890 AORUS ELITE WIFI7",
"price": 2394.2,
"description": "支持CPU Ultra U9-285K U7-265K LGA 1851",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/273561/7/16490/166950/67f33bafFcff26f8f/c0689277b3a9a14e.jpg",
"stock": 42,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel Z890\",\"socket\":\"LGA 1851\",\"memoryType\":\"DDR5\",\"wifi\":\"WiFi 7\"}"
},
{
"name": "精粤H610M-D PLUS主板",
"brand": "精粤",
"model": "H610M-D PLUS",
"price": 328.34,
"description": "1700针DDR4内存电脑游戏主板I3 12100F/12400F/13400F Intel H610主板/LGA 1700",
"imageUrl": "https://img10.360buyimg.com/n7/jfs/t1/280515/30/19640/146525/67fdf8aeFb654baaf/00eaa3147ef86a4e.png",
"stock": 26,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel H610\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\"}"
},
{
"name": "七彩虹(Colorful)BATTLE-AX H610M-E WIFI V21 游戏主板",
"brand": "七彩虹(Colorful)",
"model": "BATTLE-AX H610M-E WIFI V21",
"price": 478.04,
"description": "支持12400F/13400/14400F (Intel H610/LGA 1700)",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/154562/29/46497/163500/672c5ab1F5450a7c6/45611b3673672fc7.png",
"stock": 10,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel H610\",\"socket\":\"LGA 1700\",\"wifi\":\"WIFI\"}"
},
{
"name": "技嘉(GIGABYTE)H610M K DDR4主板",
"brand": "技嘉(GIGABYTE)",
"model": "H610M K",
"price": 448.1,
"description": "支持CPU12代酷睿12400F(Intel H610/LGA 1700),注册1年换新4年保修,内存超频支持3200/M.2存存储插槽/HDMI",
"imageUrl": "https://img13.360buyimg.com/n7/jfs/t1/234529/30/15172/166954/65f7e7fbF59bc0107/f72a38debd61707c.jpg",
"stock": 26,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel H610\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\",\"memorySpeed\":\"3200MHz\",\"storage\":\"M.2\"}"
},
{
"name": "长城H610M-A(单主板 国货之光)",
"brand": "长城 GREAT WALL",
"model": "H610M-A",
"price": 348,
"description": "官方新款H610M-A电脑台式主板12代CPU套装1700针i5-12400f板u搭长城散热器游戏多开12100f板u套装",
"imageUrl": "https://img14.360buyimg.com/n7/jfs/t1/266170/14/26636/189006/67c41717Fdf0a0078/cd2b1657a3af5dc6.jpg",
"stock": 27,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel H610\",\"socket\":\"LGA 1700\"}"
},
{
"name": "ROG STRIX B760-G GAMING WIFI S小吹雪S主板",
"brand": "ROG",
"model": "STRIX B760-G GAMING WIFI S 小吹雪S",
"price": 1515.96,
"description": "支持DDR5 CPU 14700K/14600KF(Intel B760/LGA 1700),扫码注册,一年换新,三年上门服务,白条6期免息,12+1+1供电模组,AI智能优化,易拆式天线。",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/95258/2/42982/188564/65d46c66F42266a3c/c6605225a9fd88c7.jpg",
"stock": 11,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR5\",\"powerPhase\":\"12+1+1\"}"
},
{
"name": "微星MAG B460M MORTAR迫击炮二手主板",
"brand": "微星",
"model": "MAG B460M MORTAR 迫击炮",
"price": 249,
"description": "微星 华硕 技嘉七彩虹B460 Z490 B560 Z590二手主板支持Intel10 11代",
"imageUrl": "https://img12.360buyimg.com/n7/jfs/t1/151535/7/42568/144113/65f7a37fFd86d0a83/be92115118549f7e.png",
"stock": 26,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B460\",\"socket\":\"LGA 1200\",\"condition\":\"二手\"}"
},
{
"name": "技嘉(GIGABYTE)B760M H DDR4主板",
"brand": "技嘉(GIGABYTE)",
"model": "B760M H",
"price": 577.84,
"description": "支持CPU 13600KF 12600KF Intel LGA 1700",
"imageUrl": "https://img11.360buyimg.com/n7/jfs/t1/88433/20/46603/244125/6715fb1cF779ef2c8/01f98f63bf37d3c7.jpg",
"stock": 10,
"typeName": "主板",
"specifications": "{\"chipset\":\"Intel B760\",\"socket\":\"LGA 1700\",\"memoryType\":\"DDR4\"}"
}
]
+34
View File
@@ -0,0 +1,34 @@
let result = [];
let richResult = []
$0.querySelectorAll("li").forEach((ele, index) => {
let price = parseFloat(ele.querySelector("strong").textContent.trim().slice(1));
let title = ele.querySelector(".p-name").textContent.trim();
let img = ele.querySelector("img").src;
result.push([index, title, price]);
richResult.push({
index,
title: title,
price: price,
img: img
});
})
console.log(JSON.stringify(result, null, 2));
let data = []
let res = data.map(item => {
return {
name: item.name,
brand: item.brand,
model: item.model,
price: item.price,
description: item.description,
imageUrl: richResult.find(item_ => item_.index == item.index).img,
stock: Math.random() * 50 | 0 + 10,
typeName: item.typeName,
specifications: item.specifications
};
}).filter(item => item.imageUrl).filter(item => item.typeName == "主板")
console.log(JSON.stringify(res));
+2
View File
@@ -39,3 +39,5 @@ yarn-error.log*
# typescript # typescript
*.tsbuildinfo *.tsbuildinfo
next-env.d.ts next-env.d.ts
/app/generated/prisma
+727
View File
@@ -0,0 +1,727 @@
'use client'
import { useState, useEffect } from 'react'
import { withAdminAuth } from '@/components/admin/AdminAuth'
import { Plus, Edit, Trash2, Search, Upload, Download, FileText } from 'lucide-react'
interface Component {
id: string
name: string
brand: string
model: string
price: number
description?: string
imageUrl?: string
stock: number
specifications?: string
componentType: {
id: string
name: string
}
}
interface ComponentType {
id: string
name: string
}
function AdminComponentsPage() {
const [components, setComponents] = useState<Component[]>([])
const [componentTypes, setComponentTypes] = useState<ComponentType[]>([])
const [isLoading, setIsLoading] = useState(true)
const [showModal, setShowModal] = useState(false)
const [showBatchModal, setShowBatchModal] = useState(false)
const [editingComponent, setEditingComponent] = useState<Component | null>(null)
const [searchTerm, setSearchTerm] = useState('')
const [batchFile, setBatchFile] = useState<File | null>(null)
const [batchData, setBatchData] = useState<any[]>([])
const [batchImportLoading, setBatchImportLoading] = useState(false)
const [validationErrors, setValidationErrors] = useState<string[]>([])
const [validData, setValidData] = useState<any[]>([])
const [invalidData, setInvalidData] = useState<any[]>([])
const [formData, setFormData] = useState({
name: '',
brand: '',
model: '',
price: '',
description: '',
imageUrl: '',
stock: '',
componentTypeId: '',
specifications: ''
})
useEffect(() => {
loadData()
}, [])
const loadData = async () => {
try {
const [componentsRes, typesRes] = await Promise.all([
fetch('/api/components?limit=100'),
fetch('/api/component-types')
])
if (componentsRes.ok) {
const data = await componentsRes.json()
setComponents(data.components)
}
if (typesRes.ok) {
const types = await typesRes.json()
setComponentTypes(types)
}
} catch (error) {
console.error('加载数据失败:', error)
} finally {
setIsLoading(false)
}
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
try {
const url = editingComponent
? `/api/components/${editingComponent.id}`
: '/api/components'
const method = editingComponent ? 'PUT' : 'POST'
const response = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
})
if (response.ok) {
await loadData()
setShowModal(false)
resetForm()
} else {
const data = await response.json()
alert(data.message || '操作失败')
}
} catch (error) {
console.error('提交失败:', error)
alert('操作失败')
}
}
const handleDelete = async (id: string) => {
if (!confirm('确定要删除这个配件吗?')) return
try {
const response = await fetch(`/api/components/${id}`, {
method: 'DELETE',
})
if (response.ok) {
await loadData()
} else {
alert('删除失败')
}
} catch (error) {
console.error('删除失败:', error)
alert('删除失败')
}
}
const handleEdit = (component: Component) => {
setEditingComponent(component)
setFormData({
name: component.name,
brand: component.brand,
model: component.model,
price: component.price.toString(),
description: component.description || '',
imageUrl: component.imageUrl || '',
stock: component.stock.toString(),
componentTypeId: component.componentType.id,
specifications: component.specifications || ''
})
setShowModal(true)
}
const resetForm = () => {
setFormData({
name: '',
brand: '',
model: '',
price: '',
description: '',
imageUrl: '',
stock: '',
componentTypeId: '',
specifications: ''
})
setEditingComponent(null)
}
// 批量导入相关函数
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (file) {
setBatchFile(file)
parseFile(file)
}
}
const parseFile = async (file: File) => {
const text = await file.text()
try {
let data: any[] = []
if (file.name.endsWith('.json')) {
const parsed = JSON.parse(text)
data = Array.isArray(parsed) ? parsed : [parsed]
} else if (file.name.endsWith('.csv')) {
const lines = text.split('\n')
if (lines.length < 2) {
alert('CSV文件格式错误')
return
}
const headers = lines[0].split(',').map(h => h.trim())
data = lines.slice(1)
.filter(line => line.trim())
.map(line => {
const values = line.split(',').map(v => v.trim().replace(/^"|"$/g, ''))
const obj: any = {}
headers.forEach((header, index) => {
obj[header] = values[index] || ''
})
return obj
})
}
setBatchData(data)
validateBatchData(data)
} catch (error) {
alert('文件解析失败,请检查文件格式')
console.error('File parse error:', error)
}
}
const validateBatchData = (data: any[]) => {
const errors: string[] = []
const valid: any[] = []
const invalid: any[] = []
data.forEach((item, index) => {
const itemErrors: string[] = []
// 验证必需字段
if (!item.name || typeof item.name !== 'string' || item.name.trim() === '') {
itemErrors.push('缺少商品名称')
}
if (!item.brand || typeof item.brand !== 'string' || item.brand.trim() === '') {
itemErrors.push('缺少品牌')
}
if (!item.model || typeof item.model !== 'string' || item.model.trim() === '') {
itemErrors.push('缺少型号')
}
// 验证价格
const price = typeof item.price === 'string' ? parseFloat(item.price) : item.price
if (isNaN(price) || price < 0) {
itemErrors.push('价格必须是有效的正数')
}
// 验证库存
const stock = typeof item.stock === 'string' ? parseInt(item.stock) : item.stock
if (isNaN(stock) || stock < 0) {
itemErrors.push('库存必须是有效的非负整数')
}
// 验证配件类型
const hasTypeId = item.componentTypeId && item.componentTypeId.trim() !== ''
const hasTypeName = (item.typeName || item.type) && (item.typeName || item.type).trim() !== ''
if (!hasTypeId && !hasTypeName) {
itemErrors.push('缺少配件类型信息 (componentTypeId, typeName 或 type)')
}
if (itemErrors.length > 0) {
invalid.push({ ...item, _errors: itemErrors, _index: index + 1 })
errors.push(`第${index + 1}行: ${itemErrors.join(', ')}`)
} else {
valid.push(item)
}
})
setValidationErrors(errors)
setValidData(valid)
setInvalidData(invalid)
}
const handleBatchImport = async () => {
if (validData.length === 0) {
alert('没有有效的数据可导入')
return
}
if (invalidData.length > 0) {
const proceed = confirm(`发现${invalidData.length}行无效数据,是否只导入${validData.length}行有效数据?`)
if (!proceed) return
}
setBatchImportLoading(true)
try {
const response = await fetch('/api/components/batch', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
components: validData
}),
})
const result = await response.json()
if (response.ok && result.success) {
const { summary, results } = result
// 显示详细的导入结果
const failedItems = results.filter((r: any) => !r.success)
let message = `导入完成!\n成功: ${summary.successful}\n失败: ${summary.failed}`
if (summary.newTypesCreated > 0) {
message += `\n新创建配件类型: ${summary.newTypesCreated}`
}
if (failedItems.length > 0 && failedItems.length <= 5) {
message += '\n\n失败项目:'
failedItems.forEach((item: any, index: number) => {
message += `\n${index + 1}. ${item.item.name || '未知'}: ${item.error}`
})
} else if (failedItems.length > 5) {
message += `\n\n失败项目过多,请检查数据格式或查看控制台详情`
console.log('批量导入失败项目:', failedItems)
}
alert(message)
if (summary.successful > 0) {
await loadData()
setShowBatchModal(false)
setBatchFile(null)
setBatchData([])
setValidData([])
setInvalidData([])
setValidationErrors([])
}
} else {
alert(`批量导入失败: ${result.message || '未知错误'}`)
console.error('Batch import error:', result)
}
} catch (error) {
alert('批量导入失败:网络或服务器错误')
console.error('Batch import error:', error)
} finally {
setBatchImportLoading(false)
}
}
const downloadTemplate = (format: 'csv' | 'json') => {
const sampleData = [
{
name: 'Intel Core i5-13400F',
brand: 'Intel',
model: 'i5-13400F',
price: 1299,
description: '10核16线程,基础频率2.5GHz,最大睿频4.6GHz',
imageUrl: '',
stock: 50,
typeName: 'CPU',
specifications: '{"cores":10,"threads":16,"baseClock":"2.5GHz","boostClock":"4.6GHz","socket":"LGA1700"}'
}
]
if (format === 'csv') {
const headers = ['name', 'brand', 'model', 'price', 'description', 'imageUrl', 'stock', 'typeName', 'specifications']
const csvContent = [
headers.join(','),
...sampleData.map(item => headers.map(header => `"${item[header as keyof typeof item] || ''}"`).join(','))
].join('\n')
const blob = new Blob([csvContent], { type: 'text/csv' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'components_template.csv'
a.click()
URL.revokeObjectURL(url)
} else {
const jsonContent = JSON.stringify(sampleData, null, 2)
const blob = new Blob([jsonContent], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'components_template.json'
a.click()
URL.revokeObjectURL(url)
}
}
const filteredComponents = components.filter(component =>
component.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
component.brand.toLowerCase().includes(searchTerm.toLowerCase()) ||
component.model.toLowerCase().includes(searchTerm.toLowerCase())
)
if (isLoading) {
return (
<div className="flex justify-center items-center py-12">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
)
}
return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <div className="flex justify-between items-center mb-6">
<h1 className="text-3xl font-bold text-gray-900">配件管理</h1>
<div className="flex gap-3">
<button
onClick={() => setShowBatchModal(true)}
className="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 flex items-center gap-2"
>
<Upload className="h-5 w-5" />
批量导入
</button>
<button
onClick={() => {
resetForm()
setShowModal(true)
}}
className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 flex items-center gap-2"
>
<Plus className="h-5 w-5" />
添加配件
</button>
</div>
</div>
{/* Search */}
<div className="mb-6">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-5 w-5" />
<input
type="text"
placeholder="搜索配件..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"
/>
</div>
</div>
{/* Components Table */}
<div className="bg-white shadow-md rounded-lg overflow-hidden">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
配件信息
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
类型
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
价格
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
库存
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
操作
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredComponents.map((component) => (
<tr key={component.id}>
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
<div className="h-10 w-10 flex-shrink-0">
{component.imageUrl ? (
<img loading='lazy'
className="h-10 w-10 rounded-full object-cover"
src={component.imageUrl}
alt={component.name}
/>
) : (
<div className="h-10 w-10 rounded-full bg-gray-300 flex items-center justify-center">
📦
</div>
)}
</div>
<div className="ml-4">
<div className="text-sm font-medium text-gray-900">
{component.name}
</div>
<div className="text-sm text-gray-500">
{component.brand} {component.model}
</div>
</div>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{component.componentType.name}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
¥{component.price.toLocaleString()}
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex px-2 py-1 text-xs rounded-full ${
component.stock > 0
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}`}>
{component.stock}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
<button
onClick={() => handleEdit(component)}
className="text-blue-600 hover:text-blue-900 mr-4"
>
<Edit className="h-4 w-4" />
</button>
<button
onClick={() => handleDelete(component.id)}
className="text-red-600 hover:text-red-900"
>
<Trash2 className="h-4 w-4" />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Modal */}
{showModal && (
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg p-6 w-full max-w-md max-h-[90vh] overflow-y-auto">
<h2 className="text-xl font-bold mb-4">
{editingComponent ? '编辑配件' : '添加配件'}
</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700">配件类型</label>
<select
value={formData.componentTypeId}
onChange={(e) => setFormData({ ...formData, componentTypeId: e.target.value })}
required
className="mt-1 block w-full border border-gray-300 rounded-md px-3 py-2"
>
<option value="">选择类型</option>
{componentTypes.map((type) => (
<option key={type.id} value={type.id}>
{type.name}
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">配件名称</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
required
className="mt-1 block w-full border border-gray-300 rounded-md px-3 py-2"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">品牌</label>
<input
type="text"
value={formData.brand}
onChange={(e) => setFormData({ ...formData, brand: e.target.value })}
required
className="mt-1 block w-full border border-gray-300 rounded-md px-3 py-2"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">型号</label>
<input
type="text"
value={formData.model}
onChange={(e) => setFormData({ ...formData, model: e.target.value })}
required
className="mt-1 block w-full border border-gray-300 rounded-md px-3 py-2"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">价格</label>
<input
type="number"
step="0.01"
value={formData.price}
onChange={(e) => setFormData({ ...formData, price: e.target.value })}
required
className="mt-1 block w-full border border-gray-300 rounded-md px-3 py-2"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">库存</label>
<input
type="number"
value={formData.stock}
onChange={(e) => setFormData({ ...formData, stock: e.target.value })}
required
className="mt-1 block w-full border border-gray-300 rounded-md px-3 py-2"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">描述</label>
<textarea
value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
rows={3}
className="mt-1 block w-full border border-gray-300 rounded-md px-3 py-2"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">图片URL</label>
<input
type="url"
value={formData.imageUrl}
onChange={(e) => setFormData({ ...formData, imageUrl: e.target.value })}
className="mt-1 block w-full border border-gray-300 rounded-md px-3 py-2"
/>
</div>
<div className="flex justify-end space-x-4 pt-4">
<button
type="button"
onClick={() => setShowModal(false)}
className="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50"
>
取消
</button>
<button
type="submit"
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
>
{editingComponent ? '更新' : '创建'}
</button>
</div>
</form>
</div>
</div>
)}
{/* Batch Import Modal */}
{showBatchModal && (
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg p-6 w-full max-w-md max-h-[90vh] overflow-y-auto">
<h2 className="text-xl font-bold mb-4">批量导入配件</h2>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700">选择文件</label>
<input
type="file"
onChange={handleFileChange}
accept=".json,.csv"
className="mt-1 block w-full border border-gray-300 rounded-md px-3 py-2"
/>
</div>
{batchFile && (
<div className="mb-4">
<p className="text-sm text-gray-500">
已选择文件: <span className="font-medium">{batchFile.name}</span>
</p>
</div>
)}
<div className="mb-4">
<button
onClick={() => downloadTemplate('csv')}
className="mr-2 px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 flex items-center gap-2"
>
<Download className="h-5 w-5" />
下载CSV模板
</button>
<button
onClick={() => downloadTemplate('json')}
className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 flex items-center gap-2"
>
<Download className="h-5 w-5" />
下载JSON模板
</button>
</div>
<div className="flex justify-end space-x-4">
<button
type="button"
onClick={() => setShowBatchModal(false)}
className="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50"
>
取消
</button>
<button
onClick={handleBatchImport}
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center gap-2"
disabled={batchImportLoading}
>
{batchImportLoading ? (
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-white"></div>
) : (
<Upload className="h-5 w-5" />
)}
批量导入
</button>
</div>
{batchData.length > 0 && (
<div className="mt-4">
<h3 className="text-lg font-semibold mb-2">预览数据</h3>
<div className="bg-gray-100 p-4 rounded-md max-h-[300px] overflow-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
{Object.keys(batchData[0]).map((key) => (
<th key={key} className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{key}
</th>
))}
</tr>
</thead> <tbody className="bg-white divide-y divide-gray-200">
{batchData.map((item, index) => (
<tr key={index}>
{Object.values(item).map((value, i) => (
<td key={i} className="px-4 py-2 whitespace-nowrap text-sm text-gray-900">
{String(value || '')}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
</div>
</div>
)}
</div>
)
}
export default withAdminAuth(AdminComponentsPage)
+278
View File
@@ -0,0 +1,278 @@
'use client'
import { useState, useEffect } from 'react'
import { withAdminAuth } from '@/components/admin/AdminAuth'
import { Eye, Package, Calendar, User } from 'lucide-react'
interface Order {
id: string
orderNumber: string
totalAmount: number
status: string
createdAt: string
user: {
id: string
email: string
name?: string
username: string
}
orderItems: {
id: string
quantity: number
price: number
component: {
id: string
name: string
brand: string
model: string
}
}[]
}
const statusLabels: { [key: string]: string } = {
PENDING: '待处理',
CONFIRMED: '已确认',
PROCESSING: '处理中',
SHIPPED: '已发货',
DELIVERED: '已送达',
CANCELLED: '已取消'
}
const statusColors: { [key: string]: string } = {
PENDING: 'bg-yellow-100 text-yellow-800',
CONFIRMED: 'bg-blue-100 text-blue-800',
PROCESSING: 'bg-purple-100 text-purple-800',
SHIPPED: 'bg-green-100 text-green-800',
DELIVERED: 'bg-green-100 text-green-800',
CANCELLED: 'bg-red-100 text-red-800'
}
function AdminOrdersPage() {
const [orders, setOrders] = useState<Order[]>([])
const [isLoading, setIsLoading] = useState(true)
const [selectedOrder, setSelectedOrder] = useState<Order | null>(null)
const [showModal, setShowModal] = useState(false)
useEffect(() => {
loadOrders()
}, [])
const loadOrders = async () => {
try {
const response = await fetch('/api/admin/orders')
if (response.ok) {
const data = await response.json()
setOrders(data)
}
} catch (error) {
console.error('加载订单失败:', error)
} finally {
setIsLoading(false)
}
}
const updateOrderStatus = async (orderId: string, newStatus: string) => {
try {
const response = await fetch(`/api/admin/orders/${orderId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ status: newStatus }),
})
if (response.ok) {
await loadOrders()
if (selectedOrder && selectedOrder.id === orderId) {
setSelectedOrder({ ...selectedOrder, status: newStatus })
}
} else {
alert('更新订单状态失败')
}
} catch (error) {
console.error('更新订单状态失败:', error)
alert('更新订单状态失败')
}
}
const viewOrderDetails = (order: Order) => {
setSelectedOrder(order)
setShowModal(true)
}
if (isLoading) {
return (
<div className="flex justify-center items-center py-12">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
)
}
return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="flex justify-between items-center mb-6">
<h1 className="text-3xl font-bold text-gray-900">订单管理</h1>
<div className="text-sm text-gray-600">
共 {orders.length} 个订单
</div>
</div>
{/* Orders Table */}
<div className="bg-white shadow-md rounded-lg overflow-hidden">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
订单号
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
用户
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
金额
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
状态
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
创建时间
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
操作
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{orders.map((order) => (
<tr key={order.id}>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm font-medium text-gray-900">
{order.orderNumber}
</div>
<div className="text-sm text-gray-500">
{order.orderItems.length} 个配件
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
<User className="h-4 w-4 text-gray-400 mr-2" /> <div>
<div className="text-sm font-medium text-gray-900">
{order.user?.name || order.user?.username || '未知用户'}
</div>
<div className="text-sm text-gray-500">
{order.user?.email || '无邮箱'}
</div>
</div>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
¥{order.totalAmount.toLocaleString()}
</td>
<td className="px-6 py-4 whitespace-nowrap">
<select
value={order.status}
onChange={(e) => updateOrderStatus(order.id, e.target.value)}
className={`inline-flex px-2 py-1 text-xs rounded-full border-0 ${statusColors[order.status]}`}
>
{Object.entries(statusLabels).map(([status, label]) => (
<option key={status} value={status}>
{label}
</option>
))}
</select>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<div className="flex items-center">
<Calendar className="h-4 w-4 mr-2" />
{new Date(order.createdAt).toLocaleDateString('zh-CN')}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
<button
onClick={() => viewOrderDetails(order)}
className="text-blue-600 hover:text-blue-900"
>
<Eye className="h-4 w-4" />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Order Details Modal */}
{showModal && selectedOrder && (
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg p-6 w-full max-w-2xl max-h-[90vh] overflow-y-auto">
<h2 className="text-xl font-bold mb-4">订单详情</h2>
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700">订单号</label>
<div className="text-sm text-gray-900">{selectedOrder.orderNumber}</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">状态</label>
<span className={`inline-flex px-2 py-1 text-xs rounded-full ${statusColors[selectedOrder.status]}`}>
{statusLabels[selectedOrder.status]}
</span>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">用户</label> <div className="text-sm text-gray-900">
{selectedOrder.user?.name || selectedOrder.user?.username || '未知用户'}
</div>
<div className="text-sm text-gray-500">{selectedOrder.user?.email || '无邮箱'}</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">总金额</label>
<div className="text-sm text-gray-900">¥{selectedOrder.totalAmount.toLocaleString()}</div>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">订单商品</label>
<div className="border rounded-lg">
{selectedOrder.orderItems.map((item) => (
<div key={item.id} className="p-4 border-b last:border-b-0">
<div className="flex justify-between items-center"> <div>
<div className="font-medium">{item.component?.name || '未知商品'}</div>
<div className="text-sm text-gray-500">
{item.component?.brand || '未知品牌'} {item.component?.model || ''}
</div>
</div>
<div className="text-right">
<div className="font-medium">¥{item.price.toLocaleString()}</div>
<div className="text-sm text-gray-500">数量: {item.quantity}</div>
</div>
</div>
</div>
))}
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">创建时间</label>
<div className="text-sm text-gray-900">
{new Date(selectedOrder.createdAt).toLocaleString('zh-CN')}
</div>
</div>
</div>
<div className="flex justify-end space-x-4 pt-6">
<button
onClick={() => setShowModal(false)}
className="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50"
>
关闭
</button>
</div>
</div>
</div>
)}
</div>
)
}
export default withAdminAuth(AdminOrdersPage)
+442
View File
@@ -0,0 +1,442 @@
'use client'
import { useState, useEffect } from 'react'
import Link from 'next/link'
import {
Users,
Package,
ShoppingCart,
DollarSign,
TrendingUp,
Eye,
BarChart3,
Settings,
Plus
} from 'lucide-react'
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell } from 'recharts'
interface StatsData {
overview: {
totalUsers: number
totalOrders: number
totalComponents: number
totalRevenue: number
}
topUsers: Array<{
id: string
username: string
name: string
email: string
totalSpending: number
orderCount: number
}>
topComponents: Array<{
id: string
name: string
brand: string
price: number
typeName: string
totalSales: number
totalRevenue: number
}>
recentOrders: Array<{
id: string
orderNumber: string
totalAmount: number
status: string
createdAt: string
user: {
username: string
name?: string
}
orderItems: Array<{
component: {
name: string
}
}>
}>
}
const COLORS = ['#3B82F6', '#EF4444', '#10B981', '#F59E0B', '#8B5CF6', '#EC4899']
export default function AdminDashboard() {
const [stats, setStats] = useState<StatsData | null>(null)
const [isLoading, setIsLoading] = useState(true)
const [activeTab, setActiveTab] = useState('overview')
useEffect(() => {
checkAdminAccess()
loadStats()
}, [])
const checkAdminAccess = () => {
const user = JSON.parse(localStorage.getItem('user') || 'null')
if (!user || !user.isAdmin) {
alert('权限不足,需要管理员权限')
window.location.href = '/'
return
}
}
const loadStats = async () => {
try {
const token = localStorage.getItem('token')
const response = await fetch('/api/admin/stats', {
headers: {
'Authorization': `Bearer ${token}`
}
})
if (response.ok) {
const data = await response.json()
setStats(data)
} else if (response.status === 403) {
alert('权限不足')
window.location.href = '/'
}
} catch (error) {
console.error('加载统计数据失败:', error)
} finally {
setIsLoading(false)
}
}
if (isLoading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-600">加载中...</p>
</div>
</div>
)
}
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header */}
<div className="flex justify-between items-center mb-8">
<div>
<h1 className="text-3xl font-bold text-gray-900">管理后台</h1>
<p className="text-gray-600">系统管理与数据统计</p>
</div>
<div className="flex space-x-4">
<Link
href="/admin/components"
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
>
<Plus className="h-4 w-4 inline mr-2" />
添加商品
</Link>
<Link
href="/admin/orders"
className="border border-blue-600 text-blue-600 px-4 py-2 rounded-lg hover:bg-blue-50 transition-colors"
>
<Eye className="h-4 w-4 inline mr-2" />
查看订单
</Link>
</div>
</div>
{/* Overview Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div className="bg-white rounded-lg shadow-sm p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-gray-600 text-sm">总用户数</p>
<p className="text-2xl font-bold text-gray-900">{stats?.overview.totalUsers || 0}</p>
</div>
<Users className="h-12 w-12 text-blue-600" />
</div>
</div>
<div className="bg-white rounded-lg shadow-sm p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-gray-600 text-sm">总订单数</p>
<p className="text-2xl font-bold text-gray-900">{stats?.overview.totalOrders || 0}</p>
</div>
<ShoppingCart className="h-12 w-12 text-green-600" />
</div>
</div>
<div className="bg-white rounded-lg shadow-sm p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-gray-600 text-sm">商品总数</p>
<p className="text-2xl font-bold text-gray-900">{stats?.overview.totalComponents || 0}</p>
</div>
<Package className="h-12 w-12 text-purple-600" />
</div>
</div>
<div className="bg-white rounded-lg shadow-sm p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-gray-600 text-sm">总营收</p>
<p className="text-2xl font-bold text-gray-900">¥{stats?.overview.totalRevenue?.toFixed(2) || '0.00'}</p>
</div>
<DollarSign className="h-12 w-12 text-red-600" />
</div>
</div>
</div>
{/* Tab Navigation */}
<div className="mb-8">
<div className="border-b border-gray-200">
<nav className="-mb-px flex space-x-8">
<button
onClick={() => setActiveTab('overview')}
className={`py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === 'overview'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
<BarChart3 className="h-4 w-4 inline mr-2" />
数据统计
</button>
<button
onClick={() => setActiveTab('users')}
className={`py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === 'users'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
<Users className="h-4 w-4 inline mr-2" />
用户排行
</button>
<button
onClick={() => setActiveTab('products')}
className={`py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === 'products'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
<Package className="h-4 w-4 inline mr-2" />
商品排行
</button>
<button
onClick={() => setActiveTab('orders')}
className={`py-2 px-1 border-b-2 font-medium text-sm ${
activeTab === 'orders'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
<ShoppingCart className="h-4 w-4 inline mr-2" />
最近订单
</button>
</nav>
</div>
</div>
{/* Tab Content */}
{activeTab === 'overview' && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Top Users Chart */}
<div className="bg-white rounded-lg shadow-sm p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">用户消费金额排行</h3>
<ResponsiveContainer width="100%" height={300}>
<BarChart data={stats?.topUsers.slice(0, 5) || []}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip formatter={(value) => [`¥${value}`, '消费金额']} />
<Bar dataKey="totalSpending" fill="#3B82F6" />
</BarChart>
</ResponsiveContainer>
</div>
{/* Top Components Chart */}
<div className="bg-white rounded-lg shadow-sm p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">商品销量排行</h3>
<ResponsiveContainer width="100%" height={300}>
<BarChart data={stats?.topComponents.slice(0, 5) || []}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip formatter={(value) => [`${value}件`, '销量']} />
<Bar dataKey="totalSales" fill="#EF4444" />
</BarChart>
</ResponsiveContainer>
</div>
</div>
)}
{activeTab === 'users' && (
<div className="bg-white rounded-lg shadow-sm overflow-hidden">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">用户消费排行榜</h3>
</div>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
排名
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
用户
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
邮箱
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
订单数
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
消费金额
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{stats?.topUsers.map((user, index) => (
<tr key={user.id}>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
#{index + 1}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{user.name}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{user.email}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{user.orderCount}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-red-600">
¥{user.totalSpending.toFixed(2)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
{activeTab === 'products' && (
<div className="bg-white rounded-lg shadow-sm overflow-hidden">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">商品销量排行榜</h3>
</div>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
排名
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
商品
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
品牌
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
类型
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
销量
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
营收
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{stats?.topComponents.map((component, index) => (
<tr key={component.id}>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
#{index + 1}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{component.name}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{component.brand}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{component.typeName}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{component.totalSales}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-red-600">
¥{component.totalRevenue.toFixed(2)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
{activeTab === 'orders' && (
<div className="bg-white rounded-lg shadow-sm overflow-hidden">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">最近订单</h3>
</div>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
订单号
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
用户
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
商品数量
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
订单金额
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
状态
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
创建时间
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{stats?.recentOrders.map((order) => (
<tr key={order.id}>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
{order.orderNumber}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{order.user.name || order.user.username}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{order.orderItems.length} 件
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-red-600">
¥{order.totalAmount}
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className="inline-flex px-2 py-1 text-xs font-medium rounded-full bg-blue-100 text-blue-800">
{order.status}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{new Date(order.createdAt).toLocaleDateString('zh-CN')}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
</div>
</div>
)
}
+102
View File
@@ -0,0 +1,102 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { requireAdmin } from '@/lib/admin-auth'
// 获取所有组件
export async function GET(request: NextRequest) {
try {
await requireAdmin(request)
const { searchParams } = new URL(request.url)
const page = parseInt(searchParams.get('page') || '1')
const limit = parseInt(searchParams.get('limit') || '20')
const type = searchParams.get('type')
const search = searchParams.get('search')
const where: any = {}
if (type) {
where.componentType = {
name: type
}
}
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
{ brand: { contains: search, mode: 'insensitive' } },
]
}
const [components, total] = await Promise.all([
prisma.component.findMany({
where,
include: {
componentType: true
},
orderBy: {
createdAt: 'desc'
},
skip: (page - 1) * limit,
take: limit
}),
prisma.component.count({ where })
])
return NextResponse.json({
components,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit)
}
})
} catch (error: any) {
return NextResponse.json(
{ message: error.message || '获取组件列表失败' },
{ status: error.message === '权限不足' ? 403 : 500 }
)
}
}
// 创建新组件
export async function POST(request: NextRequest) {
try {
await requireAdmin(request)
const body = await request.json()
const { name, brand, model, price, description, imageUrl, stock, specifications, componentTypeId } = body
if (!name || !brand || !model || !price || !componentTypeId) {
return NextResponse.json(
{ message: '请填写所有必填字段' },
{ status: 400 }
)
}
const component = await prisma.component.create({
data: {
name,
brand,
model,
price: parseFloat(price),
description,
imageUrl,
stock: parseInt(stock) || 0,
specifications: specifications ? JSON.stringify(specifications) : null,
componentTypeId
},
include: {
componentType: true
}
})
return NextResponse.json(component, { status: 201 })
} catch (error: any) {
return NextResponse.json(
{ message: error.message || '创建组件失败' },
{ status: 500 }
)
}
}
+47
View File
@@ -0,0 +1,47 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const { status } = await request.json()
const order = await prisma.order.update({
where: { id: id },
data: { status },
include: {
user: {
select: {
id: true,
email: true,
username: true,
name: true
}
},
orderItems: {
include: {
component: {
select: {
id: true,
name: true,
brand: true,
model: true
}
}
}
}
}
})
return NextResponse.json(order)
} catch (error) {
console.error('更新订单状态失败:', error)
return NextResponse.json(
{ message: '更新订单状态失败' },
{ status: 500 }
)
}
}
+38
View File
@@ -0,0 +1,38 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const orders = await prisma.order.findMany({
include: {
user: {
select: {
id: true,
email: true,
username: true,
name: true
}
}, orderItems: {
include: {
component: {
include: {
componentType: true
}
}
}
}
},
orderBy: {
createdAt: 'desc'
}
})
return NextResponse.json(orders)
} catch (error) {
console.error('获取订单列表失败:', error)
return NextResponse.json(
{ message: '获取订单列表失败' },
{ status: 500 }
)
}
}
+123
View File
@@ -0,0 +1,123 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { requireAdmin } from '@/lib/admin-auth'
export async function GET(request: NextRequest) {
try {
await requireAdmin(request)
// 按消费金额倒排的前十用户
const topUsersBySpending = await prisma.user.findMany({
where: {
isAdmin: false,
orders: {
some: {}
}
},
select: {
id: true,
username: true,
name: true,
email: true,
orders: {
select: {
totalAmount: true
}
}
},
take: 10
})
const usersWithSpending = topUsersBySpending.map(user => ({
id: user.id,
username: user.username,
name: user.name || user.username,
email: user.email,
totalSpending: user.orders.reduce((sum, order) => sum + order.totalAmount, 0),
orderCount: user.orders.length
})).sort((a, b) => b.totalSpending - a.totalSpending)
// 按销售量倒排序的前十配件
const topComponentsBySales = await prisma.component.findMany({
select: {
id: true,
name: true,
brand: true,
price: true,
componentType: {
select: {
name: true
}
},
orderItems: {
select: {
quantity: true
}
}
}
})
const componentsWithSales = topComponentsBySales.map(component => ({
id: component.id,
name: component.name,
brand: component.brand,
price: component.price,
typeName: component.componentType.name,
totalSales: component.orderItems.reduce((sum, item) => sum + item.quantity, 0),
totalRevenue: component.orderItems.reduce((sum, item) => sum + (item.quantity * component.price), 0)
})).sort((a, b) => b.totalSales - a.totalSales).slice(0, 10)
// 总体统计
const totalUsers = await prisma.user.count({ where: { isAdmin: false } })
const totalOrders = await prisma.order.count()
const totalComponents = await prisma.component.count()
const totalRevenue = await prisma.order.aggregate({
_sum: {
totalAmount: true
}
})
const recentOrders = await prisma.order.findMany({
take: 5,
orderBy: {
createdAt: 'desc'
},
include: {
user: {
select: {
username: true,
name: true
}
},
orderItems: {
include: {
component: {
select: {
name: true
}
}
}
}
}
})
return NextResponse.json({
overview: {
totalUsers,
totalOrders,
totalComponents,
totalRevenue: totalRevenue._sum.totalAmount || 0
},
topUsers: usersWithSpending,
topComponents: componentsWithSales,
recentOrders
})
} catch (error: any) {
console.error('获取统计数据失败:', error)
return NextResponse.json(
{ message: error.message || '获取统计数据失败' },
{ status: error.message === '权限不足' ? 403 : 500 }
)
}
}
+59
View File
@@ -0,0 +1,59 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { verifyPassword, generateToken } from '@/lib/auth'
export async function POST(request: NextRequest) {
try {
const { email, password } = await request.json()
if (!email || !password) {
return NextResponse.json(
{ message: '邮箱和密码不能为空' },
{ status: 400 }
)
}
// 查找用户
const user = await prisma.user.findUnique({
where: { email }
})
if (!user) {
return NextResponse.json(
{ message: '用户不存在' },
{ status: 401 }
)
}
// 验证密码
const isValidPassword = await verifyPassword(password, user.password)
if (!isValidPassword) {
return NextResponse.json(
{ message: '密码错误' },
{ status: 401 }
)
}
// 生成token
const token = generateToken({
userId: user.id,
email: user.email,
isAdmin: user.isAdmin
})
// 返回用户信息(不包含密码)
const { password: _, ...userWithoutPassword } = user
return NextResponse.json({
message: '登录成功',
token,
user: userWithoutPassword
})
} catch (error) {
console.error('Login error:', error)
return NextResponse.json(
{ message: '服务器错误' },
{ status: 500 }
)
}
}
+70
View File
@@ -0,0 +1,70 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { hashPassword } from '@/lib/auth'
export async function POST(request: NextRequest) {
try {
const { email, username, password, name, phone, address } = await request.json()
if (!email || !username || !password) {
return NextResponse.json(
{ message: '邮箱、用户名和密码不能为空' },
{ status: 400 }
)
}
// 检查邮箱是否已存在
const existingUserByEmail = await prisma.user.findUnique({
where: { email }
})
if (existingUserByEmail) {
return NextResponse.json(
{ message: '该邮箱已被注册' },
{ status: 400 }
)
}
// 检查用户名是否已存在
const existingUserByUsername = await prisma.user.findUnique({
where: { username }
})
if (existingUserByUsername) {
return NextResponse.json(
{ message: '该用户名已被使用' },
{ status: 400 }
)
}
// 加密密码
const hashedPassword = await hashPassword(password)
// 创建用户
const user = await prisma.user.create({
data: {
email,
username,
password: hashedPassword,
name: name || null,
phone: phone || null,
address: address || null,
isAdmin: false
}
})
// 返回用户信息(不包含密码)
const { password: _, ...userWithoutPassword } = user
return NextResponse.json({
message: '注册成功',
user: userWithoutPassword
}, { status: 201 })
} catch (error) {
console.error('Register error:', error)
return NextResponse.json(
{ message: '服务器错误' },
{ status: 500 }
)
}
}
+139
View File
@@ -0,0 +1,139 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { verifyToken } from '@/lib/auth'
// 更新购物车项数量
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const authHeader = request.headers.get('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return NextResponse.json(
{ message: '请先登录' },
{ status: 401 }
)
}
const token = authHeader.substring(7)
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json(
{ message: '登录已过期' },
{ status: 401 }
)
}
const { quantity } = await request.json()
if (!quantity || quantity < 1) {
return NextResponse.json(
{ message: '数量必须大于0' },
{ status: 400 }
)
}
// 检查购物车项是否属于当前用户
const cartItem = await prisma.cartItem.findFirst({
where: {
id: id,
userId: decoded.userId
},
include: {
component: true
}
})
if (!cartItem) {
return NextResponse.json(
{ message: '购物车项不存在' },
{ status: 404 }
)
}
// 检查库存
if (quantity > cartItem.component.stock) {
return NextResponse.json(
{ message: '库存不足' },
{ status: 400 }
)
}
const updatedCartItem = await prisma.cartItem.update({
where: { id: id },
data: { quantity: quantity },
include: {
component: {
include: {
componentType: true
}
}
}
})
return NextResponse.json(updatedCartItem)
} catch (error) {
console.error('更新购物车项失败:', error)
return NextResponse.json(
{ message: '更新购物车项失败' },
{ status: 500 }
)
}
}
// 删除购物车项
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const authHeader = request.headers.get('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return NextResponse.json(
{ message: '请先登录' },
{ status: 401 }
)
}
const token = authHeader.substring(7)
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json(
{ message: '登录已过期' },
{ status: 401 }
)
}
// 检查购物车项是否属于当前用户
const cartItem = await prisma.cartItem.findFirst({
where: {
id: id,
userId: decoded.userId
}
})
if (!cartItem) {
return NextResponse.json(
{ message: '购物车项不存在' },
{ status: 404 }
)
}
await prisma.cartItem.delete({
where: { id: id }
})
return NextResponse.json({ message: '已从购物车移除' })
} catch (error) {
console.error('删除购物车项失败:', error)
return NextResponse.json(
{ message: '删除购物车项失败' },
{ status: 500 }
)
}
}
+188
View File
@@ -0,0 +1,188 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { verifyToken } from '@/lib/auth'
// 获取购物车
export async function GET(request: NextRequest) {
try {
const authHeader = request.headers.get('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return NextResponse.json(
{ message: '请先登录' },
{ status: 401 }
)
}
const token = authHeader.substring(7)
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json(
{ message: '登录已过期' },
{ status: 401 }
)
}
const cartItems = await prisma.cartItem.findMany({
where: { userId: decoded.userId },
include: {
component: {
include: {
componentType: true
}
}
},
orderBy: { createdAt: 'desc' }
})
return NextResponse.json(cartItems)
} catch (error) {
console.error('获取购物车失败:', error)
return NextResponse.json(
{ message: '获取购物车失败' },
{ status: 500 }
)
}
}
// 添加商品到购物车
export async function POST(request: NextRequest) {
try {
const authHeader = request.headers.get('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return NextResponse.json(
{ message: '请先登录' },
{ status: 401 }
)
}
const token = authHeader.substring(7)
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json(
{ message: '登录已过期' },
{ status: 401 }
)
}
const { componentId, quantity = 1 } = await request.json()
if (!componentId) {
return NextResponse.json(
{ message: '缺少商品ID' },
{ status: 400 }
)
}
// 检查商品是否存在
const component = await prisma.component.findUnique({
where: { id: componentId }
})
if (!component) {
return NextResponse.json(
{ message: '商品不存在' },
{ status: 404 }
)
}
if (component.stock < quantity) {
return NextResponse.json(
{ message: '库存不足' },
{ status: 400 }
)
}
// 检查购物车中是否已存在该商品
const existingCartItem = await prisma.cartItem.findUnique({
where: {
userId_componentId: {
userId: decoded.userId,
componentId: componentId
}
}
})
let cartItem
if (existingCartItem) {
// 更新数量
const newQuantity = existingCartItem.quantity + quantity
if (newQuantity > component.stock) {
return NextResponse.json(
{ message: '超出库存限制' },
{ status: 400 }
)
}
cartItem = await prisma.cartItem.update({
where: { id: existingCartItem.id },
data: { quantity: newQuantity },
include: {
component: {
include: {
componentType: true
}
}
}
})
} else {
// 创建新的购物车项
cartItem = await prisma.cartItem.create({
data: {
userId: decoded.userId,
componentId: componentId,
quantity: quantity
},
include: {
component: {
include: {
componentType: true
}
}
}
})
}
return NextResponse.json(cartItem, { status: 201 })
} catch (error) {
console.error('添加到购物车失败:', error)
return NextResponse.json(
{ message: '添加到购物车失败' },
{ status: 500 }
)
}
}
// 清空购物车
export async function DELETE(request: NextRequest) {
try {
const authHeader = request.headers.get('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return NextResponse.json(
{ message: '请先登录' },
{ status: 401 }
)
}
const token = authHeader.substring(7)
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json(
{ message: '登录已过期' },
{ status: 401 }
)
}
await prisma.cartItem.deleteMany({
where: { userId: decoded.userId }
})
return NextResponse.json({ message: '购物车已清空' })
} catch (error) {
console.error('清空购物车失败:', error)
return NextResponse.json(
{ message: '清空购物车失败' },
{ status: 500 }
)
}
}
+27
View File
@@ -0,0 +1,27 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
export async function GET() {
try {
const componentTypes = await prisma.componentType.findMany({
include: {
_count: {
select: {
components: true
}
}
},
orderBy: {
name: 'asc'
}
})
return NextResponse.json(componentTypes)
} catch (error) {
console.error('Component types fetch error:', error)
return NextResponse.json(
{ message: '获取配件类型失败' },
{ status: 500 }
)
}
}
+90
View File
@@ -0,0 +1,90 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const component = await prisma.component.findUnique({
where: { id: id },
include: {
componentType: true
}
})
if (!component) {
return NextResponse.json(
{ message: '配件不存在' },
{ status: 404 }
)
}
return NextResponse.json(component)
} catch (error) {
console.error('Component fetch error:', error)
return NextResponse.json(
{ message: '获取配件详情失败' },
{ status: 500 }
)
}
}
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const data = await request.json()
const component = await prisma.component.update({
where: { id: id },
data: {
name: data.name,
brand: data.brand,
model: data.model,
price: parseFloat(data.price),
description: data.description,
imageUrl: data.imageUrl,
stock: parseInt(data.stock),
specifications: data.specifications,
componentTypeId: data.componentTypeId
},
include: {
componentType: true
}
})
return NextResponse.json(component)
} catch (error) {
console.error('Component update error:', error)
return NextResponse.json(
{ message: '更新配件失败' },
{ status: 500 }
)
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
await prisma.component.delete({
where: { id: id }
})
return NextResponse.json({ message: '删除成功' })
} catch (error) {
console.error('Component delete error:', error)
return NextResponse.json(
{ message: '删除配件失败' },
{ status: 500 }
)
}
}
+215
View File
@@ -0,0 +1,215 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
interface BatchComponentData {
name: string
brand: string
model: string
price: number
description?: string
imageUrl?: string
stock: number
specifications?: string
componentTypeId?: string
typeName?: string
type?: string
}
interface BatchResult {
success: boolean
item: BatchComponentData
error?: string
componentId?: string
}
export async function POST(request: NextRequest) {
try {
// 这个API需要管理员权限,实际应用中需要验证
const { components } = await request.json()
if (!Array.isArray(components) || components.length === 0) {
return NextResponse.json(
{ message: '请提供有效的配件数据数组' },
{ status: 400 }
)
}
const results: BatchResult[] = []
const createdTypes: { [key: string]: string } = {} // 缓存新创建的类型
// 获取现有的配件类型,用于匹配
const existingTypes = await prisma.componentType.findMany()
const typeMap = new Map(existingTypes.map(type => [type.name.toLowerCase(), type.id]))
for (const componentData of components) {
try {
// 验证必需字段
if (!componentData.name || !componentData.brand || !componentData.model) {
results.push({
success: false,
item: componentData,
error: '缺少必需字段: name, brand, model'
})
continue
}
// 处理价格和库存
const price = typeof componentData.price === 'string'
? parseFloat(componentData.price)
: componentData.price
const stock = typeof componentData.stock === 'string'
? parseInt(componentData.stock)
: componentData.stock
if (isNaN(price) || price < 0) {
results.push({
success: false,
item: componentData,
error: '价格必须是有效的正数'
})
continue
}
if (isNaN(stock) || stock < 0) {
results.push({
success: false,
item: componentData,
error: '库存必须是有效的非负整数'
})
continue
}
// 处理配件类型
let componentTypeId = componentData.componentTypeId
if (!componentTypeId) {
const typeName = componentData.typeName || componentData.type
if (!typeName) {
results.push({
success: false,
item: componentData,
error: '缺少配件类型信息 (componentTypeId, typeName 或 type)'
})
continue
}
const lowerTypeName = typeName.toLowerCase()
// 检查是否已存在
if (typeMap.has(lowerTypeName)) {
componentTypeId = typeMap.get(lowerTypeName)
} else if (createdTypes[lowerTypeName]) {
// 检查本次批量导入中是否已创建
componentTypeId = createdTypes[lowerTypeName]
} else {
// 创建新的配件类型
try {
const newType = await prisma.componentType.create({
data: {
name: typeName,
description: `自动创建的${typeName}类型`
}
})
componentTypeId = newType.id
createdTypes[lowerTypeName] = newType.id
typeMap.set(lowerTypeName, newType.id)
} catch (typeError) {
results.push({
success: false,
item: componentData,
error: `创建配件类型失败: ${typeError instanceof Error ? typeError.message : '未知错误'}`
})
continue
}
}
}
// 验证配件类型ID
if (!componentTypeId) {
results.push({
success: false,
item: componentData,
error: '无法确定配件类型'
})
continue
}
// 检查是否已存在相同的配件
const existingComponent = await prisma.component.findFirst({
where: {
name: componentData.name,
brand: componentData.brand,
model: componentData.model
}
})
if (existingComponent) {
results.push({
success: false,
item: componentData,
error: `配件已存在: ${componentData.name} - ${componentData.brand} ${componentData.model}`
})
continue
}
// 创建配件
const newComponent = await prisma.component.create({
data: {
name: componentData.name.trim(),
brand: componentData.brand.trim(),
model: componentData.model.trim(),
price,
description: componentData.description?.trim() || '',
imageUrl: componentData.imageUrl?.trim() || '',
stock,
specifications: componentData.specifications?.trim() || '',
componentTypeId
},
include: {
componentType: true
}
})
results.push({
success: true,
item: componentData,
componentId: newComponent.id
})
} catch (error) {
console.error('处理单个配件时出错:', error)
results.push({
success: false,
item: componentData,
error: error instanceof Error ? error.message : '创建配件失败'
})
}
}
// 统计结果
const successCount = results.filter(r => r.success).length
const failCount = results.filter(r => !r.success).length
return NextResponse.json({
success: true,
results,
summary: {
total: components.length,
successful: successCount,
failed: failCount,
newTypesCreated: Object.keys(createdTypes).length
}
})
} catch (error) {
console.error('批量导入配件失败:', error)
return NextResponse.json(
{
success: false,
message: '批量导入失败',
error: error instanceof Error ? error.message : '未知错误'
},
{ status: 500 }
)
}
}
+110
View File
@@ -0,0 +1,110 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const typeId = searchParams.get('type')
const brand = searchParams.get('brand')
const minPrice = searchParams.get('minPrice')
const maxPrice = searchParams.get('maxPrice')
const search = searchParams.get('search')
const page = parseInt(searchParams.get('page') || '1')
const limit = parseInt(searchParams.get('limit') || '12')
const where: any = {}
if (typeId) {
where.componentTypeId = typeId
}
if (brand) {
where.brand = {
contains: brand,
mode: 'insensitive'
}
}
if (minPrice || maxPrice) {
where.price = {}
if (minPrice) where.price.gte = parseFloat(minPrice)
if (maxPrice) where.price.lte = parseFloat(maxPrice)
}
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
{ brand: { contains: search, mode: 'insensitive' } },
{ model: { contains: search, mode: 'insensitive' } },
{ description: { contains: search, mode: 'insensitive' } }
]
}
const [components, total] = await Promise.all([
prisma.component.findMany({
where,
include: {
componentType: true
},
orderBy: {
createdAt: 'desc'
},
skip: (page - 1) * limit,
take: limit
}),
prisma.component.count({ where })
])
const totalPages = Math.ceil(total / limit)
return NextResponse.json({
components,
pagination: {
page,
limit,
total,
totalPages,
hasNext: page < totalPages,
hasPrev: page > 1
}
})
} catch (error) {
console.error('Components fetch error:', error)
return NextResponse.json(
{ message: '获取配件列表失败' },
{ status: 500 }
)
}
}
export async function POST(request: NextRequest) {
try {
// 这个API需要管理员权限,暂时简化处理
const data = await request.json()
const component = await prisma.component.create({
data: {
name: data.name,
brand: data.brand,
model: data.model,
price: parseFloat(data.price),
description: data.description,
imageUrl: data.imageUrl,
stock: parseInt(data.stock),
specifications: data.specifications,
componentTypeId: data.componentTypeId
},
include: {
componentType: true
}
})
return NextResponse.json(component, { status: 201 })
} catch (error) {
console.error('Component create error:', error)
return NextResponse.json(
{ message: '创建配件失败' },
{ status: 500 }
)
}
}
+165
View File
@@ -0,0 +1,165 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { verifyToken } from '@/lib/auth'
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const authHeader = request.headers.get('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return NextResponse.json(
{ message: '请先登录' },
{ status: 401 }
)
}
const token = authHeader.substring(7)
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json(
{ message: '登录已过期' },
{ status: 401 }
)
}
const order = await prisma.order.findFirst({
where: {
id: id,
userId: decoded.userId
},
include: {
orderItems: {
include: {
component: {
include: {
componentType: true
}
}
}
},
user: {
select: {
username: true,
email: true
}
}
}
})
if (!order) {
return NextResponse.json(
{ message: '订单不存在' },
{ status: 404 }
)
}
return NextResponse.json(order)
} catch (error) {
console.error('获取订单详情失败:', error)
return NextResponse.json(
{ message: '获取订单详情失败' },
{ status: 500 }
)
}
}
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const authHeader = request.headers.get('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return NextResponse.json(
{ message: '请先登录' },
{ status: 401 }
)
}
const token = authHeader.substring(7)
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json(
{ message: '登录已过期' },
{ status: 401 }
)
}
const { action } = await request.json()
// 查找订单
const order = await prisma.order.findFirst({
where: {
id: id,
userId: decoded.userId
},
include: {
orderItems: true
}
})
if (!order) {
return NextResponse.json(
{ message: '订单不存在' },
{ status: 404 }
)
}
// 处理不同的操作
if (action === 'cancel') {
// 只有待确认状态的订单可以取消
if (order.status !== 'PENDING') {
return NextResponse.json(
{ message: '当前订单状态不允许取消' },
{ status: 400 }
)
} // 更新订单状态为取消
const updatedOrder = await prisma.order.update({
where: { id: id },
data: { status: 'CANCELLED' },
include: {
orderItems: {
include: {
component: {
include: {
componentType: true
}
}
}
}
}
})
// 恢复库存
for (const item of order.orderItems) {
await prisma.component.update({
where: { id: item.componentId },
data: {
stock: {
increment: item.quantity
}
}
})
}
return NextResponse.json(updatedOrder)
}
return NextResponse.json(
{ message: '不支持的操作' },
{ status: 400 }
)
} catch (error) {
console.error('更新订单失败:', error)
return NextResponse.json(
{ message: '更新订单失败' },
{ status: 500 }
)
}
}
+155
View File
@@ -0,0 +1,155 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { verifyToken } from '@/lib/auth'
export async function POST(request: NextRequest) {
try {
const authHeader = request.headers.get('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return NextResponse.json(
{ message: '请先登录' },
{ status: 401 }
)
}
const token = authHeader.substring(7)
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json(
{ message: '登录已过期' },
{ status: 401 }
)
}
const { items } = await request.json()
if (!items || items.length === 0) {
return NextResponse.json(
{ message: '订单商品不能为空' },
{ status: 400 }
)
}
// 生成订单号
const orderNumber = `ORD${Date.now()}`
// 计算总金额
let totalAmount = 0
const orderItems = []
for (const item of items) {
const component = await prisma.component.findUnique({
where: { id: item.componentId }
})
if (!component) {
return NextResponse.json(
{ message: `配件不存在: ${item.componentId}` },
{ status: 400 }
)
}
if (component.stock < item.quantity) {
return NextResponse.json(
{ message: `配件库存不足: ${component.name}` },
{ status: 400 }
)
}
const itemTotal = component.price * item.quantity
totalAmount += itemTotal
orderItems.push({
componentId: component.id,
quantity: item.quantity,
price: component.price
})
}
// 创建订单
const order = await prisma.order.create({
data: {
orderNumber,
totalAmount,
userId: decoded.userId,
orderItems: {
create: orderItems
}
}, include: {
orderItems: {
include: {
component: {
include: {
componentType: true
}
}
}
}
}
})
// 更新库存
for (const item of items) {
await prisma.component.update({
where: { id: item.componentId },
data: {
stock: {
decrement: item.quantity
}
}
})
}
return NextResponse.json(order, { status: 201 })
} catch (error) {
console.error('创建订单失败:', error)
return NextResponse.json(
{ message: '创建订单失败' },
{ status: 500 }
)
}
}
export async function GET(request: NextRequest) {
try {
const authHeader = request.headers.get('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return NextResponse.json(
{ message: '请先登录' },
{ status: 401 }
)
}
const token = authHeader.substring(7)
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json(
{ message: '登录已过期' },
{ status: 401 }
)
} const orders = await prisma.order.findMany({
where: { userId: decoded.userId },
include: {
orderItems: {
include: {
component: {
include: {
componentType: true
}
}
}
}
},
orderBy: {
createdAt: 'desc'
}
})
return NextResponse.json(orders)
} catch (error) {
console.error('获取订单列表失败:', error)
return NextResponse.json(
{ message: '获取订单列表失败' },
{ status: 500 }
)
}
}
+61
View File
@@ -0,0 +1,61 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { verifyToken, hashPassword, verifyPassword } from '@/lib/auth'
export async function PUT(request: NextRequest) {
try {
const authHeader = request.headers.get('Authorization')
const token = authHeader?.replace('Bearer ', '')
if (!token) {
return NextResponse.json({ message: '未提供认证令牌' }, { status: 401 })
}
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json({ message: '无效的认证令牌' }, { status: 401 })
}
const { currentPassword, newPassword } = await request.json()
if (!currentPassword || !newPassword) {
return NextResponse.json({ message: '请提供当前密码和新密码' }, { status: 400 })
}
if (newPassword.length < 6) {
return NextResponse.json({ message: '新密码长度至少6位' }, { status: 400 })
}
// 获取用户当前信息
const user = await prisma.user.findUnique({
where: { id: decoded.userId }
})
if (!user) {
return NextResponse.json({ message: '用户不存在' }, { status: 404 })
}
// 验证当前密码
const isCurrentPasswordValid = await verifyPassword(currentPassword, user.password)
if (!isCurrentPasswordValid) {
return NextResponse.json({ message: '当前密码错误' }, { status: 400 })
}
// 哈希新密码
const hashedNewPassword = await hashPassword(newPassword)
// 更新密码
await prisma.user.update({
where: { id: decoded.userId },
data: {
password: hashedNewPassword
}
})
return NextResponse.json({ message: '密码修改成功' })
} catch (error) {
console.error('修改密码失败:', error)
return NextResponse.json({ message: '修改密码失败' }, { status: 500 })
}
}
+86
View File
@@ -0,0 +1,86 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { verifyToken } from '@/lib/auth'
export async function GET(request: NextRequest) {
try {
const authHeader = request.headers.get('Authorization')
const token = authHeader?.replace('Bearer ', '')
if (!token) {
return NextResponse.json({ message: '未提供认证令牌' }, { status: 401 })
}
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json({ message: '无效的认证令牌' }, { status: 401 })
}
const user = await prisma.user.findUnique({
where: { id: decoded.userId },
select: {
id: true,
email: true,
username: true,
name: true,
phone: true,
address: true,
isAdmin: true,
createdAt: true
}
})
if (!user) {
return NextResponse.json({ message: '用户不存在' }, { status: 404 })
}
return NextResponse.json(user)
} catch (error) {
console.error('获取用户资料失败:', error)
return NextResponse.json({ message: '获取用户资料失败' }, { status: 500 })
}
}
export async function PUT(request: NextRequest) {
try {
const authHeader = request.headers.get('Authorization')
const token = authHeader?.replace('Bearer ', '')
if (!token) {
return NextResponse.json({ message: '未提供认证令牌' }, { status: 401 })
}
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json({ message: '无效的认证令牌' }, { status: 401 })
}
const { name, phone, address } = await request.json()
const updatedUser = await prisma.user.update({
where: { id: decoded.userId },
data: {
name,
phone,
address
},
select: {
id: true,
email: true,
username: true,
name: true,
phone: true,
address: true,
isAdmin: true,
createdAt: true
}
})
return NextResponse.json(updatedUser)
} catch (error) {
console.error('更新用户资料失败:', error)
return NextResponse.json({ message: '更新用户资料失败' }, { status: 500 })
}
}
+170
View File
@@ -0,0 +1,170 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { verifyToken } from '@/lib/auth'
export async function GET(request: NextRequest) {
try {
const authHeader = request.headers.get('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return NextResponse.json(
{ message: '请先登录' },
{ status: 401 }
)
}
const token = authHeader.substring(7)
const decoded = verifyToken(token)
if (!decoded) {
return NextResponse.json(
{ message: '登录已过期' },
{ status: 401 }
)
}
// 获取用户基本信息
const user = await prisma.user.findUnique({
where: { id: decoded.userId },
select: {
id: true,
createdAt: true,
username: true,
email: true
}
})
if (!user) {
return NextResponse.json(
{ message: '用户不存在' },
{ status: 404 }
)
}
// 获取订单统计
const orderStats = await prisma.order.groupBy({
by: ['status'],
where: {
userId: decoded.userId
},
_count: {
id: true
},
_sum: {
totalAmount: true
}
})
// 获取总订单数和总消费
const totalOrders = await prisma.order.count({
where: {
userId: decoded.userId
}
})
const totalSpent = await prisma.order.aggregate({
where: {
userId: decoded.userId,
status: {
in: ['CONFIRMED', 'SHIPPED', 'DELIVERED']
}
},
_sum: {
totalAmount: true
}
}) // 获取购物车商品数量
const cartItemsCount = await prisma.cartItem.count({
where: {
userId: decoded.userId
}
})
// 获取最近订单
const recentOrders = await prisma.order.findMany({
where: {
userId: decoded.userId
},
orderBy: {
createdAt: 'desc'
},
take: 5,
select: {
id: true,
status: true,
totalAmount: true,
createdAt: true
}
})
// 按状态分组订单统计
const ordersByStatus = {
PENDING: 0,
CONFIRMED: 0,
SHIPPED: 0,
DELIVERED: 0,
CANCELLED: 0
}
orderStats.forEach(stat => {
ordersByStatus[stat.status as keyof typeof ordersByStatus] = stat._count.id
})
// 获取用户最喜欢的配件类型(购买最多的类型)
const favoriteComponentType = await prisma.orderItem.groupBy({
by: ['componentId'],
where: {
order: {
userId: decoded.userId,
status: {
in: ['CONFIRMED', 'SHIPPED', 'DELIVERED']
}
}
},
_sum: {
quantity: true
},
orderBy: {
_sum: {
quantity: 'desc'
}
},
take: 1
})
let favoriteType = null
if (favoriteComponentType.length > 0) {
const component = await prisma.component.findUnique({
where: { id: favoriteComponentType[0].componentId },
include: {
componentType: true
}
})
favoriteType = component?.componentType?.name || null
} return NextResponse.json({
user: {
id: user.id,
username: user.username,
email: user.email,
memberSince: user.createdAt
},
orderStats: {
total: totalOrders,
byStatus: ordersByStatus,
totalSpent: totalSpent._sum.totalAmount || 0
},
recentOrders,
favoriteComponentType: favoriteType, summary: {
totalOrders,
totalSpent: totalSpent._sum.totalAmount || 0,
pendingOrders: ordersByStatus.PENDING,
completedOrders: ordersByStatus.DELIVERED,
cartItems: cartItemsCount
}
})
} catch (error) {
console.error('获取用户统计失败:', error)
return NextResponse.json(
{ message: '获取统计数据失败' },
{ status: 500 }
)
}
}
+456
View File
@@ -0,0 +1,456 @@
'use client'
import { useState, useEffect } from 'react'
import Link from 'next/link'
import { Check, Plus, Package, ShoppingCart, Cpu, HardDrive, MemoryStick, Zap, Monitor, Box, Search, X } from 'lucide-react'
interface ComponentType {
id: string
name: string
description?: string
}
interface Component {
id: string
name: string
brand: string
price: number
imageUrl?: string
componentType: ComponentType
}
interface BuildConfiguration {
[key: string]: Component | null
}
const componentIcons: { [key: string]: any } = {
'CPU': Cpu,
'内存': MemoryStick,
'硬盘': HardDrive,
'主板': Zap,
'显卡': Monitor,
'机箱': Box,
}
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)
const [selectedType, setSelectedType] = useState<string | null>(null)
const [searchTerm, setSearchTerm] = useState('')
useEffect(() => {
loadData()
}, [])
const loadData = async () => {
try {
// 加载配件类型
const typesResponse = await fetch('/api/component-types')
if (typesResponse.ok) {
const types = await typesResponse.json()
setComponentTypes(types) // 为每种类型加载组件
const componentsData: { [key: string]: Component[] } = {}
for (const type of types) {
const componentsResponse = await fetch(`/api/components?type=${encodeURIComponent(type.id)}&limit=1000`)
if (componentsResponse.ok) {
const components = await componentsResponse.json()
componentsData[type.name] = components.components || []
}
}
setAvailableComponents(componentsData)
}
} catch (error) {
console.error('加载数据失败:', error)
} finally {
setIsLoading(false)
}
}
const selectComponent = (typeName: string, component: Component) => {
setBuildConfig(prev => ({
...prev,
[typeName]: component
}))
setSelectedType(null)
setSearchTerm('')
}
const removeComponent = (typeName: string) => {
setBuildConfig(prev => ({
...prev,
[typeName]: null
}))
}
const getTotalPrice = () => {
return Object.values(buildConfig).reduce((total, component) => {
return total + (component?.price || 0)
}, 0)
}
const getCompletionRate = () => {
const totalTypes = componentTypes.length
const selectedTypes = Object.values(buildConfig).filter(Boolean).length
return totalTypes > 0 ? Math.round((selectedTypes / totalTypes) * 100) : 0
}
const addAllToCart = async () => {
const selectedComponents = Object.values(buildConfig).filter(Boolean)
if (selectedComponents.length === 0) {
alert('请先选择配件')
return
}
const token = localStorage.getItem('token')
if (!token) {
alert('请先登录')
return
}
try {
// 批量添加到购物车
for (const component of selectedComponents) {
await fetch('/api/cart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
componentId: component!.id,
quantity: 1
})
})
}
window.dispatchEvent(new Event('cart-updated'))
alert('配置已添加到购物车!')
} catch (error) {
console.error('添加到购物车失败:', error)
alert('添加失败,请重试')
}
}
const createOrder = async () => {
const selectedComponents = Object.values(buildConfig).filter(Boolean)
if (selectedComponents.length !== componentTypes.length) {
alert('请完成所有配件的选择后再下单')
return
}
try {
const user = JSON.parse(localStorage.getItem('user') || 'null')
if (!user) {
alert('请先登录')
window.location.href = '/login'
return
}
const orderItems = selectedComponents.map(component => ({
componentId: component!.id,
quantity: 1,
price: component!.price
}))
const response = await fetch('/api/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify({
items: orderItems,
totalAmount: getTotalPrice()
})
})
if (response.ok) {
alert('订单创建成功!')
setBuildConfig({})
window.location.href = '/orders'
} else {
alert('订单创建失败,请重试')
}
} catch (error) {
console.error('创建订单失败:', error)
alert('订单创建失败,请重试')
}
}
const getFilteredComponents = (typeName: string) => {
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())
)
}
if (isLoading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-600">加载中...</p>
</div>
</div>
)
}
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header */}
<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">
<span>配置进度</span>
<span>{getCompletionRate()}%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${getCompletionRate()}%` }}
></div>
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
{/* Components Selection */}
<div className="lg:col-span-3">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{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">
<div className="flex items-center space-x-3">
<Icon className="h-6 w-6 text-blue-600" />
<div>
<h3 className="font-semibold text-gray-900">{type.name}</h3>
<p className="text-sm text-gray-600">{type.description}</p>
</div>
</div>
{selectedComponent && (
<Check className="h-6 w-6 text-green-600" />
)}
</div>
{selectedComponent ? (
<div className="space-y-4">
<div className="border rounded-lg p-4 bg-green-50">
<div className="flex items-center space-x-4">
<div className="w-16 h-16 bg-gray-100 rounded-lg flex items-center justify-center flex-shrink-0">
{selectedComponent.imageUrl ? (
<img loading='lazy'
src={selectedComponent.imageUrl}
alt={selectedComponent.name}
className="max-w-full max-h-full object-contain"
/>
) : (
<Package className="h-8 w-8 text-gray-400" />
)}
</div>
<div className="flex-1">
<h4 className="font-medium text-gray-900">{selectedComponent.name}</h4>
<p className="text-sm text-gray-600">{selectedComponent.brand}</p>
<p className="text-lg font-bold text-red-600">¥{selectedComponent.price}</p>
</div>
</div>
</div>
<div className="flex space-x-2">
<button
onClick={() => setSelectedType(type.name)}
className="flex-1 text-blue-600 border border-blue-600 py-2 px-4 rounded-lg hover:bg-blue-50 transition-colors"
>
更换
</button>
<button
onClick={() => removeComponent(type.name)}
className="text-red-600 border border-red-600 py-2 px-4 rounded-lg hover:bg-red-50 transition-colors"
>
移除
</button>
</div>
</div>
) : (
<button
onClick={() => setSelectedType(type.name)}
className="w-full border-2 border-dashed border-gray-300 rounded-lg py-8 text-gray-500 hover:border-blue-300 hover:text-blue-600 transition-colors"
>
<Plus className="h-8 w-8 mx-auto mb-2" />
<p>选择 {type.name}</p>
</button>
)}
</div>
)
})}
</div>
</div>
{/* Summary */}
<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]
return (
<div key={type.id} className="flex justify-between text-sm">
<span className="text-gray-600">{type.name}:</span>
<span className={component ? "text-gray-900 font-medium" : "text-gray-400"}>
{component ? `¥${component.price}` : '未选择'}
</span>
</div>
)
})}
<div className="border-t border-gray-200 pt-3">
<div className="flex justify-between font-semibold">
<span>总价:</span>
<span className="text-red-600">¥{getTotalPrice().toFixed(2)}</span>
</div>
</div>
</div>
<div className="space-y-3">
<button
onClick={addAllToCart}
disabled={Object.values(buildConfig).filter(Boolean).length === 0}
className="w-full bg-blue-600 text-white py-3 px-4 rounded-lg hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors"
>
<ShoppingCart className="h-5 w-5 inline mr-2" />
加入购物车
</button>
<button
onClick={createOrder}
disabled={getCompletionRate() < 100}
className="w-full border border-green-600 text-green-600 py-3 px-4 rounded-lg hover:bg-green-50 disabled:border-gray-300 disabled:text-gray-400 disabled:cursor-not-allowed transition-colors"
>
直接下单
</button>
</div>
<p className="text-xs text-gray-500 mt-4">
完成度: {getCompletionRate()}% ({Object.values(buildConfig).filter(Boolean).length}/{componentTypes.length})
</p>
</div>
</div>
</div>
{/* 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>
{/* 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"
/>
{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="mt-4 text-blue-600 hover:text-blue-800 font-medium"
>
清除搜索
</button>
)}
</div>
)}
</div>
</div>
</div>
)}
</div>
</div>
)
}
+393
View File
@@ -0,0 +1,393 @@
'use client'
import { useState, useEffect } from 'react'
import Link from 'next/link'
import { Trash2, Plus, Minus, ShoppingBag, ArrowLeft } from 'lucide-react'
interface CartItem {
id: string
quantity: number
component: {
id: string
name: string
brand: string
price: number
imageUrl?: string | null
stock: number
componentType: {
name: string
}
}
}
export default function CartPage() {
const [cartItems, setCartItems] = useState<CartItem[]>([])
const [isLoading, setIsLoading] = useState(true)
const [isUpdating, setIsUpdating] = useState<string | null>(null)
useEffect(() => {
loadCartItems()
}, [])
const loadCartItems = async () => {
try {
const token = localStorage.getItem('token')
if (!token) {
setIsLoading(false)
return
}
const response = await fetch('/api/cart', {
headers: {
'Authorization': `Bearer ${token}`
}
})
if (response.ok) {
const items = await response.json()
setCartItems(items)
} else if (response.status === 401) {
// 登录过期,清除token
localStorage.removeItem('token')
localStorage.removeItem('user')
window.location.href = '/login'
}
} catch (error) {
console.error('加载购物车失败:', error)
} finally {
setIsLoading(false)
}
}
const updateQuantity = async (cartItemId: string, newQuantity: number) => {
if (newQuantity < 1) return
setIsUpdating(cartItemId)
try {
const token = localStorage.getItem('token')
const response = await fetch(`/api/cart/${cartItemId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ quantity: newQuantity })
})
if (response.ok) {
// 重新加载购物车
await loadCartItems()
} else {
const data = await response.json()
alert(data.message || '更新失败')
}
} catch (error) {
console.error('更新数量失败:', error)
alert('更新失败')
} finally {
setIsUpdating(null)
}
}
const removeFromCart = async (cartItemId: string) => {
if (!confirm('确定要从购物车中移除这个商品吗?')) return
setIsUpdating(cartItemId)
try {
const token = localStorage.getItem('token')
const response = await fetch(`/api/cart/${cartItemId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`
}
})
if (response.ok) {
// 重新加载购物车
await loadCartItems()
} else {
const data = await response.json()
alert(data.message || '删除失败')
}
} catch (error) {
console.error('删除失败:', error)
alert('删除失败')
} finally {
setIsUpdating(null)
}
}
const clearCart = async () => {
if (!confirm('确定要清空购物车吗?')) return
try {
const token = localStorage.getItem('token')
const response = await fetch('/api/cart', {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`
}
})
if (response.ok) {
setCartItems([])
} else {
const data = await response.json()
alert(data.message || '清空失败')
}
} catch (error) {
console.error('清空购物车失败:', error)
alert('清空失败')
}
}
const getTotalPrice = () => {
return cartItems.reduce((total, item) => {
return total + (item.component.price * item.quantity)
}, 0)
}
const getTotalItems = () => {
return cartItems.reduce((total, item) => total + item.quantity, 0)
}
const createOrder = async () => {
if (cartItems.length === 0) {
alert('购物车为空')
return
}
try {
const user = JSON.parse(localStorage.getItem('user') || 'null')
if (!user) {
alert('请先登录')
window.location.href = '/login'
return
}
const orderItems = cartItems.map(item => ({
componentId: item.component.id,
quantity: item.quantity,
price: item.component.price
}))
const response = await fetch('/api/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify({
items: orderItems,
totalAmount: getTotalPrice()
})
})
if (response.ok) {
// 清空购物车
await clearCart()
alert('订单创建成功!')
window.location.href = '/orders'
} else {
const data = await response.json()
alert(data.message || '订单创建失败')
}
} catch (error) {
console.error('创建订单失败:', error)
alert('订单创建失败,请重试')
}
}
if (isLoading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-600">加载中...</p>
</div>
</div>
)
}
const user = JSON.parse(localStorage.getItem('user') || 'null')
if (!user) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<ShoppingBag className="h-16 w-16 text-gray-300 mx-auto mb-4" />
<h2 className="text-2xl font-semibold text-gray-900 mb-4">请先登录</h2>
<p className="text-gray-600 mb-8">登录后即可查看您的购物车</p>
<Link
href="/login"
className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
>
立即登录
</Link>
</div>
</div>
)
}
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header */}
<div className="flex items-center justify-between mb-8">
<div className="flex items-center space-x-4">
<Link
href="/components"
className="flex items-center text-blue-600 hover:text-blue-800"
>
<ArrowLeft className="h-5 w-5 mr-2" />
继续购物
</Link>
<h1 className="text-3xl font-bold text-gray-900">购物车</h1>
</div>
{cartItems.length > 0 && (
<button
onClick={clearCart}
className="text-red-600 hover:text-red-800 text-sm font-medium"
>
清空购物车
</button>
)}
</div>
{cartItems.length === 0 ? (
<div className="text-center py-16">
<ShoppingBag className="h-16 w-16 text-gray-300 mx-auto mb-4" />
<h2 className="text-2xl font-semibold text-gray-900 mb-4">购物车为空</h2>
<p className="text-gray-600 mb-8">还没有添加任何商品到购物车</p>
<Link
href="/components"
className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
>
开始购物
</Link>
</div>
) : (
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Cart Items */}
<div className="lg:col-span-2">
<div className="bg-white rounded-lg shadow-sm">
<div className="p-6 border-b border-gray-200">
<h2 className="text-lg font-semibold text-gray-900">
商品列表 ({getTotalItems()} 件商品)
</h2>
</div>
<div className="divide-y divide-gray-200">
{cartItems.map((item) => (
<div key={item.id} className="p-6">
<div className="flex items-center space-x-4">
<div className="flex-shrink-0 w-20 h-20 bg-gray-100 rounded-lg overflow-hidden">
{item.component.imageUrl ? (
<img
src={item.component.imageUrl}
alt={item.component.name}
className="w-full h-full object-cover"
/>
) : (
<div className="w-full h-full flex items-center justify-center">
<ShoppingBag className="h-8 w-8 text-gray-400" />
</div>
)}
</div>
<div className="flex-1">
<div className="flex justify-between">
<div>
<h3 className="font-medium text-gray-900">{item.component.name}</h3>
<p className="text-sm text-gray-600">{item.component.brand}</p>
<p className="text-xs text-gray-500">{item.component.componentType.name}</p>
</div>
<div className="text-right">
<p className="font-semibold text-gray-900">¥{item.component.price}</p>
<p className="text-sm text-gray-600">库存: {item.component.stock}</p>
</div>
</div>
<div className="flex items-center justify-between mt-4">
s <div className="flex items-center space-x-2">
<button
onClick={() => updateQuantity(item.id, item.quantity - 1)}
disabled={item.quantity <= 1 || isUpdating === item.id}
className="p-1 rounded-md hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed"
>
<Minus className="h-4 w-4" />
</button>
<span className="w-12 text-center">{item.quantity}</span>
<button
onClick={() => updateQuantity(item.id, item.quantity + 1)}
disabled={item.quantity >= item.component.stock || isUpdating === item.id}
className="p-1 rounded-md hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed"
>
<Plus className="h-4 w-4" />
</button>
</div>
<div className="flex items-center space-x-4">
<p className="font-semibold text-lg">
¥{(item.component.price * item.quantity).toFixed(2)}
</p>
<button
onClick={() => removeFromCart(item.id)}
disabled={isUpdating === item.id}
className="text-red-600 hover:text-red-800 disabled:opacity-50"
>
<Trash2 className="h-5 w-5" />
</button>
</div>
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
{/* Order Summary */}
<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">
<div className="flex justify-between">
<span className="text-gray-600">商品总数:</span>
<span className="font-medium">{getTotalItems()} 件</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">商品总价:</span>
<span className="font-medium">¥{getTotalPrice().toFixed(2)}</span>
</div>
<div className="border-t border-gray-200 pt-3">
<div className="flex justify-between">
<span className="font-semibold text-lg">总计:</span>
<span className="font-semibold text-lg text-red-600">
¥{getTotalPrice().toFixed(2)}
</span>
</div>
</div>
</div>
<button
onClick={createOrder}
className="w-full bg-blue-600 text-white py-3 px-4 rounded-lg hover:bg-blue-700 transition-colors font-medium"
>
结算订单
</button>
<p className="text-xs text-gray-500 mt-4 text-center">
点击结算将创建订单并清空购物车
</p>
</div>
</div>
</div>
)}
</div>
</div>
)
}
+255
View File
@@ -0,0 +1,255 @@
import { notFound } from 'next/navigation'
import { prisma } from '@/lib/prisma'
import { AddToCartButton } from '@/components/AddToCartButton'
import { ArrowLeft, Package, Star, Shield, Truck, RotateCcw } from 'lucide-react'
import Link from 'next/link'
interface ComponentDetailPageProps {
params: Promise<{
id: string
}>
}
export default async function ComponentDetailPage({ params }: ComponentDetailPageProps) {
const component = await prisma.component.findUnique({
where: { id: (await params).id },
include: {
componentType: true
}
})
if (!component) {
notFound()
}
const specifications = component.specifications ? JSON.parse(component.specifications) : {}
// 获取同类型的其他产品推荐
const relatedComponents = await prisma.component.findMany({
where: {
componentTypeId: component.componentTypeId,
id: { not: component.id }
},
take: 4,
orderBy: {
createdAt: 'desc'
}
})
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Breadcrumb */}
<nav className="flex items-center space-x-2 text-sm text-gray-500 mb-8">
<Link href="/" className="hover:text-blue-600">首页</Link>
<span>/</span>
<Link href="/components" className="hover:text-blue-600">配件商城</Link>
<span>/</span>
<Link href={`/components?type=${component.componentType.name}`} className="hover:text-blue-600">
{component.componentType.name}
</Link>
<span>/</span>
<span className="text-gray-900">{component.name}</span>
</nav>
{/* Back Button */}
<Link
href="/components"
className="inline-flex items-center text-blue-600 hover:text-blue-800 mb-6"
>
<ArrowLeft className="h-4 w-4 mr-2" />
返回商品列表
</Link>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
{/* Product Image */}
<div className="space-y-4">
<div className="aspect-square bg-white rounded-lg border border-gray-200 flex items-center justify-center">
{component.imageUrl ? (
<img loading='lazy'
src={component.imageUrl}
alt={component.name}
className="max-w-full max-h-full object-contain grow"
/>
) : (
<div className="text-center text-gray-400">
<Package className="h-24 w-24 mx-auto mb-4" />
<p>暂无商品图片</p>
</div>
)}
</div>
</div>
{/* Product Info */}
<div className="space-y-6">
<div>
<div className="flex items-center space-x-2 text-sm text-gray-500 mb-2">
<span className="bg-blue-100 text-blue-800 px-2 py-1 rounded">
{component.componentType.name}
</span>
<span className="bg-gray-100 text-gray-800 px-2 py-1 rounded">
{component.brand}
</span>
</div>
<h1 className="text-3xl font-bold text-gray-900 mb-4">
{component.name}
</h1>
<div className="flex items-center space-x-4 mb-4">
<div className="flex items-center">
{[...Array(5)].map((_, i) => (
<Star
key={i}
className={`h-5 w-5 ${i < 4 ? 'text-yellow-400 fill-current' : 'text-gray-300'}`}
/>
))}
<span className="ml-2 text-sm text-gray-600">(128 评价)</span>
</div>
</div>
</div>
{/* Price */}
<div className="border-t border-b border-gray-200 py-6">
<div className="flex items-baseline space-x-2">
<span className="text-3xl font-bold text-red-600">
¥{component.price}
</span>
<span className="text-lg text-gray-500 line-through">
¥{Math.round(component.price * 1.2)}
</span>
<span className="bg-red-100 text-red-800 text-sm px-2 py-1 rounded">
8折
</span>
</div>
<p className="text-sm text-gray-600 mt-2">价格含税,全国包邮</p>
</div>
{/* Stock Status */}
<div className="flex items-center space-x-4">
<span className="text-sm text-gray-600">库存状态:</span>
{component.stock > 0 ? (
<span className="text-green-600 font-medium">
有货 ({component.stock} 件)
</span>
) : (
<span className="text-red-600 font-medium">缺货</span>
)}
</div>
{/* Add to Cart */}
<div className="space-y-4">
<AddToCartButton
componentId={component.id}
disabled={component.stock === 0}
className="w-full"
/>
<button className="w-full border border-gray-300 text-gray-700 py-3 px-6 rounded-lg hover:bg-gray-50 transition-colors">
添加到装机配置
</button>
</div>
{/* Service Promises */}
<div className="grid grid-cols-3 gap-4 pt-6 border-t border-gray-200">
<div className="text-center">
<Truck className="h-8 w-8 text-blue-600 mx-auto mb-2" />
<p className="text-xs text-gray-600">全国包邮</p>
</div>
<div className="text-center">
<Shield className="h-8 w-8 text-green-600 mx-auto mb-2" />
<p className="text-xs text-gray-600">正品保障</p>
</div>
<div className="text-center">
<RotateCcw className="h-8 w-8 text-orange-600 mx-auto mb-2" />
<p className="text-xs text-gray-600">7天无理由退换</p>
</div>
</div>
</div>
</div>
{/* Product Details */}
<div className="mt-12 grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Description */}
<div className="lg:col-span-2">
<h2 className="text-2xl font-bold text-gray-900 mb-6">商品详情</h2>
<div className="bg-white rounded-lg p-6 shadow-sm">
<h3 className="font-semibold text-gray-900 mb-4">产品描述</h3>
<p className="text-gray-700 leading-relaxed mb-6">
{component.description || '暂无详细描述'}
</p>
{Object.keys(specifications).length > 0 && (
<>
<h3 className="font-semibold text-gray-900 mb-4">技术规格</h3>
<div className="space-y-3">
{Object.entries(specifications).map(([key, value]) => (
<div key={key} className="flex justify-between py-2 border-b border-gray-100">
<span className="text-gray-600">{key}:</span>
<span className="font-medium text-gray-900">{String(value)}</span>
</div>
))}
</div>
</>
)}
</div>
</div>
{/* Product Info */}
<div>
<h2 className="text-2xl font-bold text-gray-900 mb-6">产品信息</h2>
<div className="bg-white rounded-lg p-6 shadow-sm space-y-4">
<div className="flex justify-between">
<span className="text-gray-600">品牌:</span>
<span className="font-medium">{component.brand}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">型号:</span>
<span className="font-medium">{component.model}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">分类:</span>
<span className="font-medium">{component.componentType.name}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">库存:</span>
<span className="font-medium">{component.stock} 件</span>
</div>
</div>
</div>
</div>
{/* Related Products */}
{relatedComponents.length > 0 && (
<div className="mt-12">
<h2 className="text-2xl font-bold text-gray-900 mb-6">相关推荐</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
{relatedComponents.map((item) => (
<Link
key={item.id}
href={`/components/${item.id}`}
className="bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow"
>
<div className="p-4">
<div className="aspect-square bg-gray-100 rounded-lg mb-4 flex items-center justify-center">
{item.imageUrl ? (
<img loading='lazy'
src={item.imageUrl}
alt={item.name}
className="max-w-full max-h-full object-contain"
/>
) : (
<Package className="h-12 w-12 text-gray-400" />
)}
</div>
<h3 className="font-medium text-gray-900 mb-2 line-clamp-2">
{item.name}
</h3>
<p className="text-lg font-bold text-red-600">¥{item.price}</p>
</div>
</Link>
))}
</div>
</div>
)}
</div>
</div>
)
}
+294
View File
@@ -0,0 +1,294 @@
'use client'
import { useState, useEffect } from 'react'
import { ComponentCard } from '@/components/ComponentCard'
import { Search, Filter } from 'lucide-react'
interface Component {
id: string
name: string
brand: string
model: string
price: number
description?: string
imageUrl?: string
stock: number
componentType: {
id: string
name: string
}
}
interface ComponentType {
id: string
name: string
_count: {
components: number
}
}
interface Pagination {
page: number
limit: number
total: number
totalPages: number
hasNext: boolean
hasPrev: boolean
}
export default function ComponentsPage() {
const [components, setComponents] = useState<Component[]>([])
const [componentTypes, setComponentTypes] = useState<ComponentType[]>([])
const [pagination, setPagination] = useState<Pagination>({
page: 1,
limit: 12,
total: 0,
totalPages: 0,
hasNext: false,
hasPrev: false
})
const [filters, setFilters] = useState({
search: '',
type: '',
brand: '',
minPrice: '',
maxPrice: ''
})
const [isLoading, setIsLoading] = useState(true)
const [showFilters, setShowFilters] = useState(false)
useEffect(() => {
fetchComponentTypes()
}, [])
useEffect(() => {
fetchComponents()
}, [filters, pagination.page])
const fetchComponentTypes = async () => {
try {
const response = await fetch('/api/component-types')
if (response.ok) {
const data = await response.json()
setComponentTypes(data)
}
} catch (error) {
console.error('Failed to fetch component types:', error)
}
}
const fetchComponents = async () => {
setIsLoading(true)
try {
const searchParams = new URLSearchParams()
if (filters.search) searchParams.set('search', filters.search)
if (filters.type) searchParams.set('type', filters.type)
if (filters.brand) searchParams.set('brand', filters.brand)
if (filters.minPrice) searchParams.set('minPrice', filters.minPrice)
if (filters.maxPrice) searchParams.set('maxPrice', filters.maxPrice)
searchParams.set('page', pagination.page.toString())
searchParams.set('limit', pagination.limit.toString())
const response = await fetch(`/api/components?${searchParams}`)
if (response.ok) {
const data = await response.json()
setComponents(data.components)
setPagination(data.pagination)
}
} catch (error) {
console.error('Failed to fetch components:', error)
} finally {
setIsLoading(false)
}
}
const handleFilterChange = (key: string, value: string) => {
setFilters({ ...filters, [key]: value })
setPagination({ ...pagination, page: 1 })
}
const handlePageChange = (newPage: number) => {
setPagination({ ...pagination, page: newPage })
window.scrollTo({ top: 0, behavior: 'smooth' })
}
const resetFilters = () => {
setFilters({
search: '',
type: '',
brand: '',
minPrice: '',
maxPrice: ''
})
}
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900 mb-4">电脑配件商城</h1>
{/* Search Bar */}
<div className="flex flex-col md:flex-row gap-4 mb-6">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-5 w-5" />
<input
type="text"
placeholder="搜索配件名称、品牌、型号..."
value={filters.search}
onChange={(e) => handleFilterChange('search', e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<button
onClick={() => setShowFilters(!showFilters)}
className="flex items-center gap-2 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50"
>
<Filter className="h-5 w-5" />
筛选
</button>
</div>
{/* Filters */}
{showFilters && (
<div className="bg-white p-6 rounded-lg shadow-sm border mb-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
配件类型
</label>
<select
value={filters.type}
onChange={(e) => handleFilterChange('type', e.target.value)}
className="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="">全部类型</option>
{componentTypes.map((type) => (
<option key={type.id} value={type.id}>
{type.name} ({type._count.components})
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
品牌
</label>
<input
type="text"
placeholder="品牌名称"
value={filters.brand}
onChange={(e) => handleFilterChange('brand', e.target.value)}
className="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
最低价格
</label>
<input
type="number"
placeholder="0"
value={filters.minPrice}
onChange={(e) => handleFilterChange('minPrice', e.target.value)}
className="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
最高价格
</label>
<input
type="number"
placeholder="10000"
value={filters.maxPrice}
onChange={(e) => handleFilterChange('maxPrice', e.target.value)}
className="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500"
/>
</div>
</div>
<div className="mt-4 flex justify-end">
<button
onClick={resetFilters}
className="px-4 py-2 text-gray-600 hover:text-gray-800"
>
重置筛选
</button>
</div>
</div>
)}
</div>
{/* Results */}
<div className="mb-6">
<p className="text-gray-600">
共找到 {pagination.total} 个配件
</p>
</div>
{/* Loading */}
{isLoading ? (
<div className="flex justify-center items-center py-12">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
) : (
<>
{/* Components Grid */}
{components.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 mb-8">
{components.map((component) => (
<ComponentCard key={component.id} component={component} />
))}
</div>
) : (
<div className="text-center py-12">
<p className="text-gray-500 text-lg">没有找到符合条件的配件</p>
</div>
)}
{/* Pagination */}
{pagination.totalPages > 1 && (
<div className="flex justify-center items-center space-x-2">
<button
onClick={() => handlePageChange(pagination.page - 1)}
disabled={!pagination.hasPrev}
className="px-3 py-2 border border-gray-300 rounded-md disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50"
>
上一页
</button>
<div className="flex space-x-1">
{Array.from({ length: pagination.totalPages }, (_, i) => i + 1).map((page) => (
<button
key={page}
onClick={() => handlePageChange(page)}
className={`px-3 py-2 border rounded-md ${
page === pagination.page
? 'bg-blue-600 text-white border-blue-600'
: 'border-gray-300 hover:bg-gray-50'
}`}
>
{page}
</button>
))}
</div>
<button
onClick={() => handlePageChange(pagination.page + 1)}
disabled={!pagination.hasNext}
className="px-3 py-2 border border-gray-300 rounded-md disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50"
>
下一页
</button>
</div>
)}
</>
)}
</div>
</div>
)
}
+8 -4
View File
@@ -1,6 +1,7 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google"; import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css"; import "./globals.css";
import { Navbar } from "@/components/Navbar";
const geistSans = Geist({ const geistSans = Geist({
variable: "--font-geist-sans", variable: "--font-geist-sans",
@@ -13,8 +14,8 @@ const geistMono = Geist_Mono({
}); });
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create Next App", title: "PC DIY商城 - 电脑配件在线选购",
description: "Generated by create next app", description: "专业的电脑DIY配件商城,提供CPU、内存、硬盘、主板、显卡、机箱等优质配件",
}; };
export default function RootLayout({ export default function RootLayout({
@@ -23,11 +24,14 @@ export default function RootLayout({
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
return ( return (
<html lang="en"> <html lang="zh-CN">
<body <body
className={`${geistSans.variable} ${geistMono.variable} antialiased`} className={`${geistSans.variable} ${geistMono.variable} antialiased`}
> >
{children} <Navbar />
<main className="min-h-screen">
{children}
</main>
</body> </body>
</html> </html>
); );
+130
View File
@@ -0,0 +1,130 @@
'use client'
import { useState } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
export default function LoginPage() {
const [formData, setFormData] = useState({
email: '',
password: ''
})
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState('')
const router = useRouter()
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setIsLoading(true)
setError('')
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
})
const data = await response.json()
if (response.ok) {
localStorage.setItem('token', data.token)
localStorage.setItem('user', JSON.stringify(data.user))
// 触发用户更新事件
window.dispatchEvent(new Event('user-updated'))
router.push('/')
} else {
setError(data.message || '登录失败')
}
} catch (error) {
setError('网络错误,请稍后重试')
} finally {
setIsLoading(false)
}
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({
...formData,
[e.target.name]: e.target.value
})
}
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8">
<div>
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
登录您的账户
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
或者{' '}
<Link href="/register" className="font-medium text-blue-600 hover:text-blue-500">
创建新账户
</Link>
</p>
</div>
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
{error && (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
{error}
</div>
)}
<div className="space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
邮箱地址
</label>
<input
id="email"
name="email"
type="email"
required
value={formData.email}
onChange={handleChange}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入邮箱地址"
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
密码
</label>
<input
id="password"
name="password"
type="password"
required
value={formData.password}
onChange={handleChange}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入密码"
/>
</div>
</div>
<div>
<button
type="submit"
disabled={isLoading}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
>
{isLoading ? '登录中...' : '登录'}
</button>
</div>
<div className="text-center">
<p className="text-sm text-gray-600">
测试账号:admin@pcdiy.com,密码:admin123
</p>
</div>
</form>
</div>
</div>
)
}
+398
View File
@@ -0,0 +1,398 @@
'use client'
import { useState, useEffect } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import {
ArrowLeft,
Package,
Calendar,
DollarSign,
User,
MapPin,
Phone,
Mail,
Truck,
CheckCircle,
XCircle,
Clock
} from 'lucide-react'
interface OrderItem {
id: string
quantity: number
price: number
component: {
id: string
name: string
brand: string
model: string
imageUrl?: string
componentType: {
name: string
}
}
}
interface Order {
id: string
orderNumber: string
totalAmount: number
status: string
createdAt: string
updatedAt: string
orderItems: OrderItem[]
user: {
username: string
email: string
}
}
const statusMap: { [key: string]: { text: string; color: string; icon: any } } = {
PENDING: { text: '待确认', color: 'bg-yellow-100 text-yellow-800 border-yellow-200', icon: Clock },
CONFIRMED: { text: '已确认', color: 'bg-blue-100 text-blue-800 border-blue-200', icon: CheckCircle },
PROCESSING: { text: '处理中', color: 'bg-purple-100 text-purple-800 border-purple-200', icon: Package },
SHIPPED: { text: '已发货', color: 'bg-green-100 text-green-800 border-green-200', icon: Truck },
DELIVERED: { text: '已送达', color: 'bg-green-100 text-green-800 border-green-200', icon: CheckCircle },
CANCELLED: { text: '已取消', color: 'bg-red-100 text-red-800 border-red-200', icon: XCircle },
}
export default function OrderDetailPage({ params }: { params: Promise<{ id: string }> }) {
const [order, setOrder] = useState<Order | null>(null)
const [isLoading, setIsLoading] = useState(true)
const [isCancelling, setIsCancelling] = useState(false)
const [orderId, setOrderId] = useState<string>('')
const router = useRouter()
useEffect(() => {
const getParams = async () => {
const { id } = await params
setOrderId(id)
}
getParams()
}, [params])
useEffect(() => {
if (orderId) {
loadOrderDetail()
}
}, [orderId])
const loadOrderDetail = async () => {
try {
const token = localStorage.getItem('token')
if (!token) {
router.push('/login')
return
}
const response = await fetch(`/api/orders/${(await params).id}`, {
headers: {
'Authorization': `Bearer ${token}`
}
})
if (response.ok) {
const data = await response.json()
setOrder(data)
} else if (response.status === 401) {
localStorage.removeItem('token')
localStorage.removeItem('user')
router.push('/login')
} else if (response.status === 404) {
router.push('/orders')
}
} catch (error) {
console.error('加载订单详情失败:', error)
} finally {
setIsLoading(false)
}
}
const handleCancelOrder = async () => {
if (!order || order.status !== 'PENDING') {
alert('当前订单状态不允许取消')
return
}
if (!confirm('确定要取消这个订单吗?取消后将恢复商品库存。')) {
return
}
setIsCancelling(true)
try {
const token = localStorage.getItem('token')
const response = await fetch(`/api/orders/${orderId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ action: 'cancel' })
})
if (response.ok) {
const updatedOrder = await response.json()
setOrder(updatedOrder)
alert('订单已成功取消')
} else {
const error = await response.json()
alert(error.message || '取消订单失败')
}
} catch (error) {
console.error('取消订单失败:', error)
alert('取消订单失败,请重试')
} finally {
setIsCancelling(false)
}
}
const handleReorder = async () => {
if (!order) return
const token = localStorage.getItem('token')
if (!token) {
alert('请先登录')
return
}
try {
// 批量添加订单商品到购物车
for (const item of order.orderItems) {
await fetch('/api/cart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
componentId: item.component.id,
quantity: item.quantity
})
})
}
window.dispatchEvent(new Event('cart-updated'))
alert('商品已添加到购物车!')
router.push('/cart')
} catch (error) {
console.error('添加到购物车失败:', error)
alert('添加失败,请重试')
}
}
if (isLoading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-600">加载中...</p>
</div>
</div>
)
}
if (!order) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<Package className="h-24 w-24 text-gray-300 mx-auto mb-6" />
<h2 className="text-2xl font-semibold text-gray-900 mb-4">订单不存在</h2>
<Link
href="/orders"
className="inline-flex items-center bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
>
<ArrowLeft className="h-5 w-5 mr-2" />
返回订单列表
</Link>
</div>
</div>
)
}
const statusConfig = statusMap[order.status] || { text: order.status, color: 'bg-gray-100 text-gray-800 border-gray-200', icon: Package }
const StatusIcon = statusConfig.icon
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header */}
<div className="flex items-center justify-between mb-8">
<div className="flex items-center space-x-4">
<Link
href="/orders"
className="text-gray-600 hover:text-gray-900 transition-colors"
>
<ArrowLeft className="h-6 w-6" />
</Link>
<div>
<h1 className="text-3xl font-bold text-gray-900">订单详情</h1>
<p className="text-gray-600">订单号: {order.orderNumber}</p>
</div>
</div>
</div>
<div className="space-y-6">
{/* Order Status */}
<div className="bg-white rounded-lg shadow-sm p-6">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
<div className={`p-3 rounded-full ${statusConfig.color.replace('text-', 'bg-').replace('bg-', 'bg-').replace('-100', '-200')}`}>
<StatusIcon className="h-6 w-6" />
</div>
<div>
<h2 className="text-xl font-semibold text-gray-900">订单状态</h2>
<span className={`inline-flex px-3 py-1 text-sm font-medium rounded-full border ${statusConfig.color}`}>
{statusConfig.text}
</span>
</div>
</div>
<div className="text-right">
<p className="text-sm text-gray-600">下单时间</p>
<p className="font-medium text-gray-900">
{new Date(order.createdAt).toLocaleString('zh-CN')}
</p>
{order.updatedAt !== order.createdAt && (
<>
<p className="text-sm text-gray-600 mt-2">更新时间</p>
<p className="font-medium text-gray-900">
{new Date(order.updatedAt).toLocaleString('zh-CN')}
</p>
</>
)}
</div>
</div>
</div>
{/* Order Items */}
<div className="bg-white rounded-lg shadow-sm overflow-hidden">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">订单商品</h3>
</div>
<div className="p-6">
<div className="space-y-6">
{order.orderItems.map((item, index) => (
<div key={item.id} className={`flex items-center space-x-4 ${index > 0 ? 'pt-6 border-t border-gray-200' : ''}`}>
<div className="w-20 h-20 bg-gray-100 rounded-lg flex items-center justify-center flex-shrink-0">
{item.component?.imageUrl ? (
<img loading='lazy'
src={item.component.imageUrl}
alt={item.component?.name || '商品图片'}
className="max-w-full max-h-full object-contain"
/>
) : (
<Package className="h-10 w-10 text-gray-400" />
)}
</div>
<div className="flex-1">
<h4 className="font-medium text-gray-900 text-lg">{item.component?.name || '未知商品'}</h4>
<div className="mt-1 space-y-1">
<p className="text-sm text-gray-600">
品牌: {item.component?.brand || '未知品牌'}
</p>
<p className="text-sm text-gray-600">
型号: {item.component?.model || '未知型号'}
</p>
<p className="text-sm text-gray-600">
类型: {item.component?.componentType?.name || '未知类型'}
</p>
</div>
</div>
<div className="text-right">
<p className="text-lg font-medium text-gray-900">¥{item.price}</p>
<p className="text-sm text-gray-600">数量: {item.quantity}</p>
<p className="text-sm font-medium text-red-600">
小计: ¥{(item.price * item.quantity).toFixed(2)}
</p>
</div>
</div>
))}
</div>
</div>
</div>
{/* Order Summary */}
<div className="bg-white rounded-lg shadow-sm p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">订单汇总</h3>
<div className="space-y-3">
<div className="flex justify-between text-sm">
<span className="text-gray-600">商品数量:</span>
<span className="text-gray-900">{order.orderItems.length} 件</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-600">商品总价:</span>
<span className="text-gray-900">¥{order.totalAmount}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-600">运费:</span>
<span className="text-gray-900">免费</span>
</div>
<div className="border-t border-gray-200 pt-3">
<div className="flex justify-between">
<span className="text-lg font-semibold text-gray-900">实付款:</span>
<span className="text-xl font-bold text-red-600">¥{order.totalAmount}</span>
</div>
</div>
</div>
</div>
{/* User Info */}
<div className="bg-white rounded-lg shadow-sm p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">用户信息</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="flex items-center space-x-3">
<User className="h-5 w-5 text-gray-400" />
<div>
<p className="text-sm text-gray-600">用户名</p>
<p className="font-medium text-gray-900">{order.user.username}</p>
</div>
</div>
<div className="flex items-center space-x-3">
<Mail className="h-5 w-5 text-gray-400" />
<div>
<p className="text-sm text-gray-600">邮箱</p>
<p className="font-medium text-gray-900">{order.user.email}</p>
</div>
</div>
</div>
</div>
{/* Actions */}
<div className="bg-white rounded-lg shadow-sm p-6">
<div className="flex justify-between items-center">
<div className="text-sm text-gray-600">
<Calendar className="h-4 w-4 inline mr-1" />
订单创建于 {new Date(order.createdAt).toLocaleDateString('zh-CN')}
</div>
<div className="flex space-x-3">
{order.status === 'PENDING' && (
<button
onClick={handleCancelOrder}
disabled={isCancelling}
className="bg-red-600 text-white px-4 py-2 rounded-lg hover:bg-red-700 disabled:bg-red-300 disabled:cursor-not-allowed transition-colors"
>
{isCancelling ? '取消中...' : '取消订单'}
</button>
)}
{order.status === 'DELIVERED' && (
<button
onClick={handleReorder}
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
>
再次购买
</button>
)}
<Link
href="/orders"
className="bg-gray-100 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-200 transition-colors"
>
返回订单列表
</Link>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
+334
View File
@@ -0,0 +1,334 @@
'use client'
import { useState, useEffect } from 'react'
import Link from 'next/link'
import { Package, Calendar, DollarSign, Eye, Filter } from 'lucide-react'
interface OrderItem {
id: string
quantity: number
price: number
component: {
id: string
name: string
brand: string
imageUrl?: string
componentType: {
name: string
}
}
}
interface Order {
id: string
orderNumber: string
totalAmount: number
status: string
createdAt: string
orderItems: OrderItem[]
}
const statusMap: { [key: string]: { text: string; color: string } } = {
PENDING: { text: '待确认', color: 'bg-yellow-100 text-yellow-800' },
CONFIRMED: { text: '已确认', color: 'bg-blue-100 text-blue-800' },
PROCESSING: { text: '处理中', color: 'bg-purple-100 text-purple-800' },
SHIPPED: { text: '已发货', color: 'bg-green-100 text-green-800' },
DELIVERED: { text: '已送达', color: 'bg-green-100 text-green-800' },
CANCELLED: { text: '已取消', color: 'bg-red-100 text-red-800' },
}
export default function OrdersPage() {
const [orders, setOrders] = useState<Order[]>([])
const [isLoading, setIsLoading] = useState(true)
const [selectedStatus, setSelectedStatus] = useState<string>('ALL')
const [cancellingOrderId, setCancellingOrderId] = useState<string | null>(null)
useEffect(() => {
loadOrders()
}, [])
const loadOrders = async () => {
try {
const token = localStorage.getItem('token')
if (!token) {
window.location.href = '/login'
return
}
const response = await fetch('/api/orders', {
headers: {
'Authorization': `Bearer ${token}`
}
})
if (response.ok) {
const data = await response.json()
setOrders(data)
} else if (response.status === 401) {
localStorage.removeItem('token')
localStorage.removeItem('user')
window.location.href = '/login'
}
} catch (error) {
console.error('加载订单失败:', error)
} finally {
setIsLoading(false)
}
}
const filteredOrders = selectedStatus === 'ALL'
? orders
: orders.filter(order => order.status === selectedStatus)
const handleCancelOrder = async (orderId: string) => {
if (!confirm('确定要取消这个订单吗?取消后将恢复商品库存。')) {
return
}
setCancellingOrderId(orderId)
try {
const token = localStorage.getItem('token')
const response = await fetch(`/api/orders/${orderId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ action: 'cancel' })
})
if (response.ok) {
// 重新加载订单列表
await loadOrders()
alert('订单已成功取消')
} else {
const error = await response.json()
alert(error.message || '取消订单失败')
}
} catch (error) {
console.error('取消订单失败:', error)
alert('取消订单失败,请重试')
} finally {
setCancellingOrderId(null)
} }
const handleReorder = async (order: Order) => {
const token = localStorage.getItem('token')
if (!token) {
alert('请先登录')
return
}
try {
// 批量添加订单商品到购物车
for (const item of order.orderItems) {
await fetch('/api/cart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
componentId: item.component.id,
quantity: item.quantity
})
})
}
window.dispatchEvent(new Event('cart-updated'))
alert('商品已添加到购物车!')
window.location.href = '/cart'
} catch (error) {
console.error('添加到购物车失败:', error)
alert('添加失败,请重试')
}
}
if (isLoading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-600">加载中...</p>
</div>
</div>
)
}
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header */}
<div className="flex justify-between items-center mb-8">
<h1 className="text-3xl font-bold text-gray-900">我的订单</h1>
<Link
href="/components"
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
>
继续购物
</Link>
</div>
{/* Status Filter */}
<div className="bg-white rounded-lg shadow-sm p-4 mb-6">
<div className="flex items-center space-x-4">
<Filter className="h-5 w-5 text-gray-600" />
<span className="text-gray-700">筛选:</span>
<div className="flex flex-wrap gap-2">
<button
onClick={() => setSelectedStatus('ALL')}
className={`px-3 py-1 rounded-full text-sm transition-colors ${
selectedStatus === 'ALL'
? 'bg-blue-600 text-white'
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
}`}
>
全部
</button>
{Object.entries(statusMap).map(([status, config]) => (
<button
key={status}
onClick={() => setSelectedStatus(status)}
className={`px-3 py-1 rounded-full text-sm transition-colors ${
selectedStatus === status
? 'bg-blue-600 text-white'
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
}`}
>
{config.text}
</button>
))}
</div>
</div>
</div>
{/* Orders List */}
{filteredOrders.length === 0 ? (
<div className="text-center py-16">
<Package className="h-24 w-24 text-gray-300 mx-auto mb-6" />
<h2 className="text-2xl font-semibold text-gray-900 mb-4">
{selectedStatus === 'ALL' ? '暂无订单' : '暂无该状态的订单'}
</h2>
<p className="text-gray-600 mb-8">
{selectedStatus === 'ALL'
? '还没有任何订单,去看看有什么好东西吧!'
: '可以试试其他状态筛选'
}
</p>
<Link
href="/components"
className="inline-flex items-center bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
>
<Package className="h-5 w-5 mr-2" />
去购物
</Link>
</div>
) : (
<div className="space-y-6">
{filteredOrders.map((order) => (
<div key={order.id} className="bg-white rounded-lg shadow-sm overflow-hidden">
{/* Order Header */}
<div className="bg-gray-50 px-6 py-4 border-b border-gray-200">
<div className="flex justify-between items-center">
<div className="flex items-center space-x-6">
<div>
<p className="text-sm text-gray-600">订单号</p>
<p className="font-medium text-gray-900">{order.orderNumber}</p>
</div>
<div>
<p className="text-sm text-gray-600">下单时间</p>
<p className="font-medium text-gray-900">
{new Date(order.createdAt).toLocaleString('zh-CN')}
</p>
</div>
<div>
<p className="text-sm text-gray-600">订单状态</p>
<span className={`inline-flex px-2 py-1 text-xs font-medium rounded-full ${
statusMap[order.status]?.color || 'bg-gray-100 text-gray-800'
}`}>
{statusMap[order.status]?.text || order.status}
</span>
</div>
</div>
<div className="text-right">
<p className="text-sm text-gray-600">订单金额</p>
<p className="text-xl font-bold text-red-600">¥{order.totalAmount.toFixed(2)}</p>
</div>
</div>
</div>
{/* Order Items */}
<div className="p-6">
<div className="space-y-4">
{order.orderItems.map((item) => (
<div key={item.id} className="flex items-center space-x-4"> <div className="w-16 h-16 bg-gray-100 rounded-lg flex items-center justify-center flex-shrink-0">
{item.component?.imageUrl ? (
<img loading='lazy'
src={item.component.imageUrl}
alt={item.component?.name || '商品图片'}
className="max-w-full max-h-full object-contain"
/>
) : (
<Package className="h-8 w-8 text-gray-400" />
)}
</div><div className="flex-1">
<h3 className="font-medium text-gray-900">{item.component?.name || '未知商品'}</h3>
<p className="text-sm text-gray-600">
{item.component?.brand || '未知品牌'} | {item.component?.componentType?.name || '未知类型'}
</p>
<p className="text-sm text-gray-600">数量: {item.quantity}</p>
</div>
<div className="text-right">
<p className="font-medium text-gray-900">¥{item.price}</p>
<p className="text-sm text-gray-600">
小计: ¥{(item.price * item.quantity).toFixed(2)}
</p>
</div>
</div>
))}
</div>
{/* Order Actions */}
<div className="flex justify-between items-center mt-6 pt-6 border-t border-gray-200">
<div className="flex items-center space-x-2 text-sm text-gray-600">
<Calendar className="h-4 w-4" />
<span>共 {order.orderItems.length} 件商品</span>
<DollarSign className="h-4 w-4 ml-4" />
<span>实付款: ¥{order.totalAmount}</span>
</div>
<div className="flex space-x-3">
<Link
href={`/orders/${order.id}`}
className="inline-flex items-center text-blue-600 hover:text-blue-800 text-sm"
>
<Eye className="h-4 w-4 mr-1" />
查看详情
</Link> {order.status === 'PENDING' && (
<button
onClick={() => handleCancelOrder(order.id)}
disabled={cancellingOrderId === order.id}
className="text-red-600 hover:text-red-800 text-sm disabled:text-red-300 disabled:cursor-not-allowed"
>
{cancellingOrderId === order.id ? '取消中...' : '取消订单'}
</button>
)}
{order.status === 'DELIVERED' && (
<button
onClick={() => handleReorder(order)}
className="text-blue-600 hover:text-blue-800 text-sm"
>
再次购买
</button>
)}
</div>
</div>
</div>
</div>
))}
</div>
)}
</div>
</div>
)
}
+121 -98
View File
@@ -1,103 +1,126 @@
import Image from "next/image"; import Link from 'next/link'
import { prisma } from '@/lib/prisma'
import { ComponentCard } from '@/components/ComponentCard'
export default async function Home() {
// 获取每种配件类型的前几个商品
const componentTypes = await prisma.componentType.findMany({
include: {
components: {
take: 6,
orderBy: {
createdAt: 'desc'
}
}
}
})
export default function Home() {
return ( return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]"> <div className="min-h-screen bg-gray-50">
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start"> {/* Hero Section */}
<Image <section className="bg-gradient-to-r from-blue-600 to-purple-600 text-white py-20">
className="dark:invert" <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
src="/next.svg" <h1 className="text-4xl md:text-6xl font-bold mb-6">
alt="Next.js logo" 欢迎来到PC DIY商城
width={180} </h1>
height={38} <p className="text-xl md:text-2xl mb-8">
priority 专业的电脑配件在线选购平台,让您轻松打造梦想主机
/> </p>
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]"> <div className="space-x-4">
<li className="mb-2 tracking-[-.01em]"> <Link
Get started by editing{" "} href="/components"
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold"> className="bg-white text-blue-600 px-8 py-3 rounded-lg font-semibold hover:bg-gray-100 transition-colors"
app/page.tsx >
</code> 立即选购
. </Link>
</li> <Link
<li className="tracking-[-.01em]"> href="/build"
Save and see your changes instantly. className="border-2 border-white text-white px-8 py-3 rounded-lg font-semibold hover:bg-white hover:text-blue-600 transition-colors"
</li> >
</ol> 在线装机
</Link>
<div className="flex gap-4 items-center flex-col sm:flex-row"> </div>
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div> </div>
</main> </section>
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
<a {/* Features Section */}
className="flex items-center gap-2 hover:underline hover:underline-offset-4" <section className="py-16">
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
target="_blank" <h2 className="text-3xl font-bold text-center mb-12">为什么选择我们</h2>
rel="noopener noreferrer" <div className="grid md:grid-cols-3 gap-8">
> <div className="text-center">
<Image <div className="bg-blue-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
aria-hidden <span className="text-2xl">🔧</span>
src="/file.svg" </div>
alt="File icon" <h3 className="text-xl font-semibold mb-2">专业配件</h3>
width={16} <p className="text-gray-600">
height={16} 提供CPU、内存、硬盘、主板、显卡、机箱等全系列配件
/> </p>
Learn </div>
</a> <div className="text-center">
<a <div className="bg-green-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
className="flex items-center gap-2 hover:underline hover:underline-offset-4" <span className="text-2xl">💰</span>
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" </div>
target="_blank" <h3 className="text-xl font-semibold mb-2">价格优势</h3>
rel="noopener noreferrer" <p className="text-gray-600">
> 多品牌选择,不同价位满足各种预算需求
<Image </p>
aria-hidden </div>
src="/window.svg" <div className="text-center">
alt="Window icon" <div className="bg-purple-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
width={16} <span className="text-2xl">🚀</span>
height={16} </div>
/> <h3 className="text-xl font-semibold mb-2">在线装机</h3>
Examples <p className="text-gray-600">
</a> 智能配置推荐,一站式完成整机搭配
<a </p>
className="flex items-center gap-2 hover:underline hover:underline-offset-4" </div>
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" </div>
target="_blank" </div>
rel="noopener noreferrer" </section>
>
<Image {/* Product Categories */}
aria-hidden <section className="py-16 bg-white">
src="/globe.svg" <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
alt="Globe icon" <h2 className="text-3xl font-bold text-center mb-12">热门配件</h2>
width={16}
height={16} {componentTypes.map((type) => (
/> <div key={type.id} className="mb-12">
Go to nextjs.org → <div className="flex justify-between items-center mb-6">
</a> <h3 className="text-2xl font-semibold">{type.name}</h3>
</footer> <Link
href={`/components?type=${type.id}`}
className="text-blue-600 hover:text-blue-800"
>
查看更多 →
</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{type.components.map((component) => (
<ComponentCard key={component.id} component={component} />
))}
</div>
</div>
))}
</div>
</section>
{/* CTA Section */}
<section className="py-16 bg-gray-800 text-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<h2 className="text-3xl font-bold mb-4">准备开始装机了吗?</h2>
<p className="text-xl mb-8">
注册账号,享受更多服务和优惠
</p>
<Link
href="/register"
className="bg-blue-600 text-white px-8 py-3 rounded-lg font-semibold hover:bg-blue-700 transition-colors"
>
立即注册
</Link>
</div>
</section>
</div> </div>
); )
} }
+566
View File
@@ -0,0 +1,566 @@
'use client'
import { useState, useEffect } from 'react'
import { User, Mail, Phone, MapPin, Save, Edit, Key } from 'lucide-react'
interface UserProfile {
id: string
email: string
username: string
name?: string
phone?: string
address?: string
}
interface UserStats {
user: {
id: string
username: string
email: string
memberSince: string
}
orderStats: {
total: number
byStatus: {
PENDING: number
CONFIRMED: number
SHIPPED: number
DELIVERED: number
CANCELLED: number
}
totalSpent: number
}
recentOrders: Array<{
id: string
status: string
totalAmount: number
createdAt: string
}>
favoriteComponentType: string | null
summary: {
totalOrders: number
totalSpent: number
pendingOrders: number
completedOrders: number
cartItems: number
}
}
export default function ProfilePage() {
const [user, setUser] = useState<UserProfile | null>(null)
const [userStats, setUserStats] = useState<UserStats | null>(null)
const [isEditing, setIsEditing] = useState(false)
const [isLoading, setIsLoading] = useState(true)
const [isSaving, setIsSaving] = useState(false)
const [formData, setFormData] = useState({
name: '',
phone: '',
address: ''
})
const [passwordForm, setPasswordForm] = useState({
currentPassword: '',
newPassword: '',
confirmPassword: ''
})
const [showPasswordForm, setShowPasswordForm] = useState(false)
useEffect(() => {
loadUserProfile()
loadUserStats()
}, [])
const loadUserProfile = async () => {
try {
const token = localStorage.getItem('token')
if (!token) {
window.location.href = '/login'
return
}
const response = await fetch('/api/user/profile', {
headers: {
'Authorization': `Bearer ${token}`
}
})
if (response.ok) {
const userData = await response.json()
setUser(userData)
setFormData({
name: userData.name || '',
phone: userData.phone || '',
address: userData.address || ''
})
} else if (response.status === 401) {
localStorage.removeItem('token')
localStorage.removeItem('user')
window.location.href = '/login'
}
} catch (error) {
console.error('加载用户资料失败:', error)
} finally {
setIsLoading(false)
}
}
const loadUserStats = async () => {
try {
const token = localStorage.getItem('token')
if (!token) {
return
}
const response = await fetch('/api/user/stats', {
headers: {
'Authorization': `Bearer ${token}`
}
})
if (response.ok) {
const stats = await response.json()
setUserStats(stats)
}
} catch (error) {
console.error('加载用户统计失败:', error)
}
}
const handleSaveProfile = async () => {
setIsSaving(true)
try {
const token = localStorage.getItem('token')
const response = await fetch('/api/user/profile', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(formData)
})
if (response.ok) {
const updatedUser = await response.json()
setUser(updatedUser)
setIsEditing(false)
// 更新localStorage中的用户信息
localStorage.setItem('user', JSON.stringify(updatedUser))
alert('资料更新成功!')
} else {
alert('更新失败,请重试')
}
} catch (error) {
console.error('更新用户资料失败:', error)
alert('更新失败,请重试')
} finally {
setIsSaving(false)
}
}
const handleChangePassword = async () => {
if (passwordForm.newPassword !== passwordForm.confirmPassword) {
alert('新密码和确认密码不匹配')
return
}
if (passwordForm.newPassword.length < 6) {
alert('新密码长度至少6位')
return
}
try {
const token = localStorage.getItem('token')
const response = await fetch('/api/user/change-password', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
currentPassword: passwordForm.currentPassword,
newPassword: passwordForm.newPassword
})
})
if (response.ok) {
alert('密码修改成功!')
setPasswordForm({
currentPassword: '',
newPassword: '',
confirmPassword: ''
})
setShowPasswordForm(false)
} else {
const error = await response.json()
alert(error.message || '密码修改失败')
}
} catch (error) {
console.error('修改密码失败:', error)
alert('修改失败,请重试')
}
}
const handleCancel = () => {
setFormData({
name: user?.name || '',
phone: user?.phone || '',
address: user?.address || ''
})
setIsEditing(false)
}
if (isLoading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-600">加载中...</p>
</div>
</div>
)
}
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header */}
<div className="text-center mb-8">
<div className="w-24 h-24 bg-blue-600 rounded-full flex items-center justify-center mx-auto mb-4">
<User className="h-12 w-12 text-white" />
</div>
<h1 className="text-3xl font-bold text-gray-900">{user?.name || user?.username}</h1>
<p className="text-gray-600">{user?.email}</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Profile Info */}
<div className="lg:col-span-2">
<div className="bg-white rounded-lg shadow-sm p-6">
<div className="flex justify-between items-center mb-6">
<h2 className="text-xl font-semibold text-gray-900">个人资料</h2>
{!isEditing ? (
<button
onClick={() => setIsEditing(true)}
className="inline-flex items-center text-blue-600 hover:text-blue-800"
>
<Edit className="h-4 w-4 mr-2" />
编辑资料
</button>
) : (
<div className="flex space-x-2">
<button
onClick={handleSaveProfile}
disabled={isSaving}
className="inline-flex items-center bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 disabled:opacity-50"
>
<Save className="h-4 w-4 mr-2" />
{isSaving ? '保存中...' : '保存'}
</button>
<button
onClick={handleCancel}
className="text-gray-600 hover:text-gray-800 px-4 py-2"
>
取消
</button>
</div>
)}
</div>
<div className="space-y-6">
{/* Email (readonly) */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
<Mail className="h-4 w-4 inline mr-2" />
邮箱地址
</label>
<input
type="email"
value={user?.email || ''}
disabled
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-gray-50 text-gray-500"
/>
<p className="text-xs text-gray-500 mt-1">邮箱地址不可修改</p>
</div>
{/* Username (readonly) */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
<User className="h-4 w-4 inline mr-2" />
用户名
</label>
<input
type="text"
value={user?.username || ''}
disabled
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-gray-50 text-gray-500"
/>
<p className="text-xs text-gray-500 mt-1">用户名不可修改</p>
</div>
{/* Name */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
姓名
</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({...formData, name: e.target.value})}
disabled={!isEditing}
className={`w-full px-3 py-2 border border-gray-300 rounded-lg ${
isEditing ? 'focus:ring-2 focus:ring-blue-500 focus:border-blue-500' : 'bg-gray-50'
}`}
placeholder="请输入真实姓名"
/>
</div>
{/* Phone */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
<Phone className="h-4 w-4 inline mr-2" />
手机号码
</label>
<input
type="tel"
value={formData.phone}
onChange={(e) => setFormData({...formData, phone: e.target.value})}
disabled={!isEditing}
className={`w-full px-3 py-2 border border-gray-300 rounded-lg ${
isEditing ? 'focus:ring-2 focus:ring-blue-500 focus:border-blue-500' : 'bg-gray-50'
}`}
placeholder="请输入手机号码"
/>
</div>
{/* Address */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
<MapPin className="h-4 w-4 inline mr-2" />
收货地址
</label>
<textarea
value={formData.address}
onChange={(e) => setFormData({...formData, address: e.target.value})}
disabled={!isEditing}
rows={3}
className={`w-full px-3 py-2 border border-gray-300 rounded-lg ${
isEditing ? 'focus:ring-2 focus:ring-blue-500 focus:border-blue-500' : 'bg-gray-50'
}`}
placeholder="请输入详细地址"
/>
</div>
</div>
</div>
{/* Password Change */}
<div className="bg-white rounded-lg shadow-sm p-6 mt-6">
<div className="flex justify-between items-center mb-6">
<h2 className="text-xl font-semibold text-gray-900">安全设置</h2>
<button
onClick={() => setShowPasswordForm(!showPasswordForm)}
className="inline-flex items-center text-blue-600 hover:text-blue-800"
>
<Key className="h-4 w-4 mr-2" />
修改密码
</button>
</div>
{showPasswordForm && (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
当前密码
</label>
<input
type="password"
value={passwordForm.currentPassword}
onChange={(e) => setPasswordForm({...passwordForm, currentPassword: e.target.value})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入当前密码"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
新密码
</label>
<input
type="password"
value={passwordForm.newPassword}
onChange={(e) => setPasswordForm({...passwordForm, newPassword: e.target.value})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入新密码(至少6位)"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
确认新密码
</label>
<input
type="password"
value={passwordForm.confirmPassword}
onChange={(e) => setPasswordForm({...passwordForm, confirmPassword: e.target.value})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="请再次输入新密码"
/>
</div>
<div className="flex space-x-3">
<button
onClick={handleChangePassword}
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"
>
修改密码
</button>
<button
onClick={() => {
setShowPasswordForm(false)
setPasswordForm({
currentPassword: '',
newPassword: '',
confirmPassword: ''
})
}}
className="text-gray-600 hover:text-gray-800 px-4 py-2"
>
取消
</button>
</div>
</div>
)}
</div>
</div>
{/* Quick Actions */}
<div className="lg:col-span-1">
<div className="bg-white rounded-lg shadow-sm p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">快捷操作</h3>
<div className="space-y-3">
<a
href="/orders"
className="block w-full text-left px-4 py-3 text-gray-700 hover:bg-gray-50 rounded-lg transition-colors"
>
查看我的订单
</a>
<a
href="/cart"
className="block w-full text-left px-4 py-3 text-gray-700 hover:bg-gray-50 rounded-lg transition-colors"
>
查看购物车
</a>
<a
href="/build"
className="block w-full text-left px-4 py-3 text-gray-700 hover:bg-gray-50 rounded-lg transition-colors"
>
装机配置
</a>
<a
href="/components"
className="block w-full text-left px-4 py-3 text-gray-700 hover:bg-gray-50 rounded-lg transition-colors"
>
浏览商品
</a>
</div>
</div> {/* Account Stats */}
<div className="bg-white rounded-lg shadow-sm p-6 mt-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">账户统计</h3>
<div className="space-y-3">
<div className="flex justify-between">
<span className="text-gray-600">注册时间:</span>
<span className="font-medium text-gray-900">
{userStats?.user?.memberSince ?
new Date(userStats.user.memberSince).toLocaleDateString('zh-CN') :
'--'
}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">订单数量:</span>
<span className="font-medium text-gray-900">
{userStats?.summary?.totalOrders || 0}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">累计消费:</span>
<span className="font-medium text-red-600">
¥{userStats?.summary?.totalSpent?.toFixed(2) || '0.00'}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">待处理订单:</span>
<span className="font-medium text-orange-600">
{userStats?.summary?.pendingOrders || 0}
</span>
</div> <div className="flex justify-between">
<span className="text-gray-600">已完成订单:</span>
<span className="font-medium text-green-600">
{userStats?.summary?.completedOrders || 0}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">购物车商品:</span>
<span className="font-medium text-blue-600">
{userStats?.summary?.cartItems || 0}
</span>
</div>
{userStats?.favoriteComponentType && (
<div className="flex justify-between">
<span className="text-gray-600">偏好配件:</span>
<span className="font-medium text-blue-600">
{userStats.favoriteComponentType}
</span>
</div> )}
</div>
</div>
{/* Recent Orders */}
{userStats?.recentOrders && userStats.recentOrders.length > 0 && (
<div className="bg-white rounded-lg shadow-sm p-6 mt-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">最近订单</h3>
<div className="space-y-3">
{userStats.recentOrders.map((order) => (
<div key={order.id} className="flex justify-between items-center p-3 border border-gray-200 rounded-lg">
<div>
<p className="font-medium text-gray-900">订单 #{order.id.slice(-8)}</p>
<p className="text-sm text-gray-500">
{new Date(order.createdAt).toLocaleDateString('zh-CN')}
</p>
</div>
<div className="text-right">
<p className="font-medium text-red-600">¥{order.totalAmount.toFixed(2)}</p>
<span className={`text-xs px-2 py-1 rounded-full ${
order.status === 'PENDING' ? 'bg-yellow-100 text-yellow-800' :
order.status === 'CONFIRMED' ? 'bg-blue-100 text-blue-800' :
order.status === 'SHIPPED' ? 'bg-purple-100 text-purple-800' :
order.status === 'PROCESSING' ? 'bg-purple-100 text-purple-800' :
order.status === 'DELIVERED' ? 'bg-green-100 text-green-800' :
'bg-red-100 text-red-800'
}`}>
{order.status === 'PENDING' ? '待确认' :
order.status === 'CONFIRMED' ? '已确认' :
order.status === 'SHIPPED' ? '已发货' :
order.status === 'DELIVERED' ? '已送达' :
order.status === 'PROCESSING' ? '处理中' :
'已取消'}
</span>
</div>
</div>
))}
<div className="text-center">
<a
href="/orders"
className="text-blue-600 hover:text-blue-800 text-sm font-medium"
>
查看全部订单 →
</a>
</div>
</div>
</div>
)}
</div>
</div>
</div>
</div>
)
}
+215
View File
@@ -0,0 +1,215 @@
'use client'
import { useState } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
export default function RegisterPage() {
const [formData, setFormData] = useState({
email: '',
username: '',
password: '',
confirmPassword: '',
name: '',
phone: '',
address: ''
})
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState('')
const router = useRouter()
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setIsLoading(true)
setError('')
if (formData.password !== formData.confirmPassword) {
setError('两次输入的密码不一致')
setIsLoading(false)
return
}
try {
const response = await fetch('/api/auth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: formData.email,
username: formData.username,
password: formData.password,
name: formData.name,
phone: formData.phone,
address: formData.address
}),
})
const data = await response.json()
if (response.ok) {
router.push('/login?message=注册成功,请登录')
} else {
setError(data.message || '注册失败')
}
} catch (error) {
setError('网络错误,请稍后重试')
} finally {
setIsLoading(false)
}
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({
...formData,
[e.target.name]: e.target.value
})
}
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8">
<div>
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
创建您的账户
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
已有账户?{' '}
<Link href="/login" className="font-medium text-blue-600 hover:text-blue-500">
立即登录
</Link>
</p>
</div>
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
{error && (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
{error}
</div>
)}
<div className="space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
邮箱地址 *
</label>
<input
id="email"
name="email"
type="email"
required
value={formData.email}
onChange={handleChange}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入邮箱地址"
/>
</div>
<div>
<label htmlFor="username" className="block text-sm font-medium text-gray-700">
用户名 *
</label>
<input
id="username"
name="username"
type="text"
required
value={formData.username}
onChange={handleChange}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入用户名"
/>
</div>
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
真实姓名
</label>
<input
id="name"
name="name"
type="text"
value={formData.name}
onChange={handleChange}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入真实姓名"
/>
</div>
<div>
<label htmlFor="phone" className="block text-sm font-medium text-gray-700">
联系电话
</label>
<input
id="phone"
name="phone"
type="tel"
value={formData.phone}
onChange={handleChange}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入联系电话"
/>
</div>
<div>
<label htmlFor="address" className="block text-sm font-medium text-gray-700">
收货地址
</label>
<input
id="address"
name="address"
type="text"
value={formData.address}
onChange={handleChange}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入收货地址"
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
密码 *
</label>
<input
id="password"
name="password"
type="password"
required
value={formData.password}
onChange={handleChange}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入密码"
/>
</div>
<div>
<label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700">
确认密码 *
</label>
<input
id="confirmPassword"
name="confirmPassword"
type="password"
required
value={formData.confirmPassword}
onChange={handleChange}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="请再次输入密码"
/>
</div>
</div>
<div>
<button
type="submit"
disabled={isLoading}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50"
>
{isLoading ? '注册中...' : '注册'}
</button>
</div>
</form>
</div>
</div>
)
}
+179
View File
@@ -4,9 +4,20 @@
"": { "": {
"name": "pc-diy-store", "name": "pc-diy-store",
"dependencies": { "dependencies": {
"@prisma/client": "^6.10.1",
"@types/bcryptjs": "^3.0.0",
"@types/jsonwebtoken": "^9.0.10",
"bcryptjs": "^3.0.2",
"chart.js": "^4.5.0",
"jsonwebtoken": "^9.0.2",
"lucide-react": "^0.522.0",
"next": "15.3.4", "next": "15.3.4",
"next-auth": "^4.24.11",
"prisma": "^6.10.1",
"react": "^19.0.0", "react": "^19.0.0",
"react-chartjs-2": "^5.3.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"recharts": "^2.15.4",
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "^4", "@tailwindcss/postcss": "^4",
@@ -23,6 +34,8 @@
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
"@babel/runtime": ["@babel/runtime@7.27.6", "", {}, "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q=="],
"@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="], "@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.1.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg=="], "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.1.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg=="],
@@ -79,6 +92,8 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
"@kurkle/color": ["@kurkle/color@0.3.4", "", {}, "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="],
"@next/env": ["@next/env@15.3.4", "", {}, "sha512-ZkdYzBseS6UjYzz6ylVKPOK+//zLWvD6Ta+vpoye8cW11AjiQjGYVibF0xuvT4L0iJfAPfZLFidaEzAOywyOAQ=="], "@next/env": ["@next/env@15.3.4", "", {}, "sha512-ZkdYzBseS6UjYzz6ylVKPOK+//zLWvD6Ta+vpoye8cW11AjiQjGYVibF0xuvT4L0iJfAPfZLFidaEzAOywyOAQ=="],
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-z0qIYTONmPRbwHWvpyrFXJd5F9YWLCsw3Sjrzj2ZvMYy9NPQMPZ1NjOJh4ojr4oQzcGYwgJKfidzehaNa1BpEg=="], "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-z0qIYTONmPRbwHWvpyrFXJd5F9YWLCsw3Sjrzj2ZvMYy9NPQMPZ1NjOJh4ojr4oQzcGYwgJKfidzehaNa1BpEg=="],
@@ -97,6 +112,22 @@
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.4", "", { "os": "win32", "cpu": "x64" }, "sha512-4kDt31Bc9DGyYs41FTL1/kNpDeHyha2TC0j5sRRoKCyrhNcfZ/nRQkAUlF27mETwm8QyHqIjHJitfcza2Iykfg=="], "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.4", "", { "os": "win32", "cpu": "x64" }, "sha512-4kDt31Bc9DGyYs41FTL1/kNpDeHyha2TC0j5sRRoKCyrhNcfZ/nRQkAUlF27mETwm8QyHqIjHJitfcza2Iykfg=="],
"@panva/hkdf": ["@panva/hkdf@1.2.1", "", {}, "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw=="],
"@prisma/client": ["@prisma/client@6.10.1", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-Re4pMlcUsQsUTAYMK7EJ4Bw2kg3WfZAAlr8GjORJaK4VOP6LxRQUQ1TuLnxcF42XqGkWQ36q5CQF1yVadANQ6w=="],
"@prisma/config": ["@prisma/config@6.10.1", "", { "dependencies": { "jiti": "2.4.2" } }, "sha512-kz4/bnqrOrzWo8KzYguN0cden4CzLJJ+2VSpKtF8utHS3l1JS0Lhv6BLwpOX6X9yNreTbZQZwewb+/BMPDCIYQ=="],
"@prisma/debug": ["@prisma/debug@6.10.1", "", {}, "sha512-k2YT53cWxv9OLjW4zSYTZ6Z7j0gPfCzcr2Mj99qsuvlxr8WAKSZ2NcSR0zLf/mP4oxnYG842IMj3utTgcd7CaA=="],
"@prisma/engines": ["@prisma/engines@6.10.1", "", { "dependencies": { "@prisma/debug": "6.10.1", "@prisma/engines-version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c", "@prisma/fetch-engine": "6.10.1", "@prisma/get-platform": "6.10.1" } }, "sha512-Q07P5rS2iPwk2IQr/rUQJ42tHjpPyFcbiH7PXZlV81Ryr9NYIgdxcUrwgVOWVm5T7ap02C0dNd1dpnNcSWig8A=="],
"@prisma/engines-version": ["@prisma/engines-version@6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c", "", {}, "sha512-ZJFTsEqapiTYVzXya6TUKYDFnSWCNegfUiG5ik9fleQva5Sk3DNyyUi7X1+0ZxWFHwHDr6BZV5Vm+iwP+LlciA=="],
"@prisma/fetch-engine": ["@prisma/fetch-engine@6.10.1", "", { "dependencies": { "@prisma/debug": "6.10.1", "@prisma/engines-version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c", "@prisma/get-platform": "6.10.1" } }, "sha512-clmbG/Jgmrc/n6Y77QcBmAUlq9LrwI9Dbgy4pq5jeEARBpRCWJDJ7PWW1P8p0LfFU0i5fsyO7FqRzRB8mkdS4g=="],
"@prisma/get-platform": ["@prisma/get-platform@6.10.1", "", { "dependencies": { "@prisma/debug": "6.10.1" } }, "sha512-4CY5ndKylcsce9Mv+VWp5obbR2/86SHOLVV053pwIkhVtT9C9A83yqiqI/5kJM9T1v1u1qco/bYjDKycmei9HA=="],
"@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="],
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
@@ -131,20 +162,52 @@
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.10", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.10", "@tailwindcss/oxide": "4.1.10", "postcss": "^8.4.41", "tailwindcss": "4.1.10" } }, "sha512-B+7r7ABZbkXJwpvt2VMnS6ujcDoR2OOcFaqrLIo1xbcdxje4Vf+VgJdBzNNbrAjBj/rLZ66/tlQ1knIGNLKOBQ=="], "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.10", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.10", "@tailwindcss/oxide": "4.1.10", "postcss": "^8.4.41", "tailwindcss": "4.1.10" } }, "sha512-B+7r7ABZbkXJwpvt2VMnS6ujcDoR2OOcFaqrLIo1xbcdxje4Vf+VgJdBzNNbrAjBj/rLZ66/tlQ1knIGNLKOBQ=="],
"@types/bcryptjs": ["@types/bcryptjs@3.0.0", "", { "dependencies": { "bcryptjs": "*" } }, "sha512-WRZOuCuaz8UcZZE4R5HXTco2goQSI2XxjGY3hbM/xDvwmqFWd4ivooImsMx65OKM6CtNKbnZ5YL+YwAwK7c1dg=="],
"@types/d3-array": ["@types/d3-array@3.2.1", "", {}, "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="],
"@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
"@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="],
"@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="],
"@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="],
"@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
"@types/d3-shape": ["@types/d3-shape@3.1.7", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg=="],
"@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
"@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
"@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.10", "", { "dependencies": { "@types/ms": "*", "@types/node": "*" } }, "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA=="],
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
"@types/node": ["@types/node@20.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA=="], "@types/node": ["@types/node@20.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA=="],
"@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
"@types/react-dom": ["@types/react-dom@19.1.6", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw=="], "@types/react-dom": ["@types/react-dom@19.1.6", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw=="],
"bcryptjs": ["bcryptjs@3.0.2", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog=="],
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
"busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="], "busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="],
"caniuse-lite": ["caniuse-lite@1.0.30001724", "", {}, "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA=="], "caniuse-lite": ["caniuse-lite@1.0.30001724", "", {}, "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA=="],
"chart.js": ["chart.js@4.5.0", "", { "dependencies": { "@kurkle/color": "^0.3.0" } }, "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ=="],
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
"client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
@@ -153,18 +216,64 @@
"color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="], "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
"d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
"d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
"d3-format": ["d3-format@3.1.0", "", {}, "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="],
"d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
"d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
"d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
"d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
"d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
"d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
"d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
"decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="],
"detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
"dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
"enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="], "enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="],
"eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
"fast-equals": ["fast-equals@5.2.2", "", {}, "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
"is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
"jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
"jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="],
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="],
"jwa": ["jwa@1.4.2", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw=="],
"jws": ["jws@3.2.2", "", { "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA=="],
"lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
@@ -187,6 +296,28 @@
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="], "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
"lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="],
"lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="],
"lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="],
"lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="],
"lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
"lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="],
"lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="],
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
"lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"lucide-react": ["lucide-react@0.522.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-jnJbw974yZ7rQHHEFKJOlWAefG3ATSCZHANZxIdx8Rk/16siuwjgA4fBULpXEAWx/RlTs3FzmKW/udWUuO0aRw=="],
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
@@ -195,18 +326,56 @@
"mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="], "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"next": ["next@15.3.4", "", { "dependencies": { "@next/env": "15.3.4", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.4", "@next/swc-darwin-x64": "15.3.4", "@next/swc-linux-arm64-gnu": "15.3.4", "@next/swc-linux-arm64-musl": "15.3.4", "@next/swc-linux-x64-gnu": "15.3.4", "@next/swc-linux-x64-musl": "15.3.4", "@next/swc-win32-arm64-msvc": "15.3.4", "@next/swc-win32-x64-msvc": "15.3.4", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-mHKd50C+mCjam/gcnwqL1T1vPx/XQNFlXqFIVdgQdVAFY9iIQtY0IfaVflEYzKiqjeA7B0cYYMaCrmAYFjs4rA=="], "next": ["next@15.3.4", "", { "dependencies": { "@next/env": "15.3.4", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.4", "@next/swc-darwin-x64": "15.3.4", "@next/swc-linux-arm64-gnu": "15.3.4", "@next/swc-linux-arm64-musl": "15.3.4", "@next/swc-linux-x64-gnu": "15.3.4", "@next/swc-linux-x64-musl": "15.3.4", "@next/swc-win32-arm64-msvc": "15.3.4", "@next/swc-win32-x64-msvc": "15.3.4", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-mHKd50C+mCjam/gcnwqL1T1vPx/XQNFlXqFIVdgQdVAFY9iIQtY0IfaVflEYzKiqjeA7B0cYYMaCrmAYFjs4rA=="],
"next-auth": ["next-auth@4.24.11", "", { "dependencies": { "@babel/runtime": "^7.20.13", "@panva/hkdf": "^1.0.2", "cookie": "^0.7.0", "jose": "^4.15.5", "oauth": "^0.9.15", "openid-client": "^5.4.0", "preact": "^10.6.3", "preact-render-to-string": "^5.1.19", "uuid": "^8.3.2" }, "peerDependencies": { "@auth/core": "0.34.2", "next": "^12.2.5 || ^13 || ^14 || ^15", "nodemailer": "^6.6.5", "react": "^17.0.2 || ^18 || ^19", "react-dom": "^17.0.2 || ^18 || ^19" }, "optionalPeers": ["@auth/core", "nodemailer"] }, "sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw=="],
"oauth": ["oauth@0.9.15", "", {}, "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="],
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
"object-hash": ["object-hash@2.2.0", "", {}, "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="],
"oidc-token-hash": ["oidc-token-hash@5.1.0", "", {}, "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA=="],
"openid-client": ["openid-client@5.7.1", "", { "dependencies": { "jose": "^4.15.9", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" } }, "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
"preact": ["preact@10.26.9", "", {}, "sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA=="],
"preact-render-to-string": ["preact-render-to-string@5.2.6", "", { "dependencies": { "pretty-format": "^3.8.0" }, "peerDependencies": { "preact": ">=10" } }, "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw=="],
"pretty-format": ["pretty-format@3.8.0", "", {}, "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="],
"prisma": ["prisma@6.10.1", "", { "dependencies": { "@prisma/config": "6.10.1", "@prisma/engines": "6.10.1" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-khhlC/G49E4+uyA3T3H5PRBut486HD2bDqE2+rvkU0pwk9IAqGFacLFUyIx9Uw+W2eCtf6XGwsp+/strUwMNPw=="],
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
"react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="], "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
"react-chartjs-2": ["react-chartjs-2@5.3.0", "", { "peerDependencies": { "chart.js": "^4.1.1", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw=="],
"react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="], "react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
"react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
"react-smooth": ["react-smooth@4.0.4", "", { "dependencies": { "fast-equals": "^5.0.1", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q=="],
"react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
"recharts": ["recharts@2.15.4", "", { "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", "react-is": "^18.3.1", "react-smooth": "^4.0.4", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw=="],
"recharts-scale": ["recharts-scale@0.4.5", "", { "dependencies": { "decimal.js-light": "^2.4.1" } }, "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w=="],
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
@@ -227,12 +396,18 @@
"tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="], "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="],
"tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
"victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="],
"yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
@@ -247,6 +422,10 @@
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
"prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
} }
} }
+103
View File
@@ -0,0 +1,103 @@
'use client'
import { useState } from 'react'
import { ShoppingCart, Plus, Minus } from 'lucide-react'
interface AddToCartButtonProps {
componentId: string
disabled?: boolean
className?: string
}
export function AddToCartButton({ componentId, disabled = false, className = '' }: AddToCartButtonProps) {
const [quantity, setQuantity] = useState(1)
const [isLoading, setIsLoading] = useState(false)
const handleAddToCart = async () => {
const token = localStorage.getItem('token')
if (!token) {
alert('请先登录')
return
}
setIsLoading(true)
try {
const response = await fetch('/api/cart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
componentId,
quantity
})
})
if (response.ok) {
// 触发购物车更新事件
window.dispatchEvent(new Event('cart-updated'))
alert('已添加到购物车!')
} else if (response.status === 401) {
localStorage.removeItem('token')
localStorage.removeItem('user')
alert('登录已过期,请重新登录')
window.location.href = '/login'
} else {
const data = await response.json()
alert(data.message || '添加失败')
}
} catch (error) {
console.error('添加到购物车失败:', error)
alert('添加失败,请重试')
} finally {
setIsLoading(false)
}
}
return (
<div className={`space-y-4 ${className}`}>
{/* Quantity Selector */}
<div className="flex items-center space-x-4">
<span className="text-sm text-gray-600">数量:</span>
<div className="flex items-center border border-gray-300 rounded-lg">
<button
type="button"
onClick={() => setQuantity(Math.max(1, quantity - 1))}
disabled={quantity <= 1 || disabled}
className="p-2 hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed"
>
<Minus className="h-4 w-4" />
</button>
<span className="px-4 py-2 text-center min-w-[50px]">{quantity}</span>
<button
type="button"
onClick={() => setQuantity(quantity + 1)}
disabled={disabled}
className="p-2 hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed"
>
<Plus className="h-4 w-4" />
</button>
</div>
</div>
{/* Add to Cart Button */}
<button
onClick={handleAddToCart}
disabled={disabled || isLoading}
className={`
flex items-center justify-center space-x-2 w-full py-3 px-6 rounded-lg font-medium transition-colors
${disabled
? 'bg-gray-300 text-gray-500 cursor-not-allowed'
: 'bg-blue-600 text-white hover:bg-blue-700'
}
${isLoading ? 'opacity-50 cursor-not-allowed' : ''}
`}
>
<ShoppingCart className="h-5 w-5" />
<span>
{isLoading ? '添加中...' : disabled ? '缺货' : '加入购物车'}
</span>
</button>
</div>
)
}
+150
View File
@@ -0,0 +1,150 @@
'use client'
import Link from 'next/link'
import Image from 'next/image'
import { useState } from 'react'
import { ShoppingCart, Check } from 'lucide-react'
interface Component {
id: string
name: string
brand: string
model: string
price: number
description?: string | null
imageUrl?: string | null
stock: number
componentType?: {
id: string
name: string
}
}
interface ComponentCardProps {
component: Component
}
export function ComponentCard({ component }: ComponentCardProps) {
const [isAdding, setIsAdding] = useState(false)
const [isAdded, setIsAdded] = useState(false)
const addToCart = async (e: React.MouseEvent) => {
e.preventDefault()
e.stopPropagation()
if (component.stock <= 0 || isAdding) return
const token = localStorage.getItem('token')
if (!token) {
alert('请先登录')
return
}
setIsAdding(true)
try {
const response = await fetch('/api/cart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
componentId: component.id,
quantity: 1
})
})
if (response.ok) {
setIsAdded(true)
setTimeout(() => setIsAdded(false), 2000)
} else if (response.status === 401) {
localStorage.removeItem('token')
localStorage.removeItem('user')
alert('登录已过期,请重新登录')
window.location.href = '/login'
} else {
const data = await response.json()
alert(data.message || '添加失败')
}
} catch (error) {
console.error('添加到购物车失败:', error)
alert('添加失败')
} finally {
setIsAdding(false)
}
}
return (
<div className="bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow duration-300 overflow-hidden">
<Link href={`/components/${component.id}`}>
<div className="relative h-48 bg-gray-100">
{component.imageUrl ? (
<Image
src={component.imageUrl}
alt={component.name}
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
className="object-cover"
/>
) : (
<div className="flex items-center justify-center h-full">
<span className="text-gray-400 text-4xl">📦</span>
</div>
)}
</div>
</Link>
<div className="p-4">
<div className="text-sm text-gray-500 mb-1">{component.brand}</div>
<Link href={`/components/${component.id}`}>
<h3 className="font-semibold text-lg mb-2 line-clamp-2 hover:text-blue-600 transition-colors">
{component.name}
</h3>
</Link>
{component.description && (
<p className="text-gray-600 text-sm mb-3 line-clamp-2">
{component.description}
</p>
)}
<div className="flex justify-between items-center mb-3">
<span className="text-2xl font-bold text-blue-600">
¥{component.price.toLocaleString()}
</span>
<span className={`text-sm px-2 py-1 rounded ${component.stock > 0
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}`}>
{component.stock > 0 ? `库存 ${component.stock}` : '缺货'}
</span>
</div>
<button
onClick={addToCart}
disabled={component.stock <= 0 || isAdding}
className={`w-full py-2 px-4 rounded-lg font-medium transition-colors flex items-center justify-center ${component.stock <= 0
? 'bg-gray-100 text-gray-400 cursor-not-allowed'
: isAdded
? 'bg-green-600 text-white'
: 'bg-blue-600 text-white hover:bg-blue-700'
}`}
>
{isAdding ? (
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
) : isAdded ? (
<>
<Check className="h-4 w-4 mr-2" />
已添加
</>
) : (
<>
<ShoppingCart className="h-4 w-4 mr-2" />
{component.stock <= 0 ? '缺货' : '加入购物车'}
</>
)}
</button>
</div>
</div>
)
}
+251
View File
@@ -0,0 +1,251 @@
'use client'
import Link from 'next/link'
import { useState, useEffect } from 'react'
import { usePathname, useRouter } from 'next/navigation'
import { User, ShoppingCart, Menu, X } from 'lucide-react'
interface User {
id: string
email: string
username: string
name?: string
isAdmin: boolean
}
export function Navbar() {
const [user, setUser] = useState<User | null>(null)
const [isMenuOpen, setIsMenuOpen] = useState(false)
const pathname = usePathname()
const router = useRouter()
useEffect(() => {
// 检查用户登录状态
const checkUserStatus = () => {
const token = localStorage.getItem('token')
if (token) {
const userData = localStorage.getItem('user')
if (userData) {
setUser(JSON.parse(userData))
}
} else {
setUser(null)
}
}
checkUserStatus()
// 监听localStorage变化
const handleStorageChange = (e: StorageEvent) => {
if (e.key === 'user' || e.key === 'token') {
checkUserStatus()
}
}
// 监听自定义用户更新事件
const handleUserUpdate = () => {
checkUserStatus()
}
window.addEventListener('storage', handleStorageChange)
window.addEventListener('user-updated', handleUserUpdate)
return () => {
window.removeEventListener('storage', handleStorageChange)
window.removeEventListener('user-updated', handleUserUpdate)
}
}, [])
const handleLogout = () => {
localStorage.removeItem('token')
localStorage.removeItem('user')
setUser(null)
// 触发用户更新事件
window.dispatchEvent(new Event('user-updated'))
router.push('/')
}
// 检查当前路径是否匹配
const isActive = (path: string) => {
if (path === '/') {
return pathname === '/'
}
return pathname.startsWith(path)
}
// 导航链接样式
const getLinkStyle = (path: string) => {
return isActive(path)
? 'text-blue-600 font-medium'
: 'text-gray-700 hover:text-blue-600'
}
return (
<nav className="bg-white shadow-md sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
{/* Logo */}
<Link href="/" className="text-2xl font-bold text-blue-600">
PC DIY商城
</Link> {/* Desktop Navigation */}
<div className="hidden md:flex space-x-8">
<Link href="/" className={getLinkStyle('/')}>
首页
</Link>
<Link href="/components" className={getLinkStyle('/components')}>
配件商城
</Link>
<Link href="/build" className={getLinkStyle('/build')}>
装机配置
</Link>
</div>
{/* User Actions */}
<div className="hidden md:flex items-center space-x-4">
{user ? (
<div className="flex items-center space-x-4">
<Link href="/cart" className="text-gray-700 hover:text-blue-600">
<ShoppingCart className="h-6 w-6" />
</Link>
<div className="relative group">
<button className="flex items-center space-x-2 text-gray-700 hover:text-blue-600">
<User className="h-6 w-6" />
<span>{user.name || user.username}</span>
</button>
<div className="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200">
<Link href="/profile" className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
个人资料
</Link>
<Link href="/orders" className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
我的订单
</Link>
{user.isAdmin && (
<Link href="/admin" className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
管理后台
</Link>
)}
<button
onClick={handleLogout}
className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
>
退出登录
</button>
</div>
</div>
</div>
) : (
<div className="flex items-center space-x-4">
<Link
href="/login"
className="text-gray-700 hover:text-blue-600"
>
登录
</Link>
<Link
href="/register"
className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700"
>
注册
</Link>
</div>
)}
</div>
{/* Mobile menu button */}
<div className="md:hidden">
<button
onClick={() => setIsMenuOpen(!isMenuOpen)}
className="text-gray-700 hover:text-blue-600"
>
{isMenuOpen ? <X className="h-6 w-6" /> : <Menu className="h-6 w-6" />}
</button>
</div>
</div> {/* Mobile Navigation */}
{isMenuOpen && (
<div className="md:hidden">
<div className="px-2 pt-2 pb-3 space-y-1 sm:px-3">
<Link
href="/"
className={`block px-3 py-2 ${getLinkStyle('/')}`}
onClick={() => setIsMenuOpen(false)}
>
首页
</Link>
<Link
href="/components"
className={`block px-3 py-2 ${getLinkStyle('/components')}`}
onClick={() => setIsMenuOpen(false)}
>
配件商城
</Link>
<Link
href="/build"
className={`block px-3 py-2 ${getLinkStyle('/build')}`}
onClick={() => setIsMenuOpen(false)}
>
装机配置
</Link>
{user ? (
<>
<Link
href="/cart"
className="block px-3 py-2 text-gray-700 hover:text-blue-600"
onClick={() => setIsMenuOpen(false)}
>
购物车
</Link>
<Link
href="/profile"
className="block px-3 py-2 text-gray-700 hover:text-blue-600"
onClick={() => setIsMenuOpen(false)}
>
个人资料
</Link>
<Link
href="/orders"
className="block px-3 py-2 text-gray-700 hover:text-blue-600"
onClick={() => setIsMenuOpen(false)}
>
我的订单
</Link>
{user.isAdmin && (
<Link
href="/admin"
className="block px-3 py-2 text-gray-700 hover:text-blue-600"
onClick={() => setIsMenuOpen(false)}
>
管理后台
</Link>
)}
<button
onClick={handleLogout}
className="block w-full text-left px-3 py-2 text-gray-700 hover:text-blue-600"
>
退出登录
</button>
</>
) : (
<>
<Link
href="/login"
className="block px-3 py-2 text-gray-700 hover:text-blue-600"
onClick={() => setIsMenuOpen(false)}
>
登录
</Link>
<Link
href="/register"
className="block px-3 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
onClick={() => setIsMenuOpen(false)}
>
注册
</Link>
</>
)}
</div>
</div>
)}
</div>
</nav>
)
}
+55
View File
@@ -0,0 +1,55 @@
'use client'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
export function withAdminAuth<T extends object>(WrappedComponent: React.ComponentType<T>) {
return function AdminProtectedComponent(props: T) {
const [isLoading, setIsLoading] = useState(true)
const [isAuthorized, setIsAuthorized] = useState(false)
const router = useRouter()
useEffect(() => {
const checkAuth = () => {
const token = localStorage.getItem('token')
const user = localStorage.getItem('user')
if (!token || !user) {
router.push('/login')
return
}
try {
const userData = JSON.parse(user)
if (!userData.isAdmin) {
router.push('/')
return
}
setIsAuthorized(true)
} catch (error) {
router.push('/login')
return
} finally {
setIsLoading(false)
}
}
checkAuth()
}, [router])
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
)
}
if (!isAuthorized) {
return null
}
return <WrappedComponent {...props} />
}
}
+28
View File
@@ -0,0 +1,28 @@
import { NextRequest } from 'next/server'
import { verifyToken } from '@/lib/auth'
import { prisma } from '@/lib/prisma'
export async function requireAdmin(request: NextRequest) {
const authHeader = request.headers.get('Authorization')
const token = authHeader?.replace('Bearer ', '')
if (!token) {
throw new Error('未提供认证令牌')
}
const decoded = verifyToken(token)
if (!decoded) {
throw new Error('无效的认证令牌')
}
const user = await prisma.user.findUnique({
where: { id: decoded.userId },
select: { id: true, isAdmin: true }
})
if (!user || !user.isAdmin) {
throw new Error('权限不足')
}
return user
}
+24
View File
@@ -0,0 +1,24 @@
import bcrypt from 'bcryptjs'
import jwt from 'jsonwebtoken'
const JWT_SECRET = process.env.JWT_SECRET || 'your-jwt-secret-here'
export async function hashPassword(password: string): Promise<string> {
return await bcrypt.hash(password, 12)
}
export async function verifyPassword(password: string, hashedPassword: string): Promise<boolean> {
return await bcrypt.compare(password, hashedPassword)
}
export function generateToken(payload: any): string {
return jwt.sign(payload, JWT_SECRET, { expiresIn: '7d' })
}
export function verifyToken(token: string): any {
try {
return jwt.verify(token, JWT_SECRET)
} catch (error) {
return null
}
}
+9
View File
@@ -0,0 +1,9 @@
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
+3 -1
View File
@@ -1,7 +1,9 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
/* config options here */ images: {
remotePatterns: [new URL('https://**360buyimg.com/**')],
},
}; };
export default nextConfig; export default nextConfig;
+16 -2
View File
@@ -6,12 +6,26 @@
"dev": "next dev --turbopack", "dev": "next dev --turbopack",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint" "lint": "next lint",
"db:generate": "prisma generate",
"db:migrate": "prisma migrate dev",
"db:seed": "bun run seed.ts"
}, },
"dependencies": { "dependencies": {
"@prisma/client": "^6.10.1",
"@types/bcryptjs": "^3.0.0",
"@types/jsonwebtoken": "^9.0.10",
"bcryptjs": "^3.0.2",
"chart.js": "^4.5.0",
"jsonwebtoken": "^9.0.2",
"lucide-react": "^0.522.0",
"next": "15.3.4",
"next-auth": "^4.24.11",
"prisma": "^6.10.1",
"react": "^19.0.0", "react": "^19.0.0",
"react-chartjs-2": "^5.3.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"next": "15.3.4" "recharts": "^2.15.4"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^5", "typescript": "^5",
@@ -0,0 +1,93 @@
-- CreateEnum
CREATE TYPE "OrderStatus" AS ENUM ('PENDING', 'CONFIRMED', 'PROCESSING', 'SHIPPED', 'DELIVERED', 'CANCELLED');
-- CreateTable
CREATE TABLE "users" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"username" TEXT NOT NULL,
"password" TEXT NOT NULL,
"name" TEXT,
"phone" TEXT,
"address" TEXT,
"isAdmin" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "component_types" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
CONSTRAINT "component_types_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "components" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"brand" TEXT NOT NULL,
"model" TEXT NOT NULL,
"price" DOUBLE PRECISION NOT NULL,
"description" TEXT,
"imageUrl" TEXT,
"stock" INTEGER NOT NULL DEFAULT 0,
"specifications" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"componentTypeId" TEXT NOT NULL,
CONSTRAINT "components_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "orders" (
"id" TEXT NOT NULL,
"orderNumber" TEXT NOT NULL,
"totalAmount" DOUBLE PRECISION NOT NULL,
"status" "OrderStatus" NOT NULL DEFAULT 'PENDING',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"userId" TEXT NOT NULL,
CONSTRAINT "orders_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "order_items" (
"id" TEXT NOT NULL,
"quantity" INTEGER NOT NULL DEFAULT 1,
"price" DOUBLE PRECISION NOT NULL,
"orderId" TEXT NOT NULL,
"componentId" TEXT NOT NULL,
CONSTRAINT "order_items_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
-- CreateIndex
CREATE UNIQUE INDEX "users_username_key" ON "users"("username");
-- CreateIndex
CREATE UNIQUE INDEX "component_types_name_key" ON "component_types"("name");
-- CreateIndex
CREATE UNIQUE INDEX "orders_orderNumber_key" ON "orders"("orderNumber");
-- AddForeignKey
ALTER TABLE "components" ADD CONSTRAINT "components_componentTypeId_fkey" FOREIGN KEY ("componentTypeId") REFERENCES "component_types"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "orders" ADD CONSTRAINT "orders_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "order_items" ADD CONSTRAINT "order_items_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "orders"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "order_items" ADD CONSTRAINT "order_items_componentId_fkey" FOREIGN KEY ("componentId") REFERENCES "components"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@@ -0,0 +1,20 @@
-- CreateTable
CREATE TABLE "cart_items" (
"id" TEXT NOT NULL,
"quantity" INTEGER NOT NULL DEFAULT 1,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"userId" TEXT NOT NULL,
"componentId" TEXT NOT NULL,
CONSTRAINT "cart_items_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "cart_items_userId_componentId_key" ON "cart_items"("userId", "componentId");
-- AddForeignKey
ALTER TABLE "cart_items" ADD CONSTRAINT "cart_items_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "cart_items" ADD CONSTRAINT "cart_items_componentId_fkey" FOREIGN KEY ("componentId") REFERENCES "components"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+3
View File
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"
+116
View File
@@ -0,0 +1,116 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
username String @unique
password String
name String?
phone String?
address String?
isAdmin Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
orders Order[]
cartItems CartItem[]
@@map("users")
}
model ComponentType {
id String @id @default(cuid())
name String @unique // CPU、内存、硬盘、主板、显卡、机箱
description String?
components Component[]
@@map("component_types")
}
model Component {
id String @id @default(cuid())
name String
brand String
model String
price Float
description String?
imageUrl String?
stock Int @default(0)
specifications String? // JSON格式存储规格参数
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
componentTypeId String
componentType ComponentType @relation(fields: [componentTypeId], references: [id])
orderItems OrderItem[]
cartItems CartItem[]
@@map("components")
}
model Order {
id String @id @default(cuid())
orderNumber String @unique
totalAmount Float
status OrderStatus @default(PENDING)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId String
user User @relation(fields: [userId], references: [id])
orderItems OrderItem[]
@@map("orders")
}
model OrderItem {
id String @id @default(cuid())
quantity Int @default(1)
price Float
orderId String
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
componentId String
component Component @relation(fields: [componentId], references: [id])
@@map("order_items")
}
model CartItem {
id String @id @default(cuid())
quantity Int @default(1)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
componentId String
component Component @relation(fields: [componentId], references: [id], onDelete: Cascade)
@@unique([userId, componentId]) // 一个用户对同一个商品只能有一条购物车记录
@@map("cart_items")
}
enum OrderStatus {
PENDING
CONFIRMED
PROCESSING
SHIPPED
DELIVERED
CANCELLED
}
+261
View File
@@ -0,0 +1,261 @@
import { PrismaClient } from '@prisma/client'
import { hashPassword } from './lib/auth'
const prisma = new PrismaClient()
async function main() {
// 创建管理员用户
const adminPassword = await hashPassword('admin123')
const admin = await prisma.user.upsert({
where: { email: 'admin@pcdiy.com' },
update: {},
create: {
email: 'admin@pcdiy.com',
username: 'admin',
password: adminPassword,
name: '系统管理员',
isAdmin: true,
},
})
// 创建配件类型
const componentTypes = [
{ name: 'CPU', description: '中央处理器' },
{ name: '内存', description: '内存条' },
{ name: '硬盘', description: '存储设备' },
{ name: '主板', description: '主板' },
{ name: '显卡', description: '显卡' },
{ name: '机箱', description: '机箱' },
]
for (const type of componentTypes) {
await prisma.componentType.upsert({
where: { name: type.name },
update: {},
create: type,
})
}
// 获取已创建的配件类型
const cpuType = await prisma.componentType.findUnique({ where: { name: 'CPU' } })
const memoryType = await prisma.componentType.findUnique({ where: { name: '内存' } })
const storageType = await prisma.componentType.findUnique({ where: { name: '硬盘' } })
const motherboardType = await prisma.componentType.findUnique({ where: { name: '主板' } })
const gpuType = await prisma.componentType.findUnique({ where: { name: '显卡' } })
const caseType = await prisma.componentType.findUnique({ where: { name: '机箱' } })
// 创建示例配件
const components = [
// CPU
{
name: 'Intel Core i5-13400F',
brand: 'Intel',
model: 'i5-13400F',
price: 1299,
description: '10核16线程,基础频率2.5GHz,最大睿频4.6GHz',
stock: 50,
componentTypeId: cpuType!.id,
specifications: JSON.stringify({
cores: 10,
threads: 16,
baseClock: '2.5GHz',
boostClock: '4.6GHz',
socket: 'LGA1700'
})
},
{
name: 'AMD Ryzen 5 7600X',
brand: 'AMD',
model: '7600X',
price: 1599,
description: '6核12线程,基础频率4.7GHz,最大睿频5.3GHz',
stock: 30,
componentTypeId: cpuType!.id,
specifications: JSON.stringify({
cores: 6,
threads: 12,
baseClock: '4.7GHz',
boostClock: '5.3GHz',
socket: 'AM5'
})
},
// 内存
{
name: 'Kingston FURY Beast DDR4 3200MHz 16GB',
brand: 'Kingston',
model: 'FURY Beast',
price: 299,
description: 'DDR4 3200MHz 16GB套装(8GBx2)',
stock: 100,
componentTypeId: memoryType!.id,
specifications: JSON.stringify({
type: 'DDR4',
speed: '3200MHz',
capacity: '16GB',
modules: 2
})
},
{
name: 'Corsair Vengeance LPX DDR4 3600MHz 32GB',
brand: 'Corsair',
model: 'Vengeance LPX',
price: 699,
description: 'DDR4 3600MHz 32GB套装(16GBx2)',
stock: 60,
componentTypeId: memoryType!.id,
specifications: JSON.stringify({
type: 'DDR4',
speed: '3600MHz',
capacity: '32GB',
modules: 2
})
},
// 硬盘
{
name: 'Samsung 980 PRO 1TB NVMe SSD',
brand: 'Samsung',
model: '980 PRO',
price: 599,
description: 'PCIe 4.0 NVMe M.2 SSD,读取速度7000MB/s',
stock: 80,
componentTypeId: storageType!.id,
specifications: JSON.stringify({
type: 'NVMe SSD',
capacity: '1TB',
interface: 'PCIe 4.0',
readSpeed: '7000MB/s'
})
},
{
name: 'Western Digital Blue 2TB SATA HDD',
brand: 'Western Digital',
model: 'Blue',
price: 399,
description: '2TB 7200RPM SATA 机械硬盘',
stock: 120,
componentTypeId: storageType!.id,
specifications: JSON.stringify({
type: 'HDD',
capacity: '2TB',
interface: 'SATA',
speed: '7200RPM'
})
},
// 主板
{
name: 'ASUS PRIME B660M-A',
brand: 'ASUS',
model: 'PRIME B660M-A',
price: 699,
description: 'Intel B660芯片组,支持LGA1700接口',
stock: 40,
componentTypeId: motherboardType!.id,
specifications: JSON.stringify({
chipset: 'B660',
socket: 'LGA1700',
formFactor: 'mATX',
memorySlots: 4
})
},
{
name: 'MSI MAG B550M MORTAR',
brand: 'MSI',
model: 'MAG B550M MORTAR',
price: 799,
description: 'AMD B550芯片组,支持AM4接口',
stock: 35,
componentTypeId: motherboardType!.id,
specifications: JSON.stringify({
chipset: 'B550',
socket: 'AM4',
formFactor: 'mATX',
memorySlots: 4
})
},
// 显卡
{
name: 'NVIDIA GeForce RTX 4060 Ti',
brand: 'NVIDIA',
model: 'RTX 4060 Ti',
price: 3299,
description: '16GB GDDR6显存,支持光追和DLSS 3',
stock: 25,
componentTypeId: gpuType!.id,
specifications: JSON.stringify({
gpu: 'RTX 4060 Ti',
memory: '16GB GDDR6',
memoryBus: '128-bit',
baseClock: '2310MHz'
})
},
{
name: 'AMD Radeon RX 7600 XT',
brand: 'AMD',
model: 'RX 7600 XT',
price: 2799,
description: '16GB GDDR6显存,RDNA 3架构',
stock: 20,
componentTypeId: gpuType!.id,
specifications: JSON.stringify({
gpu: 'RX 7600 XT',
memory: '16GB GDDR6',
memoryBus: '128-bit',
baseClock: '2300MHz'
})
},
// 机箱
{
name: 'Fractal Design Core 1000',
brand: 'Fractal Design',
model: 'Core 1000',
price: 299,
description: '紧凑型mATX机箱,静音设计',
stock: 70,
componentTypeId: caseType!.id,
specifications: JSON.stringify({
formFactor: 'mATX',
dimensions: '390 x 175 x 350mm',
material: '钢制',
fans: '1x120mm'
})
},
{
name: 'NZXT H510',
brand: 'NZXT',
model: 'H510',
price: 599,
description: '中塔ATX机箱,钢化玻璃侧板',
stock: 50,
componentTypeId: caseType!.id,
specifications: JSON.stringify({
formFactor: 'ATX',
dimensions: '435 x 210 x 460mm',
material: '钢制+钢化玻璃',
fans: '2x120mm'
})
},
]
for (const component of components) {
await prisma.component.upsert({
where: {
id: 'temp-id-' + component.name.replace(/\s+/g, '-').toLowerCase()
},
update: {},
create: component,
})
}
console.log('数据初始化完成!')
console.log('管理员账号:admin@pcdiy.com')
console.log('管理员密码:admin123')
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})