diff --git a/src/pages/PointsMallPage/PointsMallPageLogic.ts b/src/pages/PointsMallPage/PointsMallPageLogic.ts index 0803f1a..65b5915 100644 --- a/src/pages/PointsMallPage/PointsMallPageLogic.ts +++ b/src/pages/PointsMallPage/PointsMallPageLogic.ts @@ -14,7 +14,7 @@ export function usePointsMallPageLogic() { const [q, setQ] = useState(''); const [sort, setSort] = useState('popular'); const [page, setPage] = useState(1); - const [pageSize, setPageSize] = useState(24); + const [pageSize, setPageSize] = useState(12); const [productsRes, setProductsRes] = useState(null); const [exchangeModalVisible, setExchangeModalVisible] = useState(false); diff --git a/src/pages/PointsMallPage/components/PointsMallH5Products.tsx b/src/pages/PointsMallPage/components/PointsMallH5Products.tsx index e89e7df..d8824df 100644 --- a/src/pages/PointsMallPage/components/PointsMallH5Products.tsx +++ b/src/pages/PointsMallPage/components/PointsMallH5Products.tsx @@ -1,5 +1,6 @@ -import { SearchOutlined } from '@ant-design/icons'; +import { ArrowRightOutlined, LeftOutlined, RightOutlined, SearchOutlined } from '@ant-design/icons'; import { Button, Card, Empty, Input, Select, Space, Spin, Tag } from 'antd'; +import { useEffect, useRef, useState } from 'react'; import type { PointsMallProduct } from '../../../api'; import type { PointsMallPageLogicOutput } from '../PointsMallPageLogic'; import '../styles/points-mall-h5-products.css'; @@ -12,8 +13,6 @@ export default function PointsMallH5Products({ logic }: Props) { const { q, sort, - page, - pageSize, productsLoading, products, total, @@ -21,83 +20,120 @@ export default function PointsMallH5Products({ logic }: Props) { exchangeLoading, setQ, setSort, + page, setPage, - setPageSize, handleExchangeClick } = logic; + const [displayProducts, setDisplayProducts] = useState([]); + const loadingMoreRef = useRef(false); + const scrollRef = useRef(null); + + // 监听搜索/排序变化,重置列表 + useEffect(() => { + setDisplayProducts(products); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [q, sort]); + + // 监听数据变化,追加到显示列表 + useEffect(() => { + if (page === 1) { + setDisplayProducts(products); + } else { + setDisplayProducts(prev => { + const existingIds = new Set(prev.map(p => p.id)); + const newItems = products.filter(p => !existingIds.has(p.id)); + return [...prev, ...newItems]; + }); + } + loadingMoreRef.current = false; + }, [products, page]); + + const hasMore = displayProducts.length < total; + + // 瀑布流滚动加载 + useEffect(() => { + const handleScroll = () => { + // 获取滚动容器,H5 端通常是 App.tsx 里的 .main-content + const container = document.querySelector('.main-content'); + if (!container || !hasMore || productsLoading || loadingMoreRef.current) return; + + const { scrollTop, scrollHeight, clientHeight } = container; + // 距离底部 100px 时触发加载 + if (scrollTop + clientHeight >= scrollHeight - 100) { + loadingMoreRef.current = true; + setPage(prev => prev + 1); + } + }; + + const container = document.querySelector('.main-content'); + container?.addEventListener('scroll', handleScroll); + return () => container?.removeEventListener('scroll', handleScroll); + }, [hasMore, productsLoading]); + return ( - +
{ - setPage(1); setQ(e.target.value); }} prefix={} placeholder="搜索商品" allowClear - className="points-mall-search-input" + variant="borderless" + className="points-mall-search-input-h5" />
-
+
{ - setPage(1); - setPageSize(v); - }} - options={[ - { value: 12, label: '每页 12' }, - { value: 24, label: '每页 24' } + { value: 'price_asc', label: 'Token升序' }, + { value: 'price_desc', label: 'Token降序' } ]} />
-
共 {total.toLocaleString()} 件商品
- {productsLoading ? ( + {displayProducts.length === 0 && productsLoading ? ( - ) : products.length === 0 ? ( + ) : displayProducts.length === 0 ? ( ) : (
- {products.map((p: PointsMallProduct) => ( + {displayProducts.map((p: PointsMallProduct) => (
{p.coverUrl ? {p.name} : null} - {p.tags?.length ? ( - - {p.tags[0]} - - ) : null}
-
{p.name}
+
+ {p.name} + {p.tags?.length ? ( + + {p.tags[0]} + + ) : null} +
+ {p.subtitle &&
{p.subtitle}
}
-
+
{p.pointsPrice >= 1000 ? `${(p.pointsPrice / 1000).toFixed(1)} K` : Number(p.pointsPrice).toLocaleString()} - + Token
)} - {!!total && ( -
- - - - 第 {page} 页 - - - -
- )} - +
+ {productsLoading ? ( +
正在加载中...
+ ) : !hasMore && displayProducts.length > 0 ? ( +
没有更多了
+ ) : null} +
+
); } diff --git a/src/pages/PointsMallPage/components/PointsMallH5Top.tsx b/src/pages/PointsMallPage/components/PointsMallH5Top.tsx index dfdd5c7..da257ef 100644 --- a/src/pages/PointsMallPage/components/PointsMallH5Top.tsx +++ b/src/pages/PointsMallPage/components/PointsMallH5Top.tsx @@ -52,27 +52,22 @@ export default function PointsMallH5Top({ logic }: Props) {
{categories.length > 0 && ( - -
-
- 商品分类 - {categories.map((c) => ( - - ))} -
-
-
+
+ {categories.map((c) => ( + + ))} +
)}
diff --git a/src/pages/PointsMallPage/styles/points-mall-h5-products.css b/src/pages/PointsMallPage/styles/points-mall-h5-products.css index c49605c..2e929b0 100644 --- a/src/pages/PointsMallPage/styles/points-mall-h5-products.css +++ b/src/pages/PointsMallPage/styles/points-mall-h5-products.css @@ -4,98 +4,201 @@ .points-mall-page-h5 .h5-points-mall-products-grid { display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); + grid-template-columns: repeat(2, 1fr); gap: 0.5rem; + padding: 0; } .points-mall-page-h5 .h5-product-tile { min-width: 0; padding: 0; - border-radius: 0.75rem; + border-radius: 0.5rem; overflow: hidden; + background: var(--color-surface); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02); + display: flex; + flex-direction: column; } .points-mall-page-h5 .h5-product-tile-cover { position: relative; width: 100%; - height: auto; - aspect-ratio: 1; - background: - radial-gradient(circle at 28% 24%, rgba(30, 134, 255, 0.22), transparent 34%), - linear-gradient(135deg, rgba(17, 103, 255, 0.14), rgba(104, 185, 255, 0.12)); + height: 100px; /* 设置最大高度 */ + background: #f7f8fa; } .h5-product-tile-image { width: 100%; height: 100%; - object-fit: cover; + object-fit: cover; /* 改为 cover 方式 */ display: block; } -.points-mall-page-h5 .h5-product-tile-tag { - position: absolute; - top: 0.25rem; - left: 0.25rem; - max-width: calc(100% - 0.5rem); - margin: 0; - padding: 0 0.3125rem; - font-size: 0.625rem; - line-height: 1.25rem; - border-radius: 999px; +.points-mall-page-h5 .h5-product-tile-tag-inline { + margin-left: 0.25rem !important; + font-size: 0.625rem !important; + padding: 0 0.25rem !important; + height: 1rem !important; + line-height: 1rem !important; + border-radius: 2px !important; + background: var(--color-brand-soft) !important; + color: var(--color-brand) !important; + display: inline-block !important; + vertical-align: middle !important; } -.points-mall-page-h5 .h5-product-tile .points-mall-product-body { - padding: 0.4375rem; - gap: 0.3125rem; -} - -.points-mall-page-h5 .h5-product-tile .points-mall-product-name { - min-height: 2.125rem; - margin: 0; - color: var(--color-text); - font-size: 0.75rem; - font-weight: 700; - line-height: 1.38; - white-space: normal; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; -} - -.points-mall-page-h5 .h5-product-tile .h5-points-mall-product-price-row { - display: grid; - grid-template-columns: minmax(0, 1fr) 1.875rem; +.points-mall-product-name-row { + display: flex; align-items: center; - gap: 0.25rem; -} - -.points-mall-page-h5 .h5-product-tile .h5-points-mall-product-price-row > div { - min-width: 0; + overflow: hidden; white-space: nowrap; } +.points-mall-page-h5 .h5-product-tile .points-mall-product-body { + padding: 0.5rem; + display: flex; + flex-direction: column; + flex: 1; + gap: 0.125rem; +} + +.points-mall-page-h5 .h5-product-tile .points-mall-product-name { + margin: 0; + color: var(--color-text); + font-size: 0.8125rem; + font-weight: 600; + line-height: 1.2; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 0 1 auto; /* 允许标题收缩 */ +} + +.points-mall-product-subtitle { + font-size: 0.6875rem; + color: var(--color-text-tertiary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 4px; +} + +.points-mall-page-h5 .h5-product-tile .h5-points-mall-product-price-row { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: auto; + gap: 4px; +} + +.points-mall-product-price-container { + display: flex; + align-items: baseline; + gap: 1px; + min-width: 0; + flex: 1; +} + .points-mall-page-h5 .h5-product-tile .points-mall-product-price { - font-size: 0.75rem; - font-weight: 900; + font-size: 0.9375rem; + font-weight: 700; + color: var(--color-brand); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .points-mall-page-h5 .h5-product-tile .points-mall-product-price-label { - margin-left: 0.125rem; + margin-left: 0; font-size: 0.625rem; + color: var(--color-text-tertiary); + flex-shrink: 0; } .points-mall-page-h5 .h5-product-tile .points-mall-product-exchange-btn { - width: 1.875rem; - min-width: 1.875rem; - height: 1.625rem; - padding: 0; - border-radius: 0.5rem; + height: 1.375rem; + min-width: 1.75rem; + padding: 0 0.4rem; + border-radius: 999px; font-size: 0.6875rem; - font-weight: 800; + font-weight: 500; + flex-shrink: 0; } -@media (max-width: 340px) { - .points-mall-page-h5 .h5-points-mall-products-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } +.h5-points-mall-filters-row { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 1rem; +} + +.points-mall-filter-search { + flex: 1; + min-width: 0; +} + +.points-mall-search-input-h5 { + background: var(--color-surface) !important; + box-shadow: var(--shadow-sm) !important; +} + +.points-mall-filter-selects-h5 { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.points-mall-filter-select-h5 .ant-select-selector { + color: var(--color-text-secondary) !important; + font-weight: 500 !important; +} + +.points-mall-filter-select-h5 .ant-select-selection-item { + font-size: 0.8125rem !important; +} + +.h5-points-mall-pagination { + margin-top: 1.5rem; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 0.5rem; +} + +.points-mall-pagination-tag { + background: transparent !important; + color: var(--color-text-tertiary) !important; + margin: 0 !important; +} + +.h5-points-mall-infinite-footer { + padding: 1.5rem 0 2rem; + text-align: center; +} + +.h5-infinite-loading { + color: var(--color-text-secondary); + font-size: 0.8125rem; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} + +.h5-infinite-no-more { + color: var(--color-text-tertiary); + font-size: 0.75rem; + position: relative; + display: flex; + align-items: center; + justify-content: center; + gap: 12px; +} + +.h5-infinite-no-more::before, +.h5-infinite-no-more::after { + content: ''; + height: 1px; + width: 2rem; + background: var(--color-border); } diff --git a/src/pages/PointsMallPage/styles/points-mall-h5.css b/src/pages/PointsMallPage/styles/points-mall-h5.css index 228039c..e4b6dc5 100644 --- a/src/pages/PointsMallPage/styles/points-mall-h5.css +++ b/src/pages/PointsMallPage/styles/points-mall-h5.css @@ -5,13 +5,12 @@ .points-mall-page-h5 .h5-points-mall-hero { padding: 0.875rem 0.75rem; - border-radius: 1.125rem; + border-radius: 12px; background: var(--gradient-hero); } .points-mall-page-h5 .h5-points-mall-header, -.points-mall-page-h5 .h5-points-mall-banner-header, -.points-mall-page-h5 .h5-points-mall-product-price-row { +.points-mall-page-h5 .h5-points-mall-banner-header { flex-direction: column; align-items: flex-start; } @@ -75,6 +74,7 @@ gap: 0.375rem; overflow-x: auto; scrollbar-width: none; + margin-bottom: 12px; } .points-mall-page-h5 .h5-points-mall-category-row::-webkit-scrollbar { @@ -102,7 +102,7 @@ .points-mall-page-h5 .h5-points-mall-banner-card { width: 100%; min-height: auto; - padding: 0.75rem; + padding: 12px; } .points-mall-page-h5 .h5-points-mall-banner-header { @@ -141,7 +141,6 @@ } .points-mall-page-h5 .h5-points-mall-filters-row { - flex-direction: column; gap: 0.5rem; } diff --git a/src/pages/StatsPage/styles/stats-page-h5.css b/src/pages/StatsPage/styles/stats-page-h5.css index 24a65fc..05f7231 100644 --- a/src/pages/StatsPage/styles/stats-page-h5.css +++ b/src/pages/StatsPage/styles/stats-page-h5.css @@ -4,9 +4,9 @@ } .stats-h5-hero { - padding: 1.125rem; + padding: 12px; border: 1px solid var(--color-border); - border-radius: 1.125rem; + border-radius: 12px; background: var(--gradient-hero); box-shadow: var(--shadow-sm); } @@ -53,6 +53,7 @@ flex-direction: column; align-items: center; text-align: center; + border-radius: 12px; } .stats-page-h5 .stats-page-card-label { @@ -78,7 +79,8 @@ .stats-page-h5 .stats-page-token-cards-grid { grid-template-columns: 1fr 1fr; - gap: 0; + gap: 8px; + margin: 12px 0; } .stats-page-h5 .stats-page-token-value { @@ -92,6 +94,11 @@ margin-top: 0.875rem; } +.stats-page-h5 .stats-page-chart-card { + box-shadow: none; + border-radius: 12px; +} + .stats-page-h5 .stats-page-chart-container, .stats-page-h5 .stats-page-token-chart-container { overflow-x: auto; diff --git a/src/styles.css b/src/styles.css index 8af9721..12903ec 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1791,7 +1791,7 @@ span.mention { background: linear-gradient(135deg, rgba(255,255,255,0.98) 0%, rgba(236,253,245,0.92) 44%, rgba(239,246,255,0.96) 100%); border: 1px solid rgba(8, 145, 178, 0.12); box-shadow: 0 20px 48px rgba(15, 23, 42, 0.06); - margin-bottom: 18px; + margin-bottom: 12px; } .points-mall-header { @@ -1807,7 +1807,7 @@ span.mention { .points-mall-balance-card { min-width: 280px; - border-radius: 18px; + border-radius: 12px; padding: 14px 16px; background: rgba(255,255,255,0.72); border: 1px solid rgba(255,255,255,0.7); @@ -1841,7 +1841,7 @@ span.mention { .points-mall-category-card { border-radius: 18px; box-shadow: 0 12px 28px rgba(15, 23, 42, 0.045); - margin-bottom: 14px; + margin-bottom: 12px; } .points-mall-category-body { @@ -1865,13 +1865,12 @@ span.mention { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 14px; - margin-bottom: 16px; + margin-bottom: 12px; } .points-mall-banner-card { - border-radius: 22px; + border-radius: 12px; padding: 22px; - border: 1px solid rgba(236, 72, 153, 0.22); background: linear-gradient(135deg, rgba(236, 72, 153, 0.16) 0%, rgba(59, 130, 246, 0.16) 60%, rgba(34, 197, 94, 0.12) 100%); box-shadow: 0 12px 30px rgba(15, 23, 42, 0.05); min-height: 176px; @@ -2338,11 +2337,16 @@ span.mention { .stats-page-agent-item { margin-bottom: 12px; padding: 12px 14px; - border-radius: 16px; + border-radius: 12px; background: linear-gradient(180deg, rgba(255,255,255,0.96) 0%, rgba(248,250,252,0.9) 100%); border: 1px solid rgba(148, 163, 184, 0.12); } +.stats-page-h5 .stats-page-agent-item { + padding: 8px; + margin-bottom: 8px; +} + .stats-page-agent-header { display: flex; align-items: center; @@ -2398,10 +2402,9 @@ span.mention { } .stats-page-token-card { - border-radius: 18px; + border-radius: 12px; padding: 16px 18px; - background: rgba(255,255,255,0.72); - border: 1px solid rgba(255,255,255,0.7); + background: var(--color-bg); } .stats-page-h5 .stats-page-token-card { @@ -2465,6 +2468,10 @@ span.mention { box-shadow: 0 12px 28px rgba(15, 23, 42, 0.045); } +.stats-page-h5 .points-integration-card { + border-radius: 12px; +} + .points-integration-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));