From 86964bb42655c2019fb7a7d6ce092a108e5317f5 Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Fri, 29 Aug 2025 22:36:05 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20feat(ui):=20enhance=20pricing=20?= =?UTF-8?q?components=20with=20improved=20icons=20and=20responsive=20desig?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace copy button icon from semi-ui IconCopy to lucide-react Copy in PricingCardView - Add conditional tooltip functionality to SelectableButtonGroup that only shows when text overflows - Implement responsive table column behavior in PricingTableColumns with mobile-aware fixed positioning - Use DOM-based overflow detection (scrollWidth vs clientWidth) for better performance - Apply useIsMobile hook to conditionally set fixed: 'right' only on desktop devices These changes improve user experience across different screen sizes and provide more consistent iconography throughout the pricing interface. --- .../common/ui/ScrollableContainer.jsx | 2 +- .../common/ui/SelectableButtonGroup.jsx | 39 ++++++--- .../model-pricing/layout/PricingSidebar.jsx | 17 +--- .../layout/content/PricingContent.jsx | 16 +++- .../layout/header/PricingTopSection.jsx | 30 +++++++ .../layout/header/PricingVendorIntro.jsx | 24 +++++- .../layout/header/SearchActions.jsx | 79 ++++++++++++++++++- .../modal/components/FilterModalContent.jsx | 4 +- .../view/card/PricingCardSkeleton.jsx | 2 +- .../view/card/PricingCardView.jsx | 5 +- .../model-pricing/view/table/PricingTable.jsx | 4 +- .../view/table/PricingTableColumns.jsx | 9 ++- .../model-pricing/useModelPricingData.jsx | 2 +- web/src/i18n/locales/en.json | 3 +- web/src/index.css | 33 ++++---- 15 files changed, 207 insertions(+), 62 deletions(-) diff --git a/web/src/components/common/ui/ScrollableContainer.jsx b/web/src/components/common/ui/ScrollableContainer.jsx index 0137c64b..4ddda7d8 100644 --- a/web/src/components/common/ui/ScrollableContainer.jsx +++ b/web/src/components/common/ui/ScrollableContainer.jsx @@ -38,7 +38,7 @@ const ScrollableContainer = forwardRef(({ children, maxHeight = '24rem', className = '', - contentClassName = 'p-2', + contentClassName = '', fadeIndicatorClassName = '', checkInterval = 100, scrollThreshold = 5, diff --git a/web/src/components/common/ui/SelectableButtonGroup.jsx b/web/src/components/common/ui/SelectableButtonGroup.jsx index 8637c821..2dd8f657 100644 --- a/web/src/components/common/ui/SelectableButtonGroup.jsx +++ b/web/src/components/common/ui/SelectableButtonGroup.jsx @@ -17,8 +17,7 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ -import React, { useState } from 'react'; -import { useIsMobile } from '../../../hooks/common/useIsMobile'; +import React, { useState, useRef, useEffect } from 'react'; import { useMinimumLoadingTime } from '../../../hooks/common/useMinimumLoadingTime'; import { useContainerWidth } from '../../../hooks/common/useContainerWidth'; import { Divider, Button, Tag, Row, Col, Collapsible, Checkbox, Skeleton, Tooltip } from '@douyinfe/semi-ui'; @@ -51,10 +50,34 @@ const SelectableButtonGroup = ({ loading = false }) => { const [isOpen, setIsOpen] = useState(false); - const [skeletonCount] = useState(6); - const isMobile = useIsMobile(); + const [skeletonCount] = useState(12); const [containerRef, containerWidth] = useContainerWidth(); + const ConditionalTooltipText = ({ text }) => { + const textRef = useRef(null); + const [isOverflowing, setIsOverflowing] = useState(false); + + useEffect(() => { + const el = textRef.current; + if (!el) return; + setIsOverflowing(el.scrollWidth > el.clientWidth); + }, [text, containerWidth]); + + const textElement = ( + + {text} + + ); + + return isOverflowing ? ( + + {textElement} + + ) : ( + textElement + ); + }; + // 基于容器宽度计算响应式列数和标签显示策略 const getResponsiveConfig = () => { if (containerWidth <= 280) return { columns: 1, showTags: true }; // 极窄:1列+标签 @@ -176,9 +199,7 @@ const SelectableButtonGroup = ({ >
{item.icon && ({item.icon})} - - {item.label} - + {item.tagCount !== undefined && shouldShowTags && ( {item.tagCount} )} @@ -203,9 +224,7 @@ const SelectableButtonGroup = ({ >
{item.icon && ({item.icon})} - - {item.label} - + {item.tagCount !== undefined && shouldShowTags && ( {item.tagCount} )} diff --git a/web/src/components/table/model-pricing/layout/PricingSidebar.jsx b/web/src/components/table/model-pricing/layout/PricingSidebar.jsx index 074ee181..2a46e1c8 100644 --- a/web/src/components/table/model-pricing/layout/PricingSidebar.jsx +++ b/web/src/components/table/model-pricing/layout/PricingSidebar.jsx @@ -24,7 +24,7 @@ import PricingQuotaTypes from '../filter/PricingQuotaTypes'; import PricingEndpointTypes from '../filter/PricingEndpointTypes'; import PricingVendors from '../filter/PricingVendors'; import PricingTags from '../filter/PricingTags'; -import PricingDisplaySettings from '../filter/PricingDisplaySettings'; + import { resetPricingFilters } from '../../../../helpers/utils'; import { usePricingFilterCounts } from '../../../../hooks/model-pricing/usePricingFilterCounts'; @@ -107,21 +107,6 @@ const PricingSidebar = ({
- - {
{/* 固定的顶部区域(分类介绍 + 搜索和操作) */}
- +
{/* 可滚动的内容区域 */} diff --git a/web/src/components/table/model-pricing/layout/header/PricingTopSection.jsx b/web/src/components/table/model-pricing/layout/header/PricingTopSection.jsx index fe842fe3..78cea080 100644 --- a/web/src/components/table/model-pricing/layout/header/PricingTopSection.jsx +++ b/web/src/components/table/model-pricing/layout/header/PricingTopSection.jsx @@ -35,6 +35,16 @@ const PricingTopSection = memo(({ filteredModels, loading, searchValue, + showWithRecharge, + setShowWithRecharge, + currency, + setCurrency, + showRatio, + setShowRatio, + viewMode, + setViewMode, + tokenUnit, + setTokenUnit, t }) => { const [showFilterModal, setShowFilterModal] = useState(false); @@ -53,6 +63,16 @@ const PricingTopSection = memo(({ isMobile={isMobile} searchValue={searchValue} setShowFilterModal={setShowFilterModal} + showWithRecharge={showWithRecharge} + setShowWithRecharge={setShowWithRecharge} + currency={currency} + setCurrency={setCurrency} + showRatio={showRatio} + setShowRatio={setShowRatio} + viewMode={viewMode} + setViewMode={setViewMode} + tokenUnit={tokenUnit} + setTokenUnit={setTokenUnit} t={t} />
@@ -78,6 +98,16 @@ const PricingTopSection = memo(({ isMobile={isMobile} searchValue={searchValue} setShowFilterModal={setShowFilterModal} + showWithRecharge={showWithRecharge} + setShowWithRecharge={setShowWithRecharge} + currency={currency} + setCurrency={setCurrency} + showRatio={showRatio} + setShowRatio={setShowRatio} + viewMode={viewMode} + setViewMode={setViewMode} + tokenUnit={tokenUnit} + setTokenUnit={setTokenUnit} /> )} diff --git a/web/src/components/table/model-pricing/layout/header/PricingVendorIntro.jsx b/web/src/components/table/model-pricing/layout/header/PricingVendorIntro.jsx index 2f02bd2b..718b94bb 100644 --- a/web/src/components/table/model-pricing/layout/header/PricingVendorIntro.jsx +++ b/web/src/components/table/model-pricing/layout/header/PricingVendorIntro.jsx @@ -126,7 +126,17 @@ const PricingVendorIntro = memo(({ handleCompositionEnd, isMobile = false, searchValue = '', - setShowFilterModal + setShowFilterModal, + showWithRecharge, + setShowWithRecharge, + currency, + setCurrency, + showRatio, + setShowRatio, + viewMode, + setViewMode, + tokenUnit, + setTokenUnit }) => { const [currentOffset, setCurrentOffset] = useState(0); const [descModalVisible, setDescModalVisible] = useState(false); @@ -239,9 +249,19 @@ const PricingVendorIntro = memo(({ isMobile={isMobile} searchValue={searchValue} setShowFilterModal={setShowFilterModal} + showWithRecharge={showWithRecharge} + setShowWithRecharge={setShowWithRecharge} + currency={currency} + setCurrency={setCurrency} + showRatio={showRatio} + setShowRatio={setShowRatio} + viewMode={viewMode} + setViewMode={setViewMode} + tokenUnit={tokenUnit} + setTokenUnit={setTokenUnit} t={t} /> - ), [selectedRowKeys, copyText, handleChange, handleCompositionStart, handleCompositionEnd, isMobile, searchValue, setShowFilterModal, t]); + ), [selectedRowKeys, copyText, handleChange, handleCompositionStart, handleCompositionEnd, isMobile, searchValue, setShowFilterModal, showWithRecharge, setShowWithRecharge, currency, setCurrency, showRatio, setShowRatio, viewMode, setViewMode, tokenUnit, setTokenUnit, t]); const renderHeaderCard = useCallback(({ title, count, description, rightContent, primaryDarkerChannel }) => ( { const handleCopyClick = useCallback(() => { @@ -42,6 +52,14 @@ const SearchActions = memo(({ setShowFilterModal?.(true); }, [setShowFilterModal]); + const handleViewModeToggle = useCallback(() => { + setViewMode?.(viewMode === 'table' ? 'card' : 'table'); + }, [viewMode, setViewMode]); + + const handleTokenUnitToggle = useCallback(() => { + setTokenUnit?.(tokenUnit === 'K' ? 'M' : 'K'); + }, [tokenUnit, setTokenUnit]); + return (
@@ -67,6 +85,63 @@ const SearchActions = memo(({ {t('复制')} + {!isMobile && ( + <> + + + {/* 充值价格显示开关 */} +
+ {t('充值价格显示')} + +
+ + {/* 货币单位选择 */} + {showWithRecharge && ( +