diff --git a/app/(main)/search/page.tsx b/app/(main)/search/page.tsx index 4de532d..aac9486 100644 --- a/app/(main)/search/page.tsx +++ b/app/(main)/search/page.tsx @@ -42,8 +42,8 @@ import { } from "@/components/ui/sheet" import { Switch } from "@/components/ui/switch" import { GameIcon } from "@/lib/game-icons" -import { mockGames, mockPlayers } from "@/lib/mock" -import type { Player } from "@/lib/types" +import { mockGames, mockPlayers, mockServices, mockShops } from "@/lib/mock" +import type { Player, Shop } from "@/lib/types" import { cn } from "@/lib/utils" function StatusBadge({ status }: { status: Player["status"] }) { @@ -165,6 +165,95 @@ function PlayerCard({ player }: { player: Player }) { ) } +interface ShopResultItem { + shop: Shop + minPrice: number + unit: string + games: string[] + hasAvailable: boolean +} + +function ShopCard({ item }: { item: ShopResultItem }) { + return ( + + + +
+ + + {item.shop.name.slice(0, 2)} + +
+

{item.shop.name}

+
+ + + 店铺 + +
+
+
+ +
+ + +
+
+ + {item.shop.rating.toFixed(1)} +
+
接单 {item.shop.totalOrders}
+
+ +
+ {item.games.slice(0, 3).map((game) => ( + + {game} + + ))} + {item.games.length > 3 && ( + + +{item.games.length - 3} + + )} +
+
+ + + + +
+ + ¥{item.minPrice} + /{item.unit} +
+ +
+
+ + ) +} + +type SearchResult = + | { + type: "player" + id: string + rating: number + orders: number + minPrice: number + item: Player + } + | { + type: "shop" + id: string + rating: number + orders: number + minPrice: number + item: ShopResultItem + } + interface FilterProps { selectedGames: string[] onGameChange: (game: string, checked: boolean) => void @@ -316,6 +405,29 @@ function SearchPageContent() { setPriceRange((prev) => ({ ...prev, [type]: value })) } + const shopResultItems = useMemo(() => { + return mockShops.map((shop) => { + const shopPlayers = mockPlayers.filter((player) => player.shopId === shop.id) + const playerIds = new Set(shopPlayers.map((player) => player.id)) + const services = mockServices.filter((service) => playerIds.has(service.playerId)) + const minPrice = + services.length > 0 ? Math.min(...services.map((service) => service.price)) : 0 + const unit = + services.length > 0 + ? services.reduce((prev, curr) => (prev.price < curr.price ? prev : curr)).unit + : "局" + const games = [...new Set(services.map((service) => service.gameName))] + const hasAvailable = shopPlayers.some((player) => player.status === "available") + return { + shop, + minPrice, + unit, + games, + hasAvailable, + } + }) + }, []) + const filteredPlayers = useMemo(() => { return mockPlayers.filter((player) => { if (searchQuery) { @@ -346,35 +458,73 @@ function SearchPageContent() { }) }, [searchQuery, selectedGames, priceRange, onlyOnline, minRating]) - const sortedPlayers = useMemo(() => { - return [...filteredPlayers].sort((a, b) => { + const filteredShops = useMemo(() => { + return shopResultItems.filter((item) => { + if (searchQuery) { + const query = searchQuery.toLowerCase() + const matchName = item.shop.name.toLowerCase().includes(query) + const matchDescription = item.shop.description.toLowerCase().includes(query) + const matchGames = item.games.some((game) => game.toLowerCase().includes(query)) + if (!matchName && !matchDescription && !matchGames) return false + } + + if (selectedGames.length > 0) { + const hasGame = item.games.some((game) => selectedGames.includes(game)) + if (!hasGame) return false + } + + const minP = priceRange.min ? Number(priceRange.min) : 0 + const maxP = priceRange.max ? Number(priceRange.max) : Infinity + if (item.minPrice < minP) return false + if (priceRange.max && item.minPrice > maxP) return false + + if (onlyOnline && !item.hasAvailable) return false + if (item.shop.rating < Number(minRating)) return false + + return true + }) + }, [shopResultItems, searchQuery, selectedGames, priceRange, onlyOnline, minRating]) + + const sortedResults = useMemo(() => { + const playerResults: SearchResult[] = filteredPlayers.map((player) => ({ + type: "player", + id: player.id, + rating: player.rating, + orders: player.totalOrders, + minPrice: Math.min(...player.services.map((service) => service.price)), + item: player, + })) + const shopResults: SearchResult[] = filteredShops.map((item) => ({ + type: "shop", + id: item.shop.id, + rating: item.shop.rating, + orders: item.shop.totalOrders, + minPrice: item.minPrice, + item, + })) + + return [...playerResults, ...shopResults].sort((a, b) => { switch (sortBy) { case "rating": return b.rating - a.rating - case "price_asc": { - const priceA = Math.min(...a.services.map((s) => s.price)) - const priceB = Math.min(...b.services.map((s) => s.price)) - return priceA - priceB - } - case "price_desc": { - const priceA = Math.min(...a.services.map((s) => s.price)) - const priceB = Math.min(...b.services.map((s) => s.price)) - return priceB - priceA - } + case "price_asc": + return a.minPrice - b.minPrice + case "price_desc": + return b.minPrice - a.minPrice case "orders": - return b.totalOrders - a.totalOrders + return b.orders - a.orders case "composite": { - const scoreA = a.rating * Math.log10(a.totalOrders + 1) - const scoreB = b.rating * Math.log10(b.totalOrders + 1) + const scoreA = a.rating * Math.log10(a.orders + 1) + const scoreB = b.rating * Math.log10(b.orders + 1) return scoreB - scoreA } default: return 0 } }) - }, [filteredPlayers, sortBy]) + }, [filteredPlayers, filteredShops, sortBy]) - const displayedPlayers = sortedPlayers.slice(0, visibleCount) + const displayedResults = sortedResults.slice(0, visibleCount) return (
@@ -421,7 +571,7 @@ function SearchPageContent() {
- + @@ -509,14 +659,20 @@ function SearchPageContent() {
- 共找到 {filteredPlayers.length} 位陪玩 + 共找到 {sortedResults.length} 位陪玩
- {displayedPlayers.length > 0 ? ( + {displayedResults.length > 0 ? (
- {displayedPlayers.map((player) => ( - + {displayedResults.map((result) => ( +
+ {result.type === "player" ? ( + + ) : ( + + )} +
))}
) : ( @@ -544,7 +700,7 @@ function SearchPageContent() { )} - {filteredPlayers.length > visibleCount && ( + {sortedResults.length > visibleCount && (
))} diff --git a/app/(order)/orders/page.tsx b/app/(order)/orders/page.tsx index 58a916b..1bee172 100644 --- a/app/(order)/orders/page.tsx +++ b/app/(order)/orders/page.tsx @@ -146,7 +146,7 @@ export default function OrderListPage() { })()} {order.status === "completed" && (