refactor(web): simplify models page header
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { Boxes, Search, Sparkles } from 'lucide-react';
|
import { Boxes, Search } from 'lucide-react';
|
||||||
import type {
|
import type {
|
||||||
BaseModelCatalogItem,
|
BaseModelCatalogItem,
|
||||||
BillingConfig,
|
BillingConfig,
|
||||||
@@ -7,7 +7,6 @@ import type {
|
|||||||
PlatformModel,
|
PlatformModel,
|
||||||
} from '@easyai-ai-gateway/contracts';
|
} from '@easyai-ai-gateway/contracts';
|
||||||
import type { ConsoleData } from '../app-state';
|
import type { ConsoleData } from '../app-state';
|
||||||
import { PageHeader } from '../components/PageHeader';
|
|
||||||
import { Badge, Card, CardContent, Input } from '../components/ui';
|
import { Badge, Card, CardContent, Input } from '../components/ui';
|
||||||
import { stableModelAlias } from './admin/platform-form';
|
import { stableModelAlias } from './admin/platform-form';
|
||||||
|
|
||||||
@@ -146,15 +145,12 @@ export function ModelsPage(props: { data: ConsoleData }) {
|
|||||||
const filteredModels = useMemo(() => {
|
const filteredModels = useMemo(() => {
|
||||||
const normalizedQuery = query.trim().toLowerCase();
|
const normalizedQuery = query.trim().toLowerCase();
|
||||||
return sourceModels.filter((model) => {
|
return sourceModels.filter((model) => {
|
||||||
const providerInfo = providerMap.get(model.providerKey);
|
|
||||||
const matchedProvider = provider === 'all' || model.providerKey === provider;
|
const matchedProvider = provider === 'all' || model.providerKey === provider;
|
||||||
const matchedCapability = modelMatchesCapability(model.modelType, capability);
|
const matchedCapability = modelMatchesCapability(model.modelType, capability);
|
||||||
const matchedQuery = [
|
const matchedQuery = [
|
||||||
model.modelName,
|
model.modelName,
|
||||||
model.modelAlias,
|
model.modelAlias,
|
||||||
model.displayName,
|
model.displayName,
|
||||||
providerInfo?.displayName,
|
|
||||||
providerInfo?.code,
|
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')
|
.join(' ')
|
||||||
@@ -162,9 +158,7 @@ export function ModelsPage(props: { data: ConsoleData }) {
|
|||||||
.includes(normalizedQuery);
|
.includes(normalizedQuery);
|
||||||
return matchedProvider && matchedCapability && matchedQuery;
|
return matchedProvider && matchedCapability && matchedQuery;
|
||||||
});
|
});
|
||||||
}, [capability, provider, providerMap, query, sourceModels]);
|
}, [capability, provider, query, sourceModels]);
|
||||||
|
|
||||||
const selectedProvider = provider === 'all' ? undefined : providerMap.get(provider);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="modelsPage">
|
<div className="modelsPage">
|
||||||
@@ -184,31 +178,14 @@ export function ModelsPage(props: { data: ConsoleData }) {
|
|||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<main className="modelsContent">
|
<main className="modelsContent">
|
||||||
<PageHeader
|
<div className="modelsToolbar">
|
||||||
eyebrow="Models"
|
<p>共 {sourceModels.length} 个模型,当前显示 {filteredModels.length} 个</p>
|
||||||
title="模型"
|
<div className="searchField modelHeaderSearch">
|
||||||
description={`共 ${sourceModels.length} 个模型,当前显示 ${filteredModels.length} 个`}
|
|
||||||
/>
|
|
||||||
<div className="modelSearchBar">
|
|
||||||
<div className="searchField">
|
|
||||||
<Search size={16} />
|
<Search size={16} />
|
||||||
<Input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="模型名称模糊搜索" />
|
<Input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="模型名称模糊搜索" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section className="providerHero">
|
|
||||||
{selectedProvider ? <ProviderIcon provider={selectedProvider} /> : (
|
|
||||||
<div className="providerLogo">
|
|
||||||
<Sparkles size={22} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div>
|
|
||||||
<strong>{selectedProvider?.displayName ?? '全部模型厂商'}</strong>
|
|
||||||
<span>{selectedProvider ? `${selectedProvider.code} · ${selectedProvider.providerType}` : `${sourceProviders.length} 个厂商来自 integration-platform 目录`}</span>
|
|
||||||
</div>
|
|
||||||
<Badge variant="success">{filteredModels.length} 个模型</Badge>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="modelCards">
|
<section className="modelCards">
|
||||||
{filteredModels.map((model) => (
|
{filteredModels.map((model) => (
|
||||||
<ModelCard model={model} provider={providerMap.get(model.providerKey)} key={model.id} />
|
<ModelCard model={model} provider={providerMap.get(model.providerKey)} key={model.id} />
|
||||||
|
|||||||
@@ -79,9 +79,17 @@
|
|||||||
gap: 14px;
|
gap: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modelSearchBar {
|
.modelsToolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsToolbar p {
|
||||||
|
margin-right: auto;
|
||||||
|
color: var(--text-soft);
|
||||||
|
font-size: var(--font-size-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchField {
|
.searchField {
|
||||||
@@ -95,6 +103,10 @@
|
|||||||
color: var(--muted-foreground);
|
color: var(--muted-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.searchField.modelHeaderSearch {
|
||||||
|
width: min(520px, 42vw);
|
||||||
|
}
|
||||||
|
|
||||||
.searchField svg {
|
.searchField svg {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
@@ -105,27 +117,6 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.providerHero {
|
|
||||||
display: flex;
|
|
||||||
min-height: 86px;
|
|
||||||
align-items: center;
|
|
||||||
gap: 14px;
|
|
||||||
padding: 18px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 10px;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.providerHero strong {
|
|
||||||
display: block;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.providerHero span {
|
|
||||||
color: var(--muted-foreground);
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.providerLogo,
|
.providerLogo,
|
||||||
.modelIcon {
|
.modelIcon {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -149,10 +140,6 @@
|
|||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.providerHero .shBadge {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modelCards {
|
.modelCards {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, minmax(260px, 1fr));
|
grid-template-columns: repeat(3, minmax(260px, 1fr));
|
||||||
@@ -1423,6 +1410,15 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modelsToolbar {
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchField.modelHeaderSearch {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.providerCatalogActions {
|
.providerCatalogActions {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|||||||
Reference in New Issue
Block a user