🏗️ refactor: Replace model categories with vendor-based filtering and optimize data structure

- **Backend Changes:**
  - Refactor pricing API to return separate vendors array with ID-based model references
  - Remove redundant vendor_name/vendor_icon fields from pricing records, use vendor_id only
  - Add vendor_description to pricing response for frontend display
  - Maintain 1-minute cache protection for pricing endpoint security

- **Frontend Data Flow:**
  - Update useModelPricingData hook to build vendorsMap from API response
  - Enhance model records with vendor info during data processing
  - Pass vendorsMap through component hierarchy for consistent vendor data access

- **UI Component Replacements:**
  - Replace PricingCategories with PricingVendors component for vendor-based filtering
  - Replace PricingCategoryIntro with PricingVendorIntro in header section
  - Remove all model category related components and logic

- **Header Improvements:**
  - Implement vendor intro with real backend data (name, icon, description)
  - Add text collapsible feature (2-line limit with expand/collapse functionality)
  - Support carousel animation for "All Vendors" view with vendor icon rotation

- **Model Detail Modal Enhancements:**
  - Update ModelHeader to use real vendor icons via getLobeHubIcon()
  - Move tags from header to ModelBasicInfo content area to avoid SideSheet title width constraints
  - Display only custom tags from backend with stringToColor() for consistent styling
  - Use Space component with wrap property for proper tag layout

- **Table View Optimizations:**
  - Integrate RenderUtils for description and tags columns
  - Implement renderLimitedItems for tags (max 3 visible, +x popover for overflow)
  - Use renderDescription for text truncation with tooltip support

- **Filter Logic Updates:**
  - Vendor filter shows disabled options instead of hiding when no models match
  - Include "Unknown Vendor" category for models without vendor information
  - Remove all hardcoded vendor descriptions, use real backend data

- **Code Quality:**
  - Fix import paths after component relocation
  - Remove unused model category utilities and hardcoded mappings
  - Ensure consistent vendor data usage across all pricing views
  - Maintain backward compatibility with existing pricing calculation logic

This refactor provides a more scalable vendor-based architecture while eliminating
data redundancy and improving user experience with real-time backend data integration.
This commit is contained in:
t0ng7u
2025-08-04 21:36:31 +08:00
parent fc69f4f757
commit 0e9c3cde7c
24 changed files with 780 additions and 576 deletions

View File

@@ -0,0 +1,75 @@
/*
Copyright (C) 2025 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import React from 'react';
import { Card, Skeleton } from '@douyinfe/semi-ui';
const PricingVendorIntroSkeleton = ({
isAllVendors = false
}) => {
const placeholder = (
<div className='mb-4'>
<Card className="!rounded-2xl" bodyStyle={{ padding: '16px' }}>
<div className="flex items-start space-x-3 md:space-x-4">
{/* 供应商图标骨架 */}
<div className="flex-shrink-0 min-w-16 h-16 rounded-2xl bg-white shadow-md flex items-center justify-center px-2">
{isAllVendors ? (
<div className="flex items-center">
{Array.from({ length: 4 }).map((_, index) => (
<Skeleton.Avatar
key={index}
active
size="default"
style={{
width: 32,
height: 32,
marginRight: index < 3 ? -8 : 0,
}}
/>
))}
</div>
) : (
<Skeleton.Avatar active size="large" style={{ width: 40, height: 40, borderRadius: 8 }} />
)}
</div>
{/* 供应商信息骨架 */}
<div className="flex-1 min-w-0">
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-3 mb-2">
<Skeleton.Title active style={{ width: 120, height: 24, marginBottom: 0 }} />
<Skeleton.Button active size="small" style={{ width: 80, height: 20, borderRadius: 12 }} />
</div>
<Skeleton.Paragraph
active
rows={2}
style={{ marginBottom: 0 }}
title={false}
/>
</div>
</div>
</Card>
</div>
);
return (
<Skeleton loading={true} active placeholder={placeholder}></Skeleton>
);
};
export default PricingVendorIntroSkeleton;